OpenAI betrachtet Crash-Debugging als epidemiologische Forschung und hat eine seit 18 Jahren bestehende Schwachstelle in GNU libunwind behoben.
Die Ingenieure von OpenAI verbrachten Wochen damit, die rätselhaften Abstürze in Rockset zu erklären. Rockset ist ein C++-Dateninfrastrukturdienst, der die Such- und Daten-Plug-Ins von ChatGPT unterstützt. Die Funktionen schienen falsche Speicheradressen zurückzugeben, und der Stack-Pointer schien sich während der Ausführung um 8 Byte zu verschieben. Jede Hypothese, die das Team aufstellte, stieß auf starke Gegenbeweise. Dieser Bug schien schlichtweg unmöglich zu existieren.
Sie gingen ursprünglich von einem einzigen Bug aus, stellten aber fest, dass es sich um zwei unabhängige Bugs handelte, die zufällig zur gleichen Zeit entdeckt wurden. Diese bahnbrechende Erkenntnis stammte nicht aus einer tiefgreifenden Untersuchung eines einzelnen Absturzereignisses, sondern aus dem Übergang zu dem, was sie als „epidemiologisches Debugging“ bezeichnen: Die Erstellung einer Pipeline, die automatisch alle Core-Dump-Dateien der Produktionsumgebung des letzten Jahres analysiert und dann nach allgemeinen Mustern sucht, anstatt Rückschlüsse aus einzelnen Fällen zu ziehen.
Das Team ließ ChatGPT ein Skript schreiben, das den Anfang jeder Core-Datei herunterlädt, die Registerdaten extrahiert, bekannte Fehlalarme filtert und jeden Absturz als „Rückgabe eines Null-Zeigers“, „Stack-Ausrichtungsfehler“ oder einen anderen Typ markiert. Sie wandten dieses Skript parallel auf alle Rockset-Core-Dumps des letzten Jahres an. Sie erkannten schnell Korrelationen. Die Probleme, die aus symptomatischer Sicht zu derselben Kategorie gehörten, entsprachen tatsächlich zwei Gruppen von Absturzereignissen mit völlig unterschiedlichen Merkmalen.
Alle diese durch Stack-Ausrichtungsfehler verursachten Abstürze stammten aus derselben Azure-Region, hatten ein klares Startdatum und traten nie auf lang laufenden Knoten auf. Das Team verfolgte die Ursache bis zu einem physischen Host, dessen CPU stillschweigend falsche Ergebnisse erzeugte. Er war weder überhitzt noch löste er Maschinenprüfausnahmen aus, sondern die mathematischen Operationen liefen einfach unbemerkt falsch ab. Nachdem dieser Host aus dem Dienst entfernt wurde, verschwanden die durch Stack-Ausrichtungsfehler verursachten Abstürze vollständig.
Nachdem die Hardware-Absturzprobleme beseitigt waren, wurden die verbleibenden durch „Rückgabe eines Null-Zeigers“ verursachten Abstürze beherrschbar. Zuvor hatte das Team die Ursache der C++-Ausnahmeentfaltung ausgeschlossen, da es glaubte, Gegenbeispiele gefunden zu haben: Abstürze traten in Codepfaden auf, die keine Ausnahmen verwendeten. Aber alle diese Gegenbeispiele stammten aus fehlerhaften Clustern mit beschädigter Hardware. Sobald diese Störfaktoren beseitigt waren, ereigneten sich alle verbleibenden Abstürze während der Ausnahmeentfaltung.
Die Grundursache des Problems war ein 18 Jahre alter Wettlaufzustand in der Funktion _Ux86_64_setcontext von GNU libunwind. Während der C++-Ausnahmeentfaltung erstellt libunwind auf dem Stack eine ucontext_t-Struktur, füllt sie mit dem erforderlichen Registerzustand auf und ruft dann _Ux86_64_setcontext auf, um die Kontrolle an den Bereinigungs-Handler zu übergeben. Das Problem besteht darin, dass _Ux86_64_setcontext den Stack-Pointer (%rsp) aktualisiert, um auf den neuen Stack-Frame zu verweisen, bevor der Vorgang zum Lesen des Befehlszeigers aus der alten Struktur abgeschlossen ist. Sobald sich %rsp ändert, gehört diese Struktur nicht mehr zum aktiven Stack und wird nicht mehr durch den Kernel-Rot-Bereich geschützt. Wenn ein Signal genau in diesem Zeitfenster zwischen der Aktualisierung von %rsp und dem Lesen von %rip eintrifft, erstellt der Kernel seinen Signal-Frame über dieser Struktur, der Befehlszeiger wird beschädigt und die Funktion springt zu NULL oder einer Mülladresse.
Die Breite des Wettlauffensters beträgt genau eine Anweisung. Bei den Taktfrequenzen moderner Prozessoren entspricht das etwa 100 Pikosekunden. Bei den meisten Programmen wird dieser Fall überhaupt nicht ausgelöst. OpenAIs Rockset verwendet die Funktion timer_create, die alle paar Millisekunden CPU-Zeit ein SIGUSR2-Signal sendet, um eine leichtgewichtige Abrechnung pro Abfrage zu realisieren, wodurch weit mehr Signalereignisse erzeugt werden als bei herkömmlichen Anwendungen. Genau diese hochfrequente Signalübertragung verwandelte den theoretisch möglichen Wettlaufzustand in tatsächliche Abstürze in der Produktionsumgebung.
Das Team reichte einen Fix und ein in sich geschlossenes Wiederholungsbeispiel bei GNU libunwind ein und bestätigte durch Überprüfung, dass andere Entfaltungsprogramme wie libgcc dieses Problem nicht aufweisen. Der Fix beseitigt dieses Zeitfenster vollständig, indem die Anweisungen neu angeordnet werden, sodass %rip gelesen wird, bevor %rsp aktualisiert wird.
Die Zusammenfassung des Teams zu dieser Lehre ist es wert, vollständig zitiert zu werden:
Der wichtigste Schritt war nicht die raffinierte Interpretation des Assembler-Codes oder die tiefgreifende Kenntnis der Details, sondern die Erstellung eines qualitativ hochwertigen Datensatzes. Ohne diesen Datensatz hätten wir zwei völlig unterschiedliche Phänomene vermischt und versucht, dieses Durcheinander durch Schlussfolgerungen zu klären. Sobald wir genaue und vollständige Gesamtdaten erhalten hatten, wurde die Struktur des Problems offensichtlich.
Wenn Ihr Team schwer zu erklärende Abstürze in der Produktionsumgebung untersucht, prüfen Sie bitte, ob Sie mehrere Bugs zu einem einzigen zusammengefasst haben. Die Symptome, die scheinbar zu keiner Hypothese passen, sind möglicherweise nicht widersprüchlich; sie können zu zwei verschiedenen Hypothesen passen, die Sie versehentlich vermischt haben. Der schnellste Weg, die Struktur des Problems zu erkennen, ist keine tiefere Analyse einzelner Fälle, sondern der Erhalt vollständiger, markierter Daten, die alle Fehlerfälle abdecken.
Dieser vollständige technische Blogbeitrag enthält detaillierte Diagramme des Stack-Speichers, die anfälligen Assembler-Anweisungen und Visualisierungen der Absturzrate, die zwei verschiedene Arten von Fehlern aufzeigen.
Link zum Original:https://www.infoq.com/news/2026/07/openai-libunwind-core-dumps/
Dieser Artikel stammt aus dem WeChat-Offiziellen Konto „AI Front“, Autor: Steef-Jan Wiggers; Übersetzer: Pingchuan, veröffentlicht mit Genehmigung von 36Kr.