Pád ripgrepu ukazuje na chybu v jádře Linuxu. Model od OpenAI odmítl s laděním pomoct
Vývojář nahlásil 26. července, že ripgrep sestavený proti knihovně musl občas spadne při hledání v obřím stromu adresářů. O dva dny později zveřejnil analýzu, podle které za pádem stojí souběh ve správě paměti jádra Linuxu: vlákno přestane vidět bajt, který samo o deset instrukcí dřív zapsalo. Potvrzení od vývojářů jádra zatím není.

Začalo to jako obyčejná stížnost na padající program. Za dva dny z ní byla analýza, která ukazuje jinam, než kde se hledalo: do správy paměti v jádře Linuxu.
Padalo jen sestavení proti musl
Daniel Fox Franke otevřel v neděli 26. července v repozitáři vyhledávacího nástroje ripgrep hlášení číslo 3494. Popsal v něm, že binárka pro cíl x86_64-unknown-linux-musl občas skončí signálem SIGSEGV, když prochází opravdu velký strom adresářů a běží přitom v mnoha vláknech. Pád nastal vždy na stejném místě: v kontrole vnitřní konzistence haldy uvnitř alokátoru mallocng, kam se program dostal přes obyčejné otevření adresáře.
Správce ripgrepu, který na GitHubu vystupuje pod jménem BurntSushi, se zeptal, jestli padají i jiné binárky. Franke odpověděl, že s ničím slinkovaným proti glibc pád vyvolat nedokázal a že stejně padá i starší ripgrep 15.1.0.
Vlákno přestane vidět, co samo napsalo
V úterý 28. července přibyl pod hlášením vzkaz, že králičí nora vede hlouběji a že podle Frankeho jde o chybu jádra. Doklady zveřejnil týž den v samostatném repozitáři pod licencí MIT-0.
Jádrem nálezu je pozorování, které na správně fungujícím počítači nemá jak nastat. Vlákno zapíše bajt na stránku paměti, kterou právě dostalo, a zhruba o deset strojových instrukcí později si totéž místo přečte – a najde nulu. Franke to změřil dvěma čteními naráz: to bezprostředně po zápisu hodnotu vidělo správně, to opožděné už ne.
Ten rozdíl je podstatný. Kdyby zápis jen nestihl dojít do paměti, procesor by ho pozdějšímu čtení podstrčil z vlastní fronty zápisů a vidělo by ho obojí. Když ho vidí jen to první, buď hodnotu mezitím přepsal někdo jiný, nebo se pod toutéž adresou vyměnila celá stránka paměti.
Pro druhou možnost mluví pohled do souboru /proc/self/pagemap v okamžiku neshody: stránka je hlášená jako přítomná a zapsaná, ale s číslem fyzického rámce 0. To je nulová stránka, kterou si jádro drží pro čtení z dosud nezapsané paměti.
Co pád zastaví a co ne
Tu první možnost, tedy přeteklé pole nebo použití uvolněné paměti, vyloučil Franke čtyřmi variantami knihovny musl. Liší se jen tím, co se se stránkami stane předtím, než do nich alokátor poprvé zapíše.
- P0 bez úprav: 250 běhů, 4 pády.
- P1 se dotkne předem všech stránek skupiny: 304 běhů, ani jeden pád.
- CTRL se dotkne předem jen té jedné stránky, na které k chybě dochází: 305 běhů, ani jeden pád.
- FAKEPF se zdrží stejně dlouho a stejně dlouho drží zámek alokátoru, ale zapisuje jinam: 308 běhů, 3 pády.
Podstatná je poslední položka. Kdyby pád způsobovalo zdržení nebo držení zámku, zmizel by i u varianty FAKEPF. Rozhoduje tedy to, jestli se na stránku sáhne dřív, než na ni alokátor poprvé zapíše – a to žádná chyba v programu ani v knihovně nevysvětlí.
Reprodukce vyšla na jednom stroji z pěti
Pád se dá vyvolat na povel, ale ne všude. Návod je v repozitáři: vygenerovat strom o zhruba 20 GiB v 1 841 862 souborech a nad ním pořád dokola pouštět rg s dvanácti vlákny. Pád přijde v průměru jednou za jednu až tři minuty; havarující běh trvá kolem 1,6 sekundy, čistý zhruba 7,6 sekundy.
Ze zkoušených pěti počítačů uspěl jen ten původní, Threadripper 9960X s jádrem 7.0.12 z openSUSE Tumbleweed. Threadripper 9970X téže generace s jádrem 6.19.10 pád nevydal, stejně jako EPYC 9575F a Xeon 678X s jádrem 6.8.0 a Ryzen AI MAX+ 395 s jádrem 6.18.35. „Pád sleduje verzi jádra, ne rodinu procesorů,“ uzavírá analýza tuhle část (přeloženo).
Kam ukazuje srovnání zdrojových kódů
Vysvětlení, ke kterému Franke došel, je souběh dvou cest v jádře. První je obsloužení výpadku stránky při zápisu do dosud nepoužité paměti; té dnes stačí zámek jedné oblasti a nemusí zamykat celou paměť procesu. Druhá je odmapování paměti voláním munmap, po kterém se všem procesorům rozešle žádost o zneplatnění záznamů v TLB, tedy v tabulce překladů adres uvnitř procesoru.
Zámek jedné oblasti proti odmapování nechrání. Když první cesta zveřejní nový záznam v tabulce stránek a druhá už mezitím touž adresu vyčistila, dorazí zneplatnění až po zveřejnění a smaže překlad, který právě vznikl.
Kde se to zhoršilo, hledal Franke porovnáním větví jádra. Cesta výpadku stránky je mezi verzemi 6.19 a 7.0 beze změny; změnil se úklid tabulek stránek při odmapování. Analýza jmenuje tři změny z ledna 2026, které ve verzi 7.0 jsou a v 6.19 nejsou: 4c640eb4181c, fb4ddf208511 a eda8c5e77622. Odpovídá to i rozdělení strojů výše.
Patří k tomu ale výhrada, kterou si Franke napsal sám: je to silná shoda, ne důkaz. Zdrojově nedoložil, že nová cesta opravdu strhne záznam, který výpadek stránky právě zveřejnil, a potvrzení nechává na revizi na straně jádra. Dotčené funkce se navíc od verze 7.0 po současný vývojový strom nezměnily, takže opravené to zatím není.
Model od OpenAI odmítl s laděním pomoct
Druhou polovinu příběhu popsal server The Register ve středu 29. července. Franke ladil chybu s pomocí jazykových modelů a u těch uzavřených narazil. Model GPT-5.6 Sol od OpenAI podle něj nespolupracoval, protože ho zastavil bezpečnostní klasifikátor; chybová hlášení ho posílala do firemního programu Enterprise Trusted Access. Modely Anthropicu prý ani nezkoušel, protože čekal totéž.
„Bezpečnostní klasifikátor OpenAI je pěkná otrava, když se snažíte vystopovat segfault,“ řekl Franke serveru The Register (přeloženo). Jinde dodal, že nástroj, který odmítá spolupracovat, je pro něj prostě rozbitý nástroj. Sama chyba se přitom poprvé ozvala v ripgrepu přibaleném k nástroji OpenAI Codex; ta binárka je podle Frankeho bajt po bajtu totožná s oficiálním vydáním 15.2.0.
Analýzu nakonec dotáhly modely s otevřenými váhami od čínských firem. Podle Frankeho přinesl klíčový průlom Kimi K3 od Moonshot AI a práci dokončil GLM 5.2 od Z’ai. Vyjádření OpenAI ani Anthropicu The Register neuvádí.
Stopy modelů jsou vidět i v dokladech samotných. Skript, který generuje testovací strom, je podle hlášení napsaný jazykovým modelem a jeden z commitů v repozitáři s analýzou se jmenuje „Human edit pass on AI prose“, tedy lidská redakce textu od modelu. Jestli takový kód patří do svobodných projektů, řeší chystané hlasování v Debianu.
Pro běžného uživatele z toho neplyne skoro nic: pád potřebuje binárku slinkovanou proti musl, skoro dva miliony souborů, dvanáct vláken a jednu konkrétní řadu jádra. Hodnota té analýzy je podle nás jinde. Ukazuje, kolik práce stojí dojít od „občas to spadne“ k pojmenování konkrétní části zdrojového kódu, a jak snadno by se u toho člověk zastavil na prvním podezřelém, tedy na alokátoru musl.
Zdroje: hlášení 3494 v repozitáři ripgrepu a repozitář s analýzou včetně dat a záplat pro musl, o části s modely psal The Register. Názvy tří jmenovaných změn jsme ověřili v repozitáři jádra Linuxu.