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