- Das x86_64-unknown-linux-musl-Binary von Ripgrep 15.2.0 beendet sich beim Durchsuchen großer Dateibäume mit hoher Parallelität sporadisch mit
SIGSEGV - Der Absturz tritt innerhalb von
callocauf, das vonopendiraufgerufen wurde; an der Spitze des Stacktraces erscheint eine Integritätsprüfung der Heap-Metadaten von musl mallocng - Die Reproduktionsumgebung besteht aus einem Baum mit etwa 20 GiB und 1,8 Mio. Dateien; wiederholt wird mit
rgnach einem nicht vorhandenen String gesucht - Auf einem 24-Core-System tritt das Problem in der Regel nach etwa 1 Minute auf, wenn genügend RAM vorhanden ist, damit der Suchbaum in den Kernel-Block-Cache passt
- Es wurde unabhängig sowohl mit dem in OpenAI Codex enthaltenen
rgals auch mit einem byteweise identischen Binary aus dem offiziellen Release reproduziert und damit als nicht von Codex-Abhängigkeiten abhängig bestätigt
Umgebung
- Verwendete Version ist ripgrep 15.2.0 rev
e89fff8mit+pcre2-Feature- SIMD zur Compile-Zeit:
+SSE2,-SSSE3,-AVX2 - SIMD zur Laufzeit:
+SSE2,+SSSE3,+AVX2 - PCRE2 10.45 und JIT sind verfügbar
- SIMD zur Compile-Zeit:
- Betriebssystem ist OpenSUSE Tumbleweed Linux x86_64
- Das zuerst entdeckte, mit OpenAI Codex gebündelte
rgist byteweise identisch mit dem offiziellen x86_64-unknown-linux-musl-Release - Unabhängig von Codex wurde es auch mit dem offiziellen Binary reproduziert; das Analyse-Binary wurde mit folgendem Befehl inklusive Debug-Symbolen gebaut
CROSS_CONTAINER_ENGINE=podman CARGO_PROFILE_RELEASE_DEBUG=true ~/.cargo/bin/cross build --release --target x86_64-unknown-linux-musl
Reproduktionsschritte
- generate_repro_tree.py erzeugt einen zufälligen Dateibaum, der die Statistiken des Repositorys nachbildet, in dem das Problem ursprünglich auftrat
- Dieses Programm wurde mit einem LLM geschrieben
- Das Ergebnis hat einen Umfang von etwa 20 GiB und 1,8 Mio. Dateien
- Vom Root des erzeugten Baums aus wird wiederholt nach einem beliebigen nicht vorhandenen String gesucht
while true; do rg tnoheueunotshisnthukoethnsueothnsiuothonesuioseuinth; done
- Ein ausreichend großer Suchbaum wurde als wesentlich für die Reproduktion beobachtet
- Auf einem 24-Core-System mit genügend freiem RAM, damit der gesamte Baum in den Kernel-Block-Cache passt, stürzt es in der Regel nach etwa 1 Minute ab
Absturzstelle
- Das tatsächliche Ergebnis ist ein
SIGSEGVmit Core-Dump - An der Spitze des Stacktraces steht
get_metaaus musl mallocng; der Absturz erfolgt an einer Integritätsprüfung der Heap-Metadaten - Der Aufruffluss verläuft von
opendir, dascallocaufruft, weiter zur Verzeichnisdurchsuchung der Rust-Standardbibliothek und zu den ripgrep-Workern vonignore::walkget_meta→__malloc_allzerop→calloc→opendirstd::fs::read_dir→ignore::walk::Work::read_dir→ignore::walk::Worker::run
- Als Analyseunterlagen sind ein Core-Dump und das entsprechende rg-Binary angehängt
Erwartetes Verhalten und aktueller Stand
- Erwartet wird, dass es auch bei groß angelegten Suchen mit hoher Parallelität ohne Segmentierungsfehler läuft
- Die bereitgestellten Informationen enthalten keine gesicherte Ursache, keinen Fix, keine Review-Ergebnisse und keinen finalen Lösungsstatus
1 Kommentare
Hacker-News-Kommentare
Im Kernel-Patch gibt es eine interessante Stelle: https://lore.kernel.org/all/CALCETrXbj__SFQMzPZhES5y6-sh4np-...
Vor zwei Jahren wäre so eine Analyse als großzügige Zeitspende an die Community gesehen worden, aber jetzt, da man weiß, dass Quelle und Token-Kosten nur 0,06 Dollar betragen, will man sie nicht lesen. Dass sie auch einen Vorgeschmack auf das gibt, was kommt, trägt ebenfalls dazu bei
In ein paar Jahren wird es das letzte Mittel sein, wenn Menschen Bugs selbst untersuchen, und andere KI-Agenten werden die Berichte lesen und die Fixes verifizieren. Wie bei vom Compiler erzeugtem Assembler und Menschen, die in der Öffentlichkeit telefonieren, dürfte es von der Spottphase in die Gleichgültigkeitsphase übergehen
Ich verstehe, dass man den Standard-Allocator von musl aus Bequemlichkeit übernimmt, aber bei einer Anwendung, deren Zweck Geschwindigkeit ist, ist es seltsam, nicht auf einen schnelleren Allocator zu wechseln
mallocng ist anfällig für Contention bei mehreren Threads. Selbst Anwendungen, die normalerweise I/O-gebunden waren, bekamen bei einem Build mit musl schon mit 8 Threads einen
malloc-Flaschenhals; nach dem Wechsel zu mimalloc stieg die Performance um den Faktor 20, lag sehr nahe an der glibc-Standardkonfiguration und war nur etwas langsamer als glibc+mimallocEs gibt hier tatsächlich ein interessantes Problem, aber es hätte sich ursprünglich nicht auf diese Weise zeigen sollen
opendirder musl libc. Die Art, wie Rust den Allocator austauscht, ersetzt nicht den Allocator des gesamten Prozesses, sondern nur den Allocator, den Rust-Code aufruftWenn man allerdings durch einen Allocator mit globalem Lock muss, scheint es besser, wenn ripgrep die Nutzung von
opendiraus libc vermeidetWenn man ripgrep auf einem HPC-Cluster gegen ein großes Cluster-Dateisystem laufen lässt, sollte man sofort aufhören und den Workflow neu entwerfen. Solche Aufgaben erzeugen massenhaft kleine I/O-Operationen, und das ist die Achillesferse großer Cluster-Dateisysteme
Man verlagert Arbeit, die in der High-Bandwidth-Memory-Schicht des Clusters erledigt werden sollte, in die Metadaten-Schicht des Dateisystems, und schon wenige Nutzer, die das gleichzeitig tun, können ein gesamtes High-Bandwidth-Dateisystem lahmlegen
Wenn an https://isolveproblems.substack.com/p/how-microsoft-vaporize... auch nur etwas dran ist, könnte schon ein einziger nicht optimierter Pfad in Azures Dateisystem-Abstraktion bei einem Nutzungsschub einen enormen Ausfallradius erzeugen
Es könnte besser sein, die Analyse des Kernel-Bugs direkt zu verlinken: https://github.com/dfoxfranke/ripgrep-3494-analysis
Headline-Absatz aufgegebenDa stehen Sätze wie: „Ein von einem Thread auf einer frisch per Page Fault angelegten anonymen Seite gespeicherter Wert verschwindet beim erneuten Lesen durch denselben Thread etwa 10 Instruktionen später, und das Backing der Seite wird während der Funktionsausführung ersetzt“, „liest man pagemap im Moment des Faults, ist das Backing die Zero Page des Kernels“, „der Mechanismus lässt sich auf die Interaktion zwischen dem schnellen anonymen Fault-Pfad pro VMA-Lock und dem TLB-Shootdown eines gleichzeitigen
munmaplocalizen“Es ist schwer zu verstehen, was das
backingeiner Seite sein soll, wasfreshly-faultedoder „etwa 10 Instruktionen später“ bedeutet und wie ein Mechanismus „localized“ wird. Es wirkt eher wie aneinandergereihte Wörter als wie eine technische ErklärungEin Beispiel für eine ordentliche technische Dokumentation ist: https://yifan.lu/2019/01/11/the-first-f00d-exploit/
Das Fazit scheint grob zu sein, dass die Kombination aus Linux 7.0 und musl 1.2.5 problematisch ist, aber die Reproduktion gelingt weiterhin nur sporadisch unter starker Last auf derselben physischen Threadripper-CPU, daher konnte ein Hardwareproblem nicht ausgeschlossen werden
Es sieht nach einem komplexen Race Condition aus, das bei einer CPU-Migration zu einem ungünstigen Zeitpunkt auftritt, oder nach einem Bug, bei dem der Pfad zum Entfernen von Page Tables vorübergehend eine falsche PTE freilegt. Ich glaube auch nicht, dass die PFN der Zero Page 0 ist
Meine Vermutung wäre, dass der direkte Page-Table-Removal-Prozess der CPU erlaubt hat, über gecachte Einträge höherer Paging-Strukturen eine bereits freigegebene und wiederverwendete Tabelle zu lesen. Ich habe so etwas früher einmal debuggt, und es war wirklich schrecklich
Warum tritt der Bug nur bei musl libc auf und nicht bei anderen libcs?
Normalerweise hätte ich die Thread-Stack-Größe von musl verdächtigt; ich frage mich, ob es als Kernel-Bug bestätigt wurde