1 पॉइंट द्वारा GN⁺ 3 시간 전 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • Ripgrep 15.2.0 की x86_64-unknown-linux-musl बाइनरी बड़े file tree को high concurrency के साथ सर्च करते समय बीच-बीच में SIGSEGV के साथ बंद हो जाती है
  • क्रैश opendir द्वारा कॉल किए गए calloc के अंदर होता है, और stack trace के सबसे ऊपर musl mallocng के heap metadata integrity check का बिंदु दिखाई देता है
  • reproduction environment लगभग 20GiB·18 लाख files वाले tree का है, और rg से मौजूद न होने वाली string को बार-बार सर्च किया जाता है
  • 24-core system पर अगर इतना RAM हो कि search tree kernel block cache में आ जाए, तो आम तौर पर लगभग 1 मिनट में समस्या दिख जाती है
  • OpenAI Codex में शामिल rg के अलावा, आधिकारिक release के byte-for-byte समान binary में भी यह अलग से reproduce हुआ, जिससे पुष्टि होती है कि यह Codex dependency से असंबंधित समस्या है

होने का environment

  • उपयोग किया गया version ripgrep 15.2.0 rev e89fff8 है और इसमें +pcre2 feature शामिल है
    • compile समय SIMD: +SSE2,-SSSE3,-AVX2
    • run समय SIMD: +SSE2,+SSSE3,+AVX2
    • PCRE2 10.45 और JIT उपलब्ध हैं
  • operating system OpenSUSE Tumbleweed Linux x86_64 है
  • सबसे पहले पाया गया OpenAI Codex bundled rg, आधिकारिक x86_64-unknown-linux-musl release के byte-for-byte समान है
  • Codex से अलग, आधिकारिक binary में भी इसे reproduce किया गया, और analysis के लिए binary को debug symbols सहित इस कमांड से build किया गया
    • CROSS_CONTAINER_ENGINE=podman CARGO_PROFILE_RELEASE_DEBUG=true ~/.cargo/bin/cross build --release --target x86_64-unknown-linux-musl

reproduction steps

  • generate_repro_tree.py उस repository के stats की नकल करने वाला random file tree बनाता है जिसमें मूल समस्या हुई थी
    • यह प्रोग्राम LLM द्वारा लिखा गया है
    • generated output लगभग 20GiB, 18 लाख files के आकार का है
  • generated tree के root पर मौजूद न होने वाली random string को बार-बार सर्च किया जाता है
    • while true; do rg tnoheueunotshisnthukoethnsueothnsiuothonesuioseuinth; done
  • देखा गया कि reproduce करने के लिए पर्याप्त बड़ा search tree ज़रूरी है
  • 24-core system पर, यदि पर्याप्त free RAM हो ताकि पूरा tree kernel block cache में आ जाए, तो आम तौर पर लगभग 1 मिनट बाद क्रैश होता है

crash point

  • वास्तविक परिणाम core dump छोड़ने वाला SIGSEGV है
  • stack trace के सबसे ऊपर musl mallocng का get_meta है, और क्रैश heap metadata integrity check बिंदु पर होता है
  • call flow में opendir द्वारा calloc कॉल होता है, और इसके बाद Rust standard library की directory traversal तथा ripgrep ignore::walk worker आते हैं
    • get_meta__malloc_allzeropcallocopendir
    • std::fs::read_dirignore::walk::Work::read_dirignore::walk::Worker::run
  • analysis material के रूप में core dump और संबंधित rg binary संलग्न हैं

expected behavior और वर्तमान स्थिति

  • expected behavior यह है कि बड़े पैमाने और high-concurrency search में भी यह segmentation fault के बिना चले
  • दिए गए विवरण में कारण की पुष्टि, fix, review result, या अंतिम समाधान की स्थिति शामिल नहीं है

