⏱️ Lectura: 9 min

Ripgrep can end up with a musl segfault when searching a tree with millions of files using many threads at once. The bug, reported on July 26, 2026 in the official BurntSushi/ripgrep repository, corrupts musl’s internal allocator memory when dozens of threads open directories at the same time.

📑 En este artículo
  1. TL;DR
  2. Introduction
  3. What happened: the musl segfault step by step
  4. Context and history
  5. Technical details and performance
  6. How to test it
  7. Impact and analysis
  8. What’s next
  9. Frequently Asked Questions
    1. Which version of ripgrep has this bug?
    2. Is this a ripgrep bug or a musl bug?
    3. Does it affect other tools besides ripgrep?
    4. How do I reproduce it myself?
    5. Is a patch available?
    6. Why are musl binaries used in tools like Codex?
  10. References

The reporter, dfoxfranke, first found it in the rg binary bundled with OpenAI Codex: they confirmed it is byte-for-byte identical to the official ripgrep-15.2.0-x86_64-unknown-linux-musl.tar.gz release. They then reproduced the crash independently, without relying on Codex at all.

TL;DR

  • Ripgrep issue #3494, opened on July 26, 2026, documents intermittent segfaults in musl binaries.
  • The affected version is ripgrep 15.2.0 (revision e89fff8), with PCRE2 10.45 and JIT enabled.
  • The crash occurs in calloc(), called from opendir(), due to a heap integrity assertion in mallocng.
  • Reproducing it requires a tree of about 20 GiB across 1.8 million files, generated with a dedicated script.
  • On a 24-core machine with the tree cached, the musl segfault appears after about a minute on average.
  • The same binary appeared embedded in OpenAI Codex, byte-for-byte identical to the official ripgrep release.
  • The backtrace points to ignore::walk::Worker, ripgrep’s directory parallelizer, as the trigger.

Introduction

Ripgrep is the text search tool used daily by editors like VS Code, Zed, and Sublime Text, as well as coding agents like Codex, Cursor, and Claude Code to index entire repositories in milliseconds. Much of that speed comes from splitting the directory traversal across as many threads as the machine has cores, using the ignore crate written by ripgrep’s own author.

To be distributed embedded inside other applications without depending on the target system’s glibc, many projects compile ripgrep against musl instead of glibc. It’s exactly that combination (musl plus high concurrency plus huge trees) that triggers the bug reported in issue #3494.

What happened: the musl segfault step by step

According to the report, ripgrep 15.2.0 (revision e89fff8) compiled for x86_64-unknown-linux-musl, with features:+pcre2 and SIMD support (SSE2, SSSE3, AVX2 at runtime), crashes with SIGSEGV when searching very large trees with many active threads. The test environment was openSUSE Tumbleweed x86_64, with a binary compiled with debug symbols via cross and Podman.

The coredump shows the crash exactly in get_meta(), at meta.h line 141 of musl’s source code, called from __malloc_allzerop() in malloc.c:384 and from there from calloc() in calloc.c:41. That calloc() is invoked by opendir(), which is in turn called by Rust’s standard library std::fs::read_dir.

The origin of that call is in ignore::walk::Work::read_dir, inside crates/ignore/src/walk.rs, executed by each Worker::run_one running on its own thread. The more threads opening directories in parallel, the higher the chance that two of them will stomp on the same metadata structure inside mallocng before it finishes being written.

Terminal showing a ripgrep search over a large file tree
The crash requires a tree of about 20 GiB spread across 1.8 million files. Foto de Danny Burke en Unsplash

Context and history

Musl replaced its original allocator with mallocng in version 1.24, released in 2020, precisely to close a series of heap corruption vulnerabilities carried over from the previous allocator. Mallocng organizes memory into size classes and uses meta objects to track which slabs are free and which are occupied; get_meta() is the function that validates that bookkeeping before handing out a new block.

Glibc, the standard library used by most desktop and server distros, resolves malloc/calloc with a different allocator (derived from ptmalloc2) and hasn’t been mentioned in any equivalent report for this case. That places the origin of the bug on musl’s side, not in ripgrep’s Rust code, even though it’s ripgrep’s concurrency pattern that triggers it so consistently.

💭 Key takeaway: the bug doesn’t live in ripgrep. It lives in how musl’s mallocng handles calloc() when dozens of threads call opendir() at the same time, sustained over time.

Technical details and performance

The reproduction pattern is specific: a tree of roughly 20 GiB spread across 1.8 million files, generated with a script that mimics the statistical distribution of the original repository where the bug appeared. On a 24-core machine, with enough memory for the kernel to cache the entire tree in the page cache, the musl segfault appears after about a minute on average when running rg in a loop.

That combination (I/O already resolved from cache, so the bottleneck shifts to CPU and memory synchronization) is exactly what exposes race conditions: the more threads that finish reading a directory and request new metadata at nearly the same time, the higher the chance that mallocng hands out or frees a block at the exact moment another thread is validating it.

