- Biner x86_64-unknown-linux-musl dari Ripgrep 15.2.0 kadang berhenti dengan
SIGSEGVsaat mencari pada pohon file besar dengan konkurensi tinggi - Crash terjadi di dalam
callocyang dipanggil olehopendir, 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
rgyang 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
e89fff8dengan fitur+pcre2- SIMD saat kompilasi:
+SSE2,-SSSE3,-AVX2 - SIMD saat runtime:
+SSE2,+SSSE3,+AVX2 - PCRE2 10.45 dan JIT tersedia
- SIMD saat kompilasi:
- Sistem operasi yang digunakan adalah OpenSUSE Tumbleweed Linux x86_64
rgbundel 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
SIGSEGVyang meninggalkan core dump - Bagian teratas stack trace adalah
get_metadari musl mallocng, dan crash terjadi pada titik pemeriksaan integritas metadata heap - Alur pemanggilan berlanjut dari
opendiryang memanggilcalloc, lalu ke penelusuran direktori pustaka standar Rust dan worker ripgrepignore::walkget_meta→__malloc_allzerop→calloc→opendirstd::fs::read_dir→ignore::walk::Work::read_dir→ignore::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
Komentar Hacker News
Ada bagian menarik di patch kernel: https://lore.kernel.org/all/CALCETrXbj__SFQMzPZhES5y6-sh4np-...
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
mallochanya 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.
opendirmilik 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
opendirdari libc.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.
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
Headlinepertama.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
munmapbersamaan”.Sulit memahami apa itu
backingdari halaman, apa maksudfreshly-faultedatau “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/
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.
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.
Mengapa bug ini terjadi hanya di musl libc, bukan libc lain?
Biasanya saya akan mencurigai ukuran stack thread milik musl, tetapi saya penasaran apakah ini sudah dipastikan sebagai bug kernel.