1 poin oleh GN⁺ 3 jam lalu | 1 komentar | Bagikan ke WhatsApp
  • Biner x86_64-unknown-linux-musl dari Ripgrep 15.2.0 kadang berhenti dengan SIGSEGV saat mencari pada pohon file besar dengan konkurensi tinggi
  • Crash terjadi di dalam calloc yang dipanggil oleh opendir, dan titik pemeriksaan integritas metadata heap milik musl mallocng muncul di bagian teratas stack trace
  • Lingkungan reproduksi berupa pohon dengan sekitar 20GiB dan 1,8 juta file, lalu string yang tidak ada dicari berulang kali dengan rg
  • Pada sistem 24 core, jika RAM cukup agar pohon pencarian masuk ke kernel block cache, masalah biasanya muncul dalam sekitar 1 menit
  • Masalah ini dapat direproduksi secara independen bukan hanya pada rg yang disertakan di OpenAI Codex, tetapi juga pada biner resmi yang identik byte per byte, sehingga dipastikan bukan masalah yang bergantung pada Codex

Lingkungan kejadian

  • Versi yang digunakan adalah ripgrep 15.2.0 rev e89fff8 dengan fitur +pcre2
    • SIMD saat kompilasi: +SSE2,-SSSE3,-AVX2
    • SIMD saat runtime: +SSE2,+SSSE3,+AVX2
    • PCRE2 10.45 dan JIT tersedia
  • Sistem operasi yang digunakan adalah OpenSUSE Tumbleweed Linux x86_64
  • rg bundel OpenAI Codex yang pertama kali ditemukan identik byte per byte dengan rilis resmi x86_64-unknown-linux-musl
  • Terpisah dari Codex, masalah juga direproduksi pada biner resmi, dan biner untuk analisis dibangun dengan simbol debug menggunakan perintah berikut
    • CROSS_CONTAINER_ENGINE=podman CARGO_PROFILE_RELEASE_DEBUG=true ~/.cargo/bin/cross build --release --target x86_64-unknown-linux-musl

Langkah reproduksi

  • generate_repro_tree.py membuat pohon file acak yang meniru statistik repositori tempat masalah ini awalnya terjadi
    • Program ini ditulis dengan LLM
    • Hasil yang dibuat berukuran sekitar 20GiB dengan 1,8 juta file
  • Dari root pohon yang dibuat, lakukan pencarian berulang untuk string acak yang tidak ada
    • while true; do rg tnoheueunotshisnthukoethnsueothnsiuothonesuioseuinth; done
  • Diamati bahwa pohon pencarian yang cukup besar penting untuk reproduksi
  • Pada sistem 24 core, jika ada cukup RAM kosong agar seluruh pohon masuk ke kernel block cache, crash biasanya terjadi setelah sekitar 1 menit

Titik crash

  • Hasil aktual adalah SIGSEGV yang meninggalkan core dump
  • Bagian teratas stack trace adalah get_meta dari musl mallocng, dan crash terjadi pada titik pemeriksaan integritas metadata heap
  • Alur pemanggilan berlanjut dari opendir yang memanggil calloc, lalu ke penelusuran direktori pustaka standar Rust dan worker ripgrep ignore::walk
    • get_meta__malloc_allzeropcallocopendir
    • std::fs::read_dirignore::walk::Work::read_dirignore::walk::Worker::run
  • Sebagai bahan analisis, dilampirkan core dump dan biner rg terkait

Perilaku yang diharapkan dan status saat ini

  • Perilaku yang diharapkan adalah tetap berjalan tanpa kesalahan segmentasi bahkan pada pencarian skala besar dengan konkurensi tinggi
  • Informasi yang diberikan tidak mencakup kepastian penyebab, usulan perbaikan, hasil peninjauan, atau status penyelesaian akhir

