1 puan yazan GN⁺ 3 시간 전 | 1 yorum | WhatsApp'ta paylaş
  • Ripgrep 15.2.0'ın x86_64-unknown-linux-musl ikilisi, büyük dosya ağaçlarında yüksek eşzamanlılıkla arama yaparken aralıklı olarak SIGSEGV ile sonlanıyor
  • Çökme, opendir tarafından çağrılan calloc içinde meydana geliyor ve yığın izinin en üstünde musl mallocng'nin heap meta veri bütünlüğü denetim noktası görünüyor
  • Yeniden üretim ortamı, yaklaşık 20GiB ve 1,8 milyon dosyadan oluşan bir ağaçtan oluşuyor ve var olmayan bir dize rg ile tekrar tekrar aranıyor
  • 24 çekirdekli bir sistemde, arama ağacı çekirdeğin blok önbelleğine sığacak kadar RAM varsa sorun genellikle yaklaşık 1 dakika içinde ortaya çıkıyor
  • Sorun, OpenAI Codex'e dahil edilen rg ile birlikte resmi sürümdeki bayt düzeyinde aynı ikilide de bağımsız olarak yeniden üretilebildiği için Codex bağımlılığıyla ilgisiz bir sorun olduğu doğrulandı

Ortam

  • Kullanılan sürüm ripgrep 15.2.0 rev e89fff8; +pcre2 özelliğini içeriyor
    • Derleme sırasında SIMD: +SSE2,-SSSE3,-AVX2
    • Çalışma sırasında SIMD: +SSE2,+SSSE3,+AVX2
    • PCRE2 10.45 ve JIT kullanılabiliyor
  • İşletim sistemi OpenSUSE Tumbleweed Linux x86_64
  • İlk fark edilen OpenAI Codex paketindeki rg, resmi x86_64-unknown-linux-musl sürümüyle bayt düzeyinde aynı
  • Codex'ten bağımsız olarak resmi ikilide de yeniden üretildi; analiz için kullanılan ikili ise aşağıdaki komutla debug sembolleri dahil edilerek derlendi
    • CROSS_CONTAINER_ENGINE=podman CARGO_PROFILE_RELEASE_DEBUG=true ~/.cargo/bin/cross build --release --target x86_64-unknown-linux-musl

Yeniden üretim adımları

  • generate_repro_tree.py, sorunun ilk ortaya çıktığı deponun istatistiklerini taklit eden rastgele bir dosya ağacı oluşturuyor
    • Bu program bir LLM ile yazıldı
    • Üretilen çıktı yaklaşık 20GiB ve 1,8 milyon dosya boyutunda
  • Oluşturulan ağacın kökünde, var olmayan rastgele bir dize tekrar tekrar aranıyor
    • while true; do rg tnoheueunotshisnthukoethnsueothnsiuothonesuioseuinth; done
  • Yeterince büyük bir arama ağacının yeniden üretim için gerekli olduğu gözlemlendi
  • 24 çekirdekli bir sistemde, tüm ağaç çekirdeğin blok önbelleğine sığacak kadar boş RAM varsa çökme genellikle yaklaşık 1 dakika sonra meydana geliyor

Çökme noktası

  • Gerçek sonuç, core dump bırakan bir SIGSEGV
  • Yığın izinin en üstünde musl mallocng'nin get_meta fonksiyonu yer alıyor ve çökme, heap meta veri bütünlüğü denetim noktasında oluşuyor
  • Çağrı akışı, opendir'in calloc çağırmasıyla başlıyor ve Rust standart kütüphanesinin dizin dolaşımı ile ripgrep ignore::walk worker'ına uzanıyor
    • get_meta__malloc_allzeropcallocopendir
    • std::fs::read_dirignore::walk::Work::read_dirignore::walk::Worker::run
  • Analiz materyali olarak core dump ve ilgili rg ikilisi eklendi

Beklenen davranış ve mevcut durum

  • Beklenen davranış, büyük ölçekli ve yüksek eşzamanlılıklı aramalarda da segmentation fault olmadan çalışması
  • Sağlanan içerikte nedenin kesinleştiği, bir düzeltme önerisi, inceleme sonucu veya nihai olarak çözülüp çözülmediği bilgisi yer almıyor