1 टिप्पणियां

 
GN⁺ 3 시간 전
Hacker News की राय
  • kernel patch में एक दिलचस्प हिस्सा है: https://lore.kernel.org/all/CALCETrXbj__SFQMzPZhES5y6-sh4np-...

    ripgrep में एक दिलचस्प bug report और ईमानदार लेकिन काफ़ी खराब AI-जनित analysis देखा
    यह https://github.com/dfoxfranke/ripgrep-3494-analysis की ओर इशारा है, और मुझे भी लगा कि यह किसी इंसान के लिखे होने के लिए ज़रूरत से ज़्यादा लंबा है। ऊपर से यह thread भी लगता है कि आज ही पोस्ट हुआ है

    • इसे पढ़ना कष्टदायक था, और उस लंबी-चौड़ी लिखाई में कहीं भी lore.kernel पर असली इंसान ने जो code या region ढूँढा, वैसी कोई चीज़ नहीं दिखी। मैं Claude को बेहतर समझने वाले किसी व्यक्ति से पूछना चाहता हूँ: क्या इसमें सचमुच root cause पहचानने वाला कोई हिस्सा है?
    • लगभग 2000 तक सार्वजनिक जगहों पर मोबाइल फ़ोन इस्तेमाल करने वालों को दिखावटी और परेशान करने वाला समझा जाता था, और अभी AI भी कुछ वैसी ही अप्रिय अस्वीकृति की अवस्था से गुजर रहा है
      2 साल पहले ऐसी analysis को community के लिए उदार समय-दान माना जाता, लेकिन अब जब पता है कि इसका source और token cost सिर्फ $0.06 है, तो पढ़ने का मन ही नहीं होता। आगे क्या होने वाला है, इसका संकेत भी इसमें शामिल है
      कुछ साल बाद इंसानों द्वारा खुद bug की तह तक जाना अंतिम उपाय बन जाएगा, और दूसरी AI agents report पढ़कर fixes को verify करेंगी। जैसे compiler-generated assembly और सार्वजनिक जगहों पर मोबाइल फ़ोन इस्तेमाल करने वालों को नज़रअंदाज़ करना सीख लिया गया, वैसे ही हम मज़ाक उड़ाने के चरण से उदासीनता के चरण में चले जाएँगे
  • musl के default allocator को सुविधा के लिए वैसे ही इस्तेमाल करना समझ में आता है, लेकिन जहाँ application का उद्देश्य ही speed हो वहाँ उसे किसी तेज allocator से न बदलना अजीब है
    mallocng multi-thread contention के प्रति कमज़ोर है। जो application सामान्यतः I/O bottleneck था, उसे musl के साथ build करने पर सिर्फ 8 threads पर malloc bottleneck आ गया, और mimalloc पर बदलते ही performance 20 गुना बढ़ गई, जिससे वह glibc default configuration के बहुत क़रीब आ गई, हालाँकि glibc+mimalloc से थोड़ी धीमी रही
    सचमुच यहाँ एक दिलचस्प समस्या है, लेकिन यह मूल रूप से इस तरह सामने आनी ही नहीं चाहिए थी

    • ripgrep 64-bit musl पर build करते समय वास्तव में jemalloc को global allocator के रूप में सेट करता है: https://github.com/BurntSushi/ripgrep/blob/435f59fc4b43af3ab...
    • segmentation fault stack को देखें तो allocation musl libc के opendir में हो रहा है। Rust का allocator replacement तरीका पूरे process का allocator नहीं बदलता, बल्कि सिर्फ वही allocator बदलता है जिसे Rust code call करता है
      फिर भी अगर global lock इस्तेमाल करने वाले allocator से होकर जाना पड़ता है, तो बेहतर होगा कि ripgrep libc के opendir के उपयोग से बचे
    • यह kernel bug है। मैं इससे सहमत हूँ कि libc allocators बिना किसी खास वजह के काफ़ी खराब होते हैं, लेकिन यह समस्या mimalloc या glibc समेत दूसरे application code में भी लगभग इसी संभावना से हो सकती है
    • ज़्यादातर programs allocation reuse करके speed बढ़ाते हैं। अगर allocation ही न करना पड़े तो तेज allocator की भी ज़रूरत नहीं, और ripgrep का काम स्वभावतः बार-बार allocation माँगता भी नहीं है
    • उल्टा musl के mallocng की hardening features की वजह से ही यह kernel bug पकड़ में आया। नहीं तो यह महीनों तक चुपचाप memory corrupt करता रहता और शायद ध्यान भी न जाता
  • अगर आप HPC cluster पर बड़े cluster file system के खिलाफ ripgrep चला रहे हैं, तो तुरंत रुकिए और अपना workflow फिर से डिज़ाइन कीजिए। ऐसा काम बहुत बड़ी मात्रा में छोटे I/O operations पैदा करता है, और यही बड़े cluster file systems की Achilles' heel है
    इससे वह काम, जो cluster की high-bandwidth memory hierarchy में होना चाहिए, file system की metadata layer पर धकेल दिया जाता है, और अगर कुछ ही users इसे साथ में चलाएँ तो पूरा high-bandwidth file system ठप हो सकता है

    • यह कोई HPC cluster नहीं, बस मेरे workstation पर इस्तेमाल होने वाला btrfs है
    • मुझे यह भी जिज्ञासा थी कि कहीं हाल में GitHub के अस्थिर होने की जड़ में कुछ ऐसा ही तो नहीं। AI के उपयोग से बढ़े हुए अरबों छोटे file operations अचानक हो रहे हैं, और object graph स्वभावतः fragmented होता है, इसलिए एक page को पहले से fetch करके सामान्य Git operations को उसी page के objects तक सीमित रखना भी आसान नहीं
      अगर https://isolveproblems.substack.com/p/how-microsoft-vaporize... में लिखा थोड़ा भी सही है, तो Azure की file system abstraction में सिर्फ एक unoptimized path भी usage spike को बहुत बड़े blast radius में बदल सकता है
  • kernel bug analysis को सीधे link करना शायद बेहतर होगा: https://github.com/dfoxfranke/ripgrep-3494-analysis

    • मैंने पढ़ने की कोशिश की, लेकिन पहले Headline paragraph पर ही छोड़ दिया
      “नई page fault वाली anonymous page में thread ने जो value store की थी, वह लगभग 10 instructions बाद उसी thread की re-read में गायब हो जाती है, और function execution के दौरान page का backing बदल जाता है”, “fault के समय pagemap पढ़ने पर backing kernel की zero page होती है”, “mechanism को per-VMA lock के anonymous fault fast path और concurrent munmap के TLB shootdown के interaction तक localize किया जा सकता है” — ऐसी पंक्तियाँ लगातार आती हैं
      page का backing क्या है, freshly-faulted या “लगभग 10 instructions बाद” का क्या मतलब है, और mechanism कैसे “localize” होता है — यह समझना मुश्किल है। तकनीकी विवरण से ज़्यादा यह शब्दों को जोड़कर लिखा गया पाठ लगता है
      ठीक-ठाक technical document का उदाहरण यह है: https://yifan.lu/2019/01/11/the-first-f00d-exploit/
    • “overflow अब भी overflow करता है, use-after-free अब भी use-after-free ही है, और musl mask race अब भी race ही करती है” वाली पंक्ति कंप्यूटर कविता जैसी लगती है, इसलिए मज़ेदार है
      निष्कर्ष मोटे तौर पर यह लगता है कि Linux 7.0 और musl 1.2.5 का संयोजन समस्या पैदा कर रहा है, लेकिन reproduction अभी भी उसी भौतिक Threadripper CPU पर भारी load देने पर ही कभी-कभी सफल होता है, इसलिए hardware issue को ख़ारिज नहीं किया जा सका
    • AI-जनित bug report पढ़ना भयानक है
    • tracing खुद बेहतरीन है, लेकिन explanation समझ से बाहर है। अतिरिक्त TLB flush अपने-आप में error नहीं हो सकता; CPU जब चाहे flush कर सकता है। असली error शायद वहाँ है जहाँ zero-page PTE मौजूद नहीं होना चाहिए था
      यह शायद CPU migration के असहज समय पर होने से पैदा हुई कोई जटिल race condition है, या page table removal path में ऐसा bug है जिसने अस्थायी रूप से गलत PTE दिखा दिया। मुझे यह भी नहीं लगता कि zero page का PFN 0 होता है
      मेरा अंदाज़ा है कि सीधे page table removal की प्रक्रिया में CPU को ऊपरी paging structures की cached entries के ज़रिए पहले से free होकर reuse हो चुकी table पढ़ने की अनुमति मिल गई होगी। मैंने पहले ऐसे issue debug किए हैं, और वे सचमुच भयानक थे
    • यह एक典型ी लंबी LLM कचरा analysis है। analysis सही भी हो सकती है, लेकिन विस्तार से पढ़ना मुश्किल है, और अगर यही analysis किसी इंसान ने की होती तो इसे पाँचवें हिस्से तक समेट देता
  • bug सिर्फ musl libc में ही क्यों दिखता है, किसी और libc में क्यों नहीं?

    • यह सिर्फ संयोग हो सकता है, और सिर्फ एक मशीन पर भी हो रहा है
    • संभवतः इसलिए कि musl allocator नई page fault वाली एक single page को सीधे application के सामने ला देता है। दूसरे allocators आमतौर पर कई pages एक साथ पहले से allocate कर लेते हैं, इसलिए race condition की time window और संकरी हो जाती है
  • सामान्यतः मैं musl के thread stack size पर शक करता, लेकिन क्या अब यह पक्का हो गया है कि यह kernel bug ही है?