OpenAI utilizza modelli e agenti AI sempre più sofisticati per effettuare ricerche di dati ad alte prestazioni. Uno di questi servizi è Rockset, che utilizza C++ per massimizzare le prestazioni e minimizzare l'uso della memoria. Tuttavia, il C++ non garantisce la sicurezza della memoria, quindi errori possono causare crash.

Nascita Di Un Bug

Rockset è un sistema cloud-native per la ricerca e l'analisi in tempo reale. Utilizzato per molteplici applicazioni interne, è un elemento fondamentale del funzionamento del servizio ChatGPT di OpenAI. L’implementazione del suo livello di esecuzione avviene in C++, che garantisce un controllo del CPU ottimale ma presenta il rischio di errori di accesso a memoria non corretti.

Il team utilizza un gestore di segnali per tracciare i crash in tempo reale e analizzare i dump di memoria (core dump) al fine di diagnosticare e risolvere problemi. Quando questi errori sono avvenuti inizialmente, il team ha ipotizzato un problema di software e ha esaminato diversi dump di memoria in cerca di spiegazioni.

Analisi Iniziale

Uno degli errori più comuni coinvolgeva il metodo `DocumentTree::updateDocument`. Sembrava che durante l’esecuzione, il metodo avesse chiamato una funzione sconosciuta che avesse corrotto lo stack prima del ritorno alla posizione iniziale del codice. In alcuni casi, l’indirizzo di ritorno salvato nel frame era NULL. In alcuni altri, il registro di puntamento dello stack (`%rsp`) sembrava fuori sincrono di 8 byte.

Inizialmente, il team ha considerato ipotesi relative ad errori nel codice, problemi con il compilatore, librerie con errori o problemi con il kernel. Tuttavia, non si riusciva a spiegare correttamente la causa del problema a causa della complessità dell’esecuzione.

Scoperta Di Un Doppio Bug

La complessità dell’indagine ha portato i ricercatori a considerare anche problemi di hardware non rilevati inizialmente. In alcuni casi, i crash riscontrati sembravano essere dovuti a corruzioni di dati hardware. In un host Azure specifico, sembra che il processore non abbia funzionato correttamente eseguendo correttamente i calcoli matematici.

Ipotesi Sulla Rete C++

Accanto al problema hardware, si è scoperto che il problema software era causato da un bug in un’ampiamente utilizzata libreria open source chiamata GNU libunwind. Questo errore aveva un’età di ben 18 anni e non era mai stato rilevato finora.

I problemi sembravano coincidere nel tempo, causando complicazioni nella identificazione delle cause reali. Per risolverli, il team ha utilizzato un approccio ispirato all'epidemiologia: accumulare dati completi di tutti i crash rilevati e cercare pattern comuni tra di esse.

Epidemiologia Dei Crash

Informazioni Dall’Analisi Dei Dump Di Memoria

I file di dump di memoria sono una fonte cruciale per comprendere il comportamento anomalo del programma. Grazie all'abilità di Rockset di essere compilato con `-fno-omit-frame-pointer`, è stato possibile ricostruire l'ordine delle chiamate delle funzioni per ogni crash mediante il registro `%rbp`.

Zone Rosse Dell’Architettura

L’ABI AMD64 System V riserva 128 byte al di sotto del registro `%rsp` (red zone), promettendo al programma un’area protetta anche in caso di gestione di segnali del sistema. Durante il crash da segnale di `SIGSEGV`, il gestore del segnale si esegue sullo stack. Le tracce di stack non attive vengono sovrascritte, eccetto gli ultimi 128 byte. Questo aspetto ha dato origine all’analisi dei frame corrotti, rivelando errori che avevano avuto origine in funzioni precedentemente completate.

Indizi Dal Registro

Un caso particolarmente rilevante ha riguardato un crash da stack non allineato. In quel caso, `%rsp` si era misteriosamente disallineato durante l’esecuzione di una funzione con un percorso molto semplice. Nonostante non vi fossero errori nei puntatori o nei metodi di esecuzione avanzati, la struttura di controllo era inattesa.

Investigazione Finale

Gli investigatori avevano ipotizzato inizialmente un problema di kernel. In seguito, grazie alle informazioni ricavate dal red zone, si è potuto escludere questo tipo di problema.

La soluzione finale ha visto il team risolvere due bug separati: uno di natura hardware (un processo del processore non corretto) e l'altro legato ad una libreria software (libunwind). Questo approccio ha reso possibile non solo identificare i problemi, ma anche adottare una procedura sistematica per prevenire futuri eventi di crash correlati.