Ripgrep 15.2.0 musl バイナリが大規模検索中にセグメンテーション違反を発生

Ripgrep musl バイナリで SIGSEGV クラッシュが発生

x86_64-unknown-linux-musl ターゲット向けにビルドされた Ripgrep バージョン 15.2.0 のバイナリは、高い並行性で非常に大規模な検索を実行する際に、稀に SIGSEGV (セグメンテーション違反) でクラッシュすることがあります。このクラッシュは、opendir によって開始された calloc 呼び出し中に発生し、具体的には musl の mallocng アロケータ内のヒープメタデータに関する整合性アサーション失敗をトリガーします。

技術的な根本原因とバックトレース

このクラッシュは、ripgrep が非常に大規模なディレクトリツリー(具体的には、数百万個のファイルを含むもの、例:20GiB のデータにわたる 180 万個のファイル)を走査する際に発生します。失敗は mallocng/meta.h 内の get_meta() でのクラッシュとして現れ、ヒープメタデータが破損または不整合になっていることを示しています。

クラッシュに至る実行パスは以下の通りです:

  1. ignore::walk::Worker::run がディレクトリ走査を開始します。
  2. std::fs::read_dir が呼び出され、ディレクトリの内容をリストアップします。
  3. これにより、C ライブラリ関数 opendir の呼び出しにつながります。
  4. opendir はディレクトリストリームのためにメモリを割り当てるために calloc を呼び出します。
  5. mallocng (musl のアロケータ) がヒープメタデータ内の整合性違反を検出し、セグメンテーション違反をトリガーします。

再現要件

このバグを再現するには、規模と並行性に関する特定の条件が必要です:

  • ターゲットバイナリ: x86_64-unknown-linux-musl 向けにビルドされたバイナリ (例: 公式の 15.2.0 リリースバイナリ)。
  • 規模: 膨大な数のファイルを含むディレクトリツリー。再現スクリプト (generate_repro_tree.py) を使用して、約 180 万個のファイルと 20GiB のデータを持つツリーを作成しました。
  • 並行性: 高い CPU コア数 (例: 24 コア) と高い並行性レベル。
  • 環境: この問題は OpenSUSE Tumbleweed Linux x86_64 で観察されました。

コミュニティの洞察と分析

この問題に関する議論では、根本原因が ripgrep 自体を超えて、musl libc と Linux カーネルの間のより深い相互作用にまで及んでいることが強調されています。

カーネルレベルのバグ

証拠は、この問題が実際にはカーネルのバグの現れであることを示唆しています。コミュニティメンバーは、クラッシュを ripgrep の Rust コード内の論理エラーではなく、カーネルレベルのメモリ管理に関連付ける詳細な分析とカーネルパッチを引用しました。

アロケータのパフォーマンスと競合

一部のユーザーは、musl の mallocng が高並行マルチスレッド実行時の競合に苦戦することで知られていると指摘しました。ある貢献者は、デフォルトの musl アロケータを mimalloc のようなより高性能な代替品に置き換えることで、競合を大幅に減らし、高性能アプリケーションにおいてこのような種類の失敗を回避できる可能性があると提案しました:

"mallocng はマルチスレッド実行時の競合の処理に長けていません... mimalloc への切り換えてパフォーマンスが 20 倍向上しました... 最初からこのような形で表面化すべきではありませんでした。"

ファイルシステムのインパクト

運用的な観点からは、大規模なクラスターファイルシステム全体で高並行検索を実行することは、ファイルシステムのメタデータメカニズムに悪影響を及ぼす可能性があります。あるユーザーは、このパターンによる小さな I/O がメタデータサーバーに極端な負荷を生成し、高帯域幅ファイルシステムを機能不全に陥らせる可能性があると警告しました。

Sources