1 yorum

 
GN⁺ 3 시간 전
Hacker News yorumları
  • Çekirdek yamasında ilginç bir bölüm var: https://lore.kernel.org/all/CALCETrXbj__SFQMzPZhES5y6-sh4np-...

    ripgrep hakkında ilginç bir hata raporu ve özenli ama oldukça berbat bir AI üretimi analiz gördüm
    Burada sözü edilen şey https://github.com/dfoxfranke/ripgrep-3494-analysis; ben de bunun bir insanın yazmış olamayacağı kadar uzun olduğunu düşündüm. Ayrıca bu başlık sanki daha bugün açılmış gibi görünüyor

    • Okuması işkenceydi ve o uzun yazının hiçbir yerinde lore.kernel'deki gerçek insanların bulduğu kodu ya da alanı işaret eden bir bölüm göremedim. Claude'u daha iyi anlayan birine sormak isterim: gerçekten asıl nedeni tespit eden bir kısım var mı?
    • 2000'ler civarına kadar kamusal alanda cep telefonu kullanan insanlar kendini beğenmiş sinir bozucu tipler gibi görülürdü; şimdi AI da benzer bir rahatsız edici reddetme aşamasından geçiyor
      2 yıl önce böyle bir analiz, topluluğa cömertçe zaman bağışlamanın sonucu sayılırdı; ama artık kaynağını ve token maliyetinin sadece 0,06 dolar olduğunu bildiğim için okumak istemiyorum. Bunun ileride olacaklara işaret etmesi de bunda etkili
      Birkaç yıl sonra insanların hataları elle kurcalaması son çare olacak ve başka AI ajanları raporları okuyup düzeltmeleri doğrulayacak. Derleyicinin ürettiği assembly'yi ve kamusal alanda cep telefonu kullananları görmezden geldiğimiz gibi, alay etme aşamasından umursamama aşamasına geçeceğiz
  • musl'un varsayılan ayırıcısını sırf kullanışlı diye olduğu gibi kullanmayı anlıyorum ama amacı doğrudan hız olan bir uygulamada daha hızlı bir ayırıcıya geçilmemiş olması garip
    mallocng çoklu iş parçacığı çekişmesinde zayıf. Normalde G/Ç darboğazlı olan bir uygulama bile musl ile derlenince sadece 8 iş parçacığında malloc darboğazına girdi; mimalloc'a geçince performans 20 kat arttı, glibc varsayılan yapılandırmasına çok yaklaştı ve glibc+mimalloc'dan biraz daha yavaştı
    Gerçekten ilginç bir sorun var ama bunun en başta bu şekilde ortaya çıkmaması gerekirdi

    • ripgrep, 64 bit musl ile derlendiğinde gerçekten de jemalloc'u küresel ayırıcı olarak belirliyor: https://github.com/BurntSushi/ripgrep/blob/435f59fc4b43af3ab...
    • Segmentation fault yığınına bakılırsa ayırma, musl libc'nin opendir içinde gerçekleşiyor. Rust'ın kullandığı ayırıcı değiştirme yöntemi, tüm sürecin ayırıcısını değiştirmiyor; yalnızca Rust kodunun çağırdığı ayırıcıyı değiştiriyor
      Yine de küresel kilit kullanan bir ayırıcıdan geçmek zorundaysa, ripgrep'in libc'nin opendir kullanımından kaçınması daha iyi olabilir
    • Bu bir çekirdek hatası. libc ayırıcılarının sebepsiz yere kötü olduğu konusunda katılıyorum ama bu sorun mimalloc ya da glibc dahil başka uygulama kodlarında da benzer olasılıkla ortaya çıkabilir gibi görünüyor
    • Programların çoğu, hız kazanmak için ayrılmış belleği yeniden kullanır. Hiç ayırma yapmıyorsanız hızlı bir ayırıcıya da gerek yoktur ve ripgrep'in işi zaten özünde sık ayırma gerektirmiyor
    • Hatta musl'un mallocng'sinin sunduğu güçlendirme özellikleri sayesinde çekirdek hatası fark edilebildi. Aksi halde aylar boyunca sessizce belleği bozup fark edilmeden kalabilirdi
  • Bir HPC kümesinde büyük ölçekli küme dosya sistemi üzerinde ripgrep çalıştırıyorsanız, hemen durup iş akışını yeniden tasarlamalısınız. Bu tür işler çok sayıda küçük G/Ç üretir; bu da büyük küme dosya sistemlerinin Aşil topuğudur
    Kümenin yüksek bant genişlikli bellek katmanında yapılması gereken işi dosya sisteminin metadata katmanına itersiniz ve aynı anda sadece birkaç kullanıcı bunu çalıştırsa bile tüm yüksek bant genişlikli dosya sistemi felç olabilir

    • Bu bir HPC kümesi değil, iş istasyonumda kullandığım btrfs
    • Son dönemde GitHub'ın dengesizleşmesinin temel nedeni de buna benzer bir şey mi diye merak ettim. AI kullanımıyla büyüyen milyarlarca küçük dosya işlemi birden ortaya çıkıyor ve nesne grafiği doğası gereği parçalı olduğu için, tek bir sayfayı önden getirip sıradan Git işlemlerinin sadece o sayfadaki nesnelere dokunmasını sağlamak da zor
      https://isolveproblems.substack.com/p/how-microsoft-vaporize... içeriği az da olsa doğruysa, Azure'un dosya sistemi soyutlamasında optimize edilmemiş tek bir yol bile kullanım artışıyla muazzam bir etki alanına dönüşebilir
  • Çekirdek hatası analizine doğrudan bağlantı vermek daha iyi olabilir: https://github.com/dfoxfranke/ripgrep-3494-analysis

    • Okumaya çalıştım ama ilk Headline paragrafında bıraktım
      “Yeni page fault almış anonim bir sayfaya thread'in yazdığı değer yaklaşık 10 komut sonra aynı thread tarafından tekrar okunurken kayboluyor ve fonksiyon çalışması sırasında sayfanın backing'i değiştiriliyor”, “fault anında pagemap okunursa backing çekirdeğin zero page'i oluyor”, “mekanizma, VMA başına kilitli anonim fault hızlı yolu ile eşzamanlı munmap'in TLB shootdown'ı arasındaki etkileşime localize ediliyor” gibi cümleler sürüp gidiyor
      Bir sayfanın backing'inin ne olduğu, freshly-faulted ya da “yaklaşık 10 komut sonra”nın ne anlama geldiği, mekanizmanın nasıl “localize” edildiği anlaşılmıyor. Teknik açıklamadan çok yan yana dizilmiş kelimeler gibi duruyor
      Düzgün bir teknik belgenin örneği şu: https://yifan.lu/2019/01/11/the-first-f00d-exploit/
    • “Taşma hâlâ taşmadır, use-after-free hâlâ use-after-free'dir ve musl mask race hâlâ race'dir” kısmı bilgisayar şiiri gibi, komik
      Sonuç kabaca Linux 7.0 ile musl 1.2.5 birleşiminde bir sorun varmış gibi görünüyor ama aynı fiziksel Threadripper CPU üzerinde ağır yük altında bile yeniden üretim hâlâ ancak aralıklı olarak mümkün olduğu için donanım sorununu dışlayamamışlar
    • AI üretimi hata raporları okumak korkunç
    • İz sürme kısmı harika ama açıklama mantıklı değil. Ek TLB flush'ları hata olamaz; CPU istediği zaman flush yapabilir. Asıl hata, olmaması gereken anda zero-page PTE bulunması gibi görünüyor
      Muhtemelen CPU'nun garip bir anda taşınmasıyla oluşan karmaşık bir yarış durumu ya da sayfa tablosu kaldırma yolunun yanlış PTE'yi geçici olarak görünür kıldığı bir hata. Ayrıca zero page'in PFN'inin 0 olduğunu da sanmıyorum
      Tahminimce, doğrudan sayfa tablosu kaldırma sürecinde CPU'nun üst düzey sayfalama yapılarındaki önbelleğe alınmış girdiler üzerinden önceden serbest bırakılmış ve yeniden kullanılmış tabloları okumasına izin verilmiş olabilir. Daha önce buna benzer bir şeyi debug etmiştim; gerçekten korkunçtu
    • Tipik olarak uzun uzadıya bir LLM döküntüsü analizi. Analiz doğru olabilir ama ayrıntılı okumak zor; aynı analizi bir insan yapsaydı muhtemelen beşte biri uzunluğunda olurdu
  • Hata neden başka libc'de değil de yalnızca musl libc'de ortaya çıkıyor?

    • Sadece tesadüf; hatta tek bir makinede bile yaşanabiliyor
    • Muhtemelen musl ayırıcısı, yeni page fault oluşmuş tek bir sayfayı uygulamaya hemen açtığı için. Diğer ayırıcılar genelde birden çok sayfayı önceden ayırdığı için yarış koşulunun zaman penceresi daha dar oluyor
  • Normalde musl'un iş parçacığı yığını boyutundan şüphelenirdim ama bunun çekirdek hatası olduğu doğrulandı mı merak ediyorum