1 komentar

 
GN⁺ 3 jam lalu
Komentar Hacker News
  • Ada bagian menarik di patch kernel: https://lore.kernel.org/all/CALCETrXbj__SFQMzPZhES5y6-sh4np-...

    Saya melihat laporan bug menarik di ripgrep, serta analisis buatan AI yang tekun tetapi cukup buruk
    Ini merujuk ke https://github.com/dfoxfranke/ripgrep-3494-analysis, dan saya juga merasa tulisan itu terlalu panjang untuk dianggap ditulis manusia. Selain itu, thread ini tampaknya baru diunggah hari ini.

    • Membacanya menyiksa, dan di seluruh tulisan panjang lebar itu saya tidak menemukan bagian yang menunjuk kode atau area yang sama seperti yang ditemukan orang sungguhan di lore.kernel. Saya ingin bertanya kepada orang yang lebih memahami Claude: apakah ada bagian yang benar-benar mengidentifikasi penyebab sebenarnya?
    • Hingga sekitar tahun 2000, orang yang memakai ponsel di tempat umum diperlakukan seperti orang sok yang menyebalkan, dan AI sekarang juga sedang melewati fase penolakan yang tidak menyenangkan serupa.
      Kalau ini dua tahun lalu, analisis seperti ini akan dianggap sebagai hasil seseorang yang dengan murah hati menyumbangkan waktunya untuk komunitas, tetapi sekarang karena kita tahu sumbernya dan biaya tokennya hanya 0,06 dolar, orang jadi enggan membacanya. Fakta bahwa ini juga mengisyaratkan apa yang akan terjadi ke depan turut berperan.
      Beberapa tahun lagi, manusia yang menyelidiki bug secara langsung akan menjadi pilihan terakhir, dan agen AI lain akan membaca laporan serta memverifikasi perbaikannya. Sepertinya kita akan bergerak dari tahap mengejek ke tahap acuh tak acuh, seperti kita mengabaikan assembly yang dihasilkan compiler dan pengguna ponsel di tempat umum.
  • Saya paham memakai allocator bawaan musl apa adanya karena praktis, tetapi aneh jika aplikasi yang memang mengejar kecepatan tidak beralih ke allocator yang lebih cepat.
    mallocng rentan terhadap kontensi multi-thread. Aplikasi yang biasanya bottleneck di I/O pun, ketika dibangun dengan musl, mengalami bottleneck malloc hanya dengan 8 thread; setelah diganti ke mimalloc, performanya meningkat 20 kali lipat, menjadi sangat dekat dengan konfigurasi bawaan glibc dan sedikit lebih lambat daripada glibc+mimalloc.
    Memang ada masalah menarik di sini, tetapi seharusnya sejak awal tidak terungkap dengan cara seperti ini.

    • Saat dibangun untuk musl 64-bit, ripgrep sebenarnya menetapkan jemalloc sebagai allocator global: https://github.com/BurntSushi/ripgrep/blob/435f59fc4b43af3ab...
    • Dari stack segfault, alokasi terjadi di opendir milik musl libc. Cara Rust mengganti allocator yang dipakai bukan mengganti allocator untuk seluruh proses, melainkan hanya mengganti allocator yang dipanggil oleh kode Rust.
      Namun jika tetap harus melewati allocator yang memakai lock global, ripgrep tampaknya lebih baik menghindari penggunaan opendir dari libc.
    • Ini adalah bug kernel. Saya setuju bahwa allocator libc sering kali buruk tanpa alasan jelas, tetapi masalah ini tampaknya bisa terjadi dengan probabilitas serupa di kode aplikasi lain, termasuk mimalloc atau glibc.
    • Kebanyakan program meningkatkan kecepatan dengan menggunakan ulang alokasi. Kalau tidak mengalokasikan sama sekali, allocator cepat pun tidak diperlukan, dan pekerjaan ripgrep sendiri pada dasarnya tidak menuntut alokasi yang sering.
    • Justru berkat fitur hardening yang disediakan mallocng milik musl, bug kernel ini bisa ditemukan. Kalau tidak, mungkin ia diam-diam merusak memori selama berbulan-bulan tanpa terlihat.
  • Jika Anda menjalankan ripgrep terhadap sistem file klaster skala besar di klaster HPC, segera hentikan dan desain ulang alur kerja Anda. Pekerjaan seperti ini menghasilkan banyak I/O kecil, dan itu adalah titik lemah sistem file klaster skala besar.
    Anda memindahkan pekerjaan yang seharusnya ditangani di lapisan memori berbandwidth tinggi klaster ke lapisan metadata sistem file; beberapa pengguna saja yang menjalankannya bersamaan bisa melumpuhkan seluruh sistem file berbandwidth tinggi.

    • Ini bukan klaster HPC, hanya btrfs yang saya pakai di workstation saya.
    • Saya sempat bertanya-tanya apakah akar penyebab GitHub yang belakangan tidak stabil juga mirip. Miliaran operasi file kecil yang diperkuat oleh penggunaan AI tiba-tiba terjadi, dan karena grafik objek pada dasarnya terfragmentasi, sulit juga melakukan prefetch satu halaman lalu membuat operasi Git biasa hanya menyentuh objek di dalam halaman itu.
      Jika isi https://isolveproblems.substack.com/p/how-microsoft-vaporize... sedikit saja benar, satu jalur yang tidak teroptimasi dalam abstraksi sistem file Azure saja bisa membuat lonjakan penggunaan berubah menjadi radius gangguan yang sangat besar.
  • Mungkin lebih baik menautkan langsung analisis bug kernel ini: https://github.com/dfoxfranke/ripgrep-3494-analysis

    • Saya mencoba membacanya, tetapi menyerah di paragraf Headline pertama.
      Ada kalimat seperti “nilai yang disimpan thread pada halaman anonim yang baru terkena page fault menghilang saat dibaca ulang oleh thread yang sama sekitar 10 instruksi kemudian, dan backing halaman diganti saat fungsi berjalan”, “jika pagemap dibaca pada saat fault, backing-nya adalah zero page kernel”, “mekanismenya dilokalisasi sebagai interaksi antara fast path anonymous fault dari lock per-VMA dan TLB shootdown dari munmap bersamaan”.
      Sulit memahami apa itu backing dari halaman, apa maksud freshly-faulted atau “sekitar 10 instruksi kemudian”, dan bagaimana mekanisme bisa “dilokalisasi”. Ini terasa lebih seperti rangkaian kata yang ditempel-tempel daripada penjelasan teknis.
      Contoh dokumen teknis yang baik adalah ini: https://yifan.lu/2019/01/11/the-first-f00d-exploit/
    • Bagian “overflow tetap overflow, use-after-free tetap use-after-free, dan race pada mask musl tetap race” terdengar seperti puisi komputer dan membuat saya tertawa.
      Kesimpulannya kira-kira tampak bahwa ada masalah pada kombinasi Linux 7.0 dan musl 1.2.5, tetapi reproduksinya masih hanya berhasil sesekali saat memberi beban berat pada CPU Threadripper fisik yang sama, jadi masalah hardware belum bisa dikesampingkan.
    • Membaca laporan bug buatan AI itu mengerikan.
    • Pelacakannya sendiri bagus, tetapi penjelasannya tidak masuk akal. Flush TLB tambahan tidak mungkin menjadi kesalahan, dan CPU boleh melakukan flush kapan saja. Kesalahan sebenarnya tampaknya ada pada keberadaan zero-page PTE ketika seharusnya tidak ada.
      Ini terlihat seperti kondisi race rumit yang terjadi saat CPU bermigrasi pada momen yang tidak pas, atau bug pada jalur penghapusan page table yang untuk sementara mengekspos PTE yang salah. Saya juga tidak berpikir PFN zero page adalah 0.
      Dugaan saya, dalam proses penghapusan page table langsung, CPU mungkin diizinkan membaca tabel yang sudah dibebaskan dan digunakan ulang melalui entri cache dari struktur paging tingkat atas. Saya pernah men-debug masalah seperti ini dulu, dan itu benar-benar mengerikan.
    • Ini tipikal analisis sampah LLM yang bertele-tele. Bisa saja analisisnya benar, tetapi sulit dibaca dengan saksama; kalau manusia membuat analisis yang sama, panjangnya mungkin hanya seperlima.
  • Mengapa bug ini terjadi hanya di musl libc, bukan libc lain?

    • Hanya kebetulan, dan ini juga hanya terjadi di satu mesin.
    • Kemungkinan besar karena allocator musl langsung mengekspos satu halaman yang baru terkena page fault kepada aplikasi. Allocator lain biasanya melakukan pra-alokasi beberapa halaman sekaligus, sehingga jendela waktu kondisi race menjadi lebih sempit.
  • Biasanya saya akan mencurigai ukuran stack thread milik musl, tetapi saya penasaran apakah ini sudah dipastikan sebagai bug kernel.