Build targetWhen to use itAdvantageLimitation
x86_64-unknown-linux-muslStatic binary embedded in another app (Codex, minimal containers, dependency-free distribution)Doesn’t depend on the target system’s glibc versionUses mallocng: exposed to this musl segfault under high concurrency
x86_64-unknown-linux-gnuNative install via the distro’s package manager (apt, dnf, pacman)Uses glibc’s allocator, with no such bug reported so farRequires a compatible glibc installed on the target system

sequenceDiagram
    participant W1 as Worker thread 1
    participant W2 as Worker thread 2
    participant H as mallocng Heap
    W1->>H: opendir() calls calloc()
    W2->>H: opendir() calls calloc()
    H-->>W1: corrupted heap metadata
    Note over W1,H: get_meta() fails its assert
    H-->>W1: SIGSEGV

How to test it

To install ripgrep on any platform:

# Windows (PowerShell, via winget)
winget install BurntSushi.ripgrep.MSVC

# macOS (Homebrew)
brew install ripgrep

# Linux, Debian/Ubuntu
sudo apt install ripgrep

# Linux, Arch
sudo pacman -S ripgrep

# Any platform with Rust installed
cargo install ripgrep --features pcre2

To reproduce the musl segfault specifically, you need to compile or download the x86_64-unknown-linux-musl target and build a test tree large enough:

# 1. Generate a ~20 GiB test tree with 1.8M files
# (adapted from the original script dfoxfranke used in issue #3494)
python3 generate_repro_tree.py --root /tmp/repro-tree \
  --files 1800000 --total-size 20GiB

# 2. Search in a loop for a string that doesn't exist in the tree
cd /tmp/repro-tree
while true; do
  rg tnoheueunotshisnthukoethnsueothnsiuothonesuioseuinth
done
⚠️ Careful: this repro needs about 20 GiB of free disk space and saturates the machine’s cores for several minutes. Don’t run it on a production server or on a laptop with little free space.

To confirm you’re using the musl binary and not the glibc one, run rg --version and check the features line: if x86_64-unknown-linux-musl appears in the name of the artifact you downloaded, you’re in the scenario exposed to the bug.

Block diagram of corrupted heap memory
Mallocng replaced musl’s previous allocator in version 1.24, in 2020. Foto de Claudio Schwarz en Unsplash

Impact and analysis

The specific case that originated the report is OpenAI Codex, which bundles a static rg for musl as part of its code search tool. But the same risk applies to any product that distributes ripgrep compiled against musl and uses it with high concurrency over large repositories: coding agents that index entire monorepos, CI pipelines that run massive searches, or minimal Alpine-based containers (which use musl as their default libc).

For an AI agent traversing a repository of millions of lines before answering a question, a musl segfault in the middle of a search isn’t just an annoyance: it can cut an automated task off halfway through with no readable error message for the end user, because the process simply dies.

What’s next

As of this writing, the issue remains open in ripgrep’s tracker, with no confirmed fix or advisory published by the musl project. There are several possible outcomes: musl could fix the race condition in mallocng, ripgrep could limit the number of threads when it detects it’s running against musl, or the project itself could migrate to an alternative allocator (like mimalloc or jemalloc) statically linked to sidestep the system’s bug.

In the meantime, the simplest mitigation for anyone packaging ripgrep against musl is to lower concurrency with the --threads flag, at the cost of some speed loss on searches over huge trees.

📖 Summary on Telegram: View summary

Try it yourself: run rg --version on your binary and, if it says x86_64-unknown-linux-musl, follow the steps in the How to test it section to see if your build also triggers the mallocng assertion.

Frequently Asked Questions

Which version of ripgrep has this bug?

It was confirmed in ripgrep 15.2.0 (revision e89fff8), compiled for the x86_64-unknown-linux-musl target with PCRE2 10.45. There are no reports of glibc builds reproducing the same musl segfault.

Is this a ripgrep bug or a musl bug?

The failing code is internal to musl, inside mallocng. Ripgrep triggers the condition because its ignore crate distributes directory traversal across many threads that call opendir() simultaneously and continuously.

Does it affect other tools besides ripgrep?

The reporter themselves first found it in the rg binary embedded in OpenAI Codex, confirmed byte-for-byte identical to the official release. Any tool that bundles a static rg against musl and runs it with high concurrency over large trees can be exposed to the same problem.

How do I reproduce it myself?

You need a tree of about 20 GiB with 1.8 million files and to run rg in a loop searching for a nonexistent string, on a machine with enough cores and RAM to cache the entire tree. The details are in the How to test it section above.

Is a patch available?

At the time of this publication, issue #3494 remains open and there’s no advisory from musl.libc.org confirming a fix. As a practical mitigation, you can avoid the musl target or lower concurrency with –threads.

Why are musl binaries used in tools like Codex?

Static musl binaries don’t depend on the glibc version installed on the target system, so the same executable runs the same way on any Linux distro or inside a minimal container, something valuable for tools distributed embedded inside other apps.

References

📱 Do you like this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day.

Imagen destacada: Foto de Jiří Navrátil en Unsplash

Categories: Noticias Tech

Andrés Morales

Developer and AI researcher. Writes about language models, frameworks, developer tooling, and open source releases. Covers ML papers, the tech startup ecosystem, and programming trends.

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.