News · OpenAI rastreia falhas de busca no ChatGPT até um host defeituoso na Azure e uma race condition de 18 anos na libunwind

May, 27Leitura de 4 min
Plataforma

OpenAI rastreia falhas de busca no ChatGPT até um host defeituoso na Azure e uma race condition de 18 anos na libunwind

Como uma análise em escala populacional de um ano de core dumps separou dois bugs sem relação que se disfarçavam de uma única falha impossível

Duas falhas com a mesma fantasia

O assunto é o Rockset, o sistema de busca e análise nativo de nuvem que a OpenAI adquiriu em 2024 e hoje usa para manter atualizado o índice da base de conhecimento de um workspace no ChatGPT. Sua camada de execução é escrita em C++, e alguns meses antes da publicação começaram a surgir falhas que pareciam fisicamente impossíveis: uma função normal parecia terminar e então retornava para um endereço inválido, às vezes um slot de endereço de retorno NULL, às vezes com o stack pointer %rsp desalinhado em exatamente 8 bytes.

Nenhum dos dois sintomas se encaixa em código de aplicação comum. Uma escrita perdida que atinge exatamente um endereço de retorno salvo é extremamente improvável, e desalinhar o %rsp em 8 bytes sem assembly inline, setcontext ou longjmp — nenhum dos quais o Rockset usa — é ainda mais estranho, já que o código compilado só toca esse registrador no prólogo e no epílogo de uma função. Toda hipótese levantada pela equipe ou pelo ChatGPT tinha fortes evidências contra ela. O motivo, descobriram, era que estavam olhando para dois bugs sem relação ao mesmo tempo: corrupção silenciosa de hardware em um único host da Azure, e uma race condition de 18 anos na GNU libunwind.

Médico versus epidemiologista

O instinto inicial da equipe foi clínico: examinar de perto alguns core dumps, formular hipóteses, descartá-las uma a uma. A maioria das falhas caía em um único método fortemente inlined, DocumentTree::updateDocument, o que deixava um conjunto avassalador de funções candidatas. As consultas de log geravam tanto falsos positivos quanto falsos negativos, porque os próprios bugs de corrupção de stack corrompiam os stack traces usados para classificá-los. A inspeção manual não escalava. Pior: a equipe já havia descartado uma causa de hardware porque as falhas apareciam em várias regiões e tipos de hardware — uma conclusão que estava exatamente invertida para uma das duas populações de falhas.

O ponto de virada foi uma mudança de método. Em vez de diagnosticar um paciente por vez, passaram a tratar a frota inteira como uma população. Pediram ao ChatGPT que escrevesse um script que baixasse um prefixo de cada arquivo core, extraísse os registradores, filtrasse falsos positivos conhecidos com base nos logs e classificasse cada falha como retorno-para-null, stack-desalinhado ou outra. Ao rodar isso em todos os core dumps de produção do Rockset do ano anterior, as correlações apareceram instantaneamente: as falhas de retorno-para-null estavam espalhadas por muitos clusters sem data de início clara, enquanto as falhas de stack-desalinhado vinham de uma única região, tinham uma data de início bem definida e nunca ocorriam em nós de longa duração — a assinatura clássica de uma única máquina física com hardware defeituoso.

Uma janela de cem picossegundos, e a estimativa de Fermi que a tornou plausível

O host defeituoso foi colocado em lista de bloqueio, e suas falhas de stack-desalinhado desapareceram, ainda que a equipe nunca tenha conseguido reproduzir a corrupção de registradores em semanas de testes de estresse. Com esse cluster removido, os cores restantes de retorno-para-null todos aconteciam durante o unwinding de exceções em C++. A GNU libunwind sintetiza um ucontext_t na stack e o entrega a uma rotina interna, _Ux86_64_setcontext, cujas instruções finais movem o %rsp para o novo fundo da stack antes que uma instrução posterior leia o instruction pointer de destino a partir dessa mesma struct. Uma vez que o %rsp se move, a struct deixa de estar protegida pela red zone, então um sinal entregue nesse intervalo permite que o kernel construa seu signal frame sobre essa memória e zere o instruction pointer restaurado.

A janela vulnerável é uma única instrução — algo em torno de cem picossegundos em uma CPU moderna out-of-order — o que parecia demasiado estreito para explicar mais de uma dezena de falhas de retorno-para-null por dia. Uma estimativa de Fermi fechou essa lacuna: uma janela de 10⁻¹⁰ segundos contra um SIGUSR2 chegando a cada 10⁻² segundos de tempo de CPU dá a cada handler de limpeza cerca de 1 chance em 10⁻⁸ de perder a corrida; um host disparando 10⁴ exceções por segundo por controle de contrapressão de ingestão falha então algo como uma vez a cada poucas horas, o que em escala de frota é mais que suficiente.

O Rockset era atípico nos três eixos que determinam a taxa de falhas. Ele dispara exceções com frequência como forma de controle de sobrecarga, entrega o SIGUSR2 com uma frequência incomum via seu coarse_thread_cputime_clock, e, mais cedo naquele ano, havia adicionado uma chamada timer_getoverrun que fazia o signal handler consumir mais stack — o suficiente para alcançar e corromper o ucontext_t obsoleto. Antes dessa mudança, as falhas simplesmente não apareciam. O bug de 18 anos sempre esteve presente; o produto entre taxa de exceções, taxa de sinais e uso de stack pelo handler acabara de cruzar o limiar que o tornava visível.

Instrumentação como a solução de fato

A mitigação imediata foi trocar a GNU libunwind pelo unwinder da libgcc — uma boa troca por si só, já que a libgcc recebeu mais trabalho para reduzir a contenção de locks em VMs grandes — e a OpenAI enviou upstream um reprodutor autocontido e a correção para a GNU libunwind. Para a falha de hardware, a equipe melhorou o handler de sinal fatal para registrar o estado dos registradores em log, permitindo detectar recorrências só pelos logs, sem precisar de um core dump, e mudou o control plane para reutilizar VMs em vez de reciclá-las, o que facilita a detecção de nós defeituosos.

A lição que realmente sustenta o caso não é nenhum dos detalhes de ABI, DWARF ou maquinário de exceções revelados pela investigação. É que a confusão só persistiu porque dois fenômenos estavam misturados em uma única história, e ela se desfez no exato momento em que o conjunto de dados ficou limpo e completo. Esse é o ponto que vale levar para outras equipes de infraestrutura: quando um bug parece impossível, o que falta muitas vezes são dados populacionais precisos, não um diagnóstico mais inteligente de um único caso.

Confiabilidade não é só corrigir bugs depois que eles acontecem — é construir os dados, os fluxos de trabalho e as habilidades que transformam problemas impossíveis em problemas diagnosticáveis e solucionáveis.Montana Labs

Find this story relevant to you?

Contact us to find a unique solution

Contact us

Precisa de um parceiro de engenharia de IA que consiga construir de verdade?

Ajudamos empresas no Brasil a integrar IA, acelerar produtos com IA, automatizar operações e modernizar os sistemas de software por trás do negócio.

Get in touch

Leituras relacionadas

Mais análises sobre entrega de produto, IA operacional e o trabalho de sistemas que faz a implantação se sustentar na prática.

Jul, 134 min de leitura
Plataforma

Doppel automatiza a remoção de sites de phishing com um pipeline de cinco etapas usando GPT-5 e RFT

Jul, 134 min de leitura
Plataforma

A aposta da Deutsche Telekom: as redes de voz como interface de IA

Jul, 134 min de leitura
Plataforma

A expansão de 5GW da Meta na Louisiana é anunciada com bônus para professores, não teraflops