⏱️ Lectura: 10 min

Ripgrep puede terminar en un segfault en musl si busca dentro de un árbol con millones de archivos usando muchos hilos a la vez. El bug, reportado el 26 de julio de 2026 en el repositorio oficial de BurntSushi/ripgrep, corrompe la memoria interna del allocator de musl cuando decenas de hilos abren directorios al mismo tiempo.

📑 En este artículo
  1. TL;DR
  2. Introducción
  3. Qué pasó: el segfault en musl paso a paso
  4. Contexto e historia
  5. Detalles técnicos y rendimiento
  6. Cómo probarlo
  7. Impacto y análisis
  8. Qué sigue
  9. Preguntas frecuentes
    1. ¿Qué versión de ripgrep tiene este bug?
    2. ¿Es un bug de ripgrep o de musl?
    3. ¿Afecta a otras herramientas además de ripgrep?
    4. ¿Cómo lo reproduzco yo mismo?
    5. ¿Hay un parche disponible?
    6. ¿Por qué se usan binarios musl en herramientas como Codex?
  10. Referencias

El reportante, dfoxfranke, lo encontró primero en el binario rg que trae empaquetado OpenAI Codex: confirmó que es idéntico, byte a byte, al release oficial ripgrep-15.2.0-x86_64-unknown-linux-musl.tar.gz. Después reprodujo el crash de forma independiente, sin depender de Codex para nada.

TL;DR

  • El issue #3494 de ripgrep, abierto el 26 de julio de 2026, documenta segfaults intermitentes en binarios musl.
  • La versión afectada es ripgrep 15.2.0 (revisión e89fff8), con PCRE2 10.45 y JIT habilitado.
  • El crash ocurre en calloc(), llamado desde opendir(), por una aserción de integridad de heap en mallocng.
  • Reproducirlo exige un árbol de unos 20 GiB en 1,8 millones de archivos, generado con un script dedicado.
  • En una máquina de 24 núcleos con el árbol cacheado, el segfault en musl aparece en promedio al minuto.
  • El mismo binario apareció embebido en OpenAI Codex, idéntico byte a byte al release oficial de ripgrep.
  • El backtrace señala a ignore::walk::Worker, el paralelizador de directorios de ripgrep, como disparador.

Introducción

Ripgrep es el buscador de texto que usan a diario editores como VS Code, Zed y Sublime Text, además de agentes de codificación como Codex, Cursor y Claude Code para indexar repositorios completos en milisegundos. Buena parte de esa velocidad viene de repartir el recorrido de directorios entre tantos hilos como núcleos tenga la máquina, usando el crate ignore del propio autor de ripgrep.

Para distribuirse embebido dentro de otras aplicaciones sin depender de la glibc del sistema destino, muchos proyectos compilan ripgrep contra musl en lugar de glibc. Es exactamente ese combo (musl más alta concurrencia más árboles enormes) el que dispara el bug reportado en el issue #3494.

Qué pasó: el segfault en musl paso a paso

Según el reporte, ripgrep 15.2.0 (revisión e89fff8) compilado para x86_64-unknown-linux-musl, con features:+pcre2 y soporte SIMD (SSE2, SSSE3, AVX2 en runtime), crashea con SIGSEGV al buscar en árboles muy grandes con muchos hilos activos. El entorno de prueba fue openSUSE Tumbleweed x86_64, con un binario compilado con símbolos de depuración vía cross y Podman.

El coredump muestra el crash exactamente en get_meta(), en meta.h línea 141 del código fuente de musl, llamado desde __malloc_allzerop() en malloc.c:384 y desde ahí desde calloc() en calloc.c:41. Ese calloc() lo invoca opendir(), que a su vez es llamado por std::fs::read_dir de la biblioteca estándar de Rust.

El origen de esa llamada está en ignore::walk::Work::read_dir, dentro de crates/ignore/src/walk.rs, ejecutado por cada Worker::run_one que corre en su propio hilo. Cuantos más hilos abran directorios en paralelo, mayor la probabilidad de que dos de ellos pisen la misma estructura de metadata dentro de mallocng antes de que termine de escribirse.

Terminal mostrando una búsqueda con ripgrep sobre un árbol grande de archivos
El crash exige un árbol de unos 20 GiB repartidos en 1,8 millones de archivos. Foto de Alex Pudov en Unsplash

Contexto e historia

Musl reemplazó su allocator original por mallocng en la versión 1.24, publicada en 2020, precisamente para cerrar una serie de vulnerabilidades de corrupción de heap que arrastraba el allocator anterior. Mallocng organiza la memoria en clases de tamaño y usa objetos meta para llevar el registro de qué slabs están libres y cuáles ocupados; get_meta() es la función que valida esa contabilidad antes de entregar un bloque nuevo.

Glibc, la biblioteca estándar que usa la mayoría de las distros de escritorio y servidor, resuelve malloc/calloc con un allocator distinto (derivado de ptmalloc2) y no aparece mencionada en ningún reporte equivalente para este caso. Eso ubica el origen del bug del lado de musl, no del código Rust de ripgrep, aunque sea el patrón de concurrencia de ripgrep el que lo dispara con tanta consistencia.

💭 Clave: el bug no vive en ripgrep. Vive en cómo mallocng de musl maneja calloc() cuando decenas de hilos llaman opendir() al mismo tiempo y de forma sostenida.

Detalles técnicos y rendimiento

El patrón de reproducción es específico: un árbol de aproximadamente 20 GiB distribuidos en 1,8 millones de archivos, generado con un script que imita la distribución estadística del repositorio original donde apareció el bug. En una máquina de 24 núcleos, con memoria suficiente para que el kernel cachee todo el árbol en el page cache, el segfault en musl aparece en promedio al cabo de un minuto corriendo rg en loop.

Esa combinación (E/S ya resuelta desde caché, así que el cuello de botella pasa a ser CPU y sincronización de memoria) es justamente la que expone condiciones de carrera: cuantos más hilos terminan de leer un directorio y piden metadata nueva casi al mismo tiempo, más chances hay de que mallocng entregue o libere un bloque en el momento exacto en que otro hilo lo está validando.

Build targetCuándo usarlaVentajaLimitación
x86_64-unknown-linux-muslBinario estático embebido en otra app (Codex, contenedores mínimos, distribución sin dependencias)No depende de la versión de glibc del sistema destinoUsa mallocng: expuesto a este segfault en musl bajo alta concurrencia
x86_64-unknown-linux-gnuInstalación nativa vía el gestor de paquetes de la distro (apt, dnf, pacman)Usa el allocator de glibc, sin este bug reportado hasta ahoraRequiere una glibc compatible instalada en el sistema destino

sequenceDiagram
    participant W1 as Hilo worker 1
    participant W2 as Hilo worker 2
    participant H as Heap mallocng
    W1->>H: opendir() invoca calloc()
    W2->>H: opendir() invoca calloc()
    H-->>W1: metadata de heap corrupta
    Note over W1,H: get_meta() falla su assert
    H-->>W1: SIGSEGV

Cómo probarlo

Para instalar ripgrep en cualquier plataforma:

# Windows (PowerShell, vía winget)
winget install BurntSushi.ripgrep.MSVC

# macOS (Homebrew)
brew install ripgrep

# Linux, Debian/Ubuntu
sudo apt install ripgrep

# Linux, Arch
sudo pacman -S ripgrep

# Cualquier plataforma con Rust instalado
cargo install ripgrep --features pcre2

Para reproducir el segfault en musl específicamente necesitás compilar o descargar el target x86_64-unknown-linux-musl y armar un árbol de prueba lo bastante grande:

# 1. Generar un árbol de prueba de ~20 GiB con 1,8M de archivos
# (adaptado del script original que usó dfoxfranke en el issue #3494)
python3 generate_repro_tree.py --root /tmp/repro-tree \
  --files 1800000 --total-size 20GiB

# 2. Buscar en loop una cadena que no existe en el árbol
cd /tmp/repro-tree
while true; do
  rg tnoheueunotshisnthukoethnsueothnsiuothonesuioseuinth
done
⚠️ Ojo: este repro necesita unos 20 GiB de disco libre y satura los núcleos de la máquina por varios minutos. No lo corras en un servidor de producción ni en una laptop con poco espacio.

Para confirmar que estás usando el binario musl y no el de glibc, corré rg --version y revisá la línea de features: si aparece x86_64-unknown-linux-musl en el nombre del artefacto que descargaste, estás en el escenario expuesto al bug.

Diagrama de bloques de memoria heap corrupta
Mallocng reemplazó al allocator anterior de musl en la versión 1.24, en 2020. Foto de Erik Mclean en Unsplash

Impacto y análisis

El caso concreto que originó el reporte es OpenAI Codex, que empaqueta un rg estático para musl como parte de su herramienta de búsqueda de código. Pero el mismo riesgo aplica a cualquier producto que distribuya ripgrep compilado contra musl y lo use con concurrencia alta sobre repositorios grandes: agentes de codificación que indexan monorepos completos, pipelines de CI que corren búsquedas masivas, o contenedores mínimos basados en Alpine (que usa musl como libc por defecto).

Para un agente de IA que recorre un repositorio de millones de líneas antes de responder una pregunta, un segfault en musl en medio de la búsqueda no es solo una molestia: puede cortar una tarea automatizada a mitad de camino sin ningún mensaje de error legible para el usuario final, porque el proceso simplemente muere.

Qué sigue

A la fecha de esta nota el issue sigue abierto en el tracker de ripgrep, sin un fix confirmado ni un advisory publicado por el proyecto musl. Las salidas posibles son varias: que musl corrija la condición de carrera en mallocng, que ripgrep limite la cantidad de hilos cuando detecta que corre contra musl, o que el propio proyecto migre a un allocator alternativo (como mimalloc o jemalloc) enlazado estáticamente para esquivar el bug del sistema.

Mientras tanto, la mitigación más simple para quien empaquete ripgrep contra musl es bajar la concurrencia con la flag --threads, al costo de perder algo de velocidad en búsquedas sobre árboles enormes.

📖 Resumen en Telegram: Ver resumen

Probalo vos: corré rg --version sobre tu binario y, si dice x86_64-unknown-linux-musl, seguí los pasos de la sección Cómo probarlo para ver si tu build también dispara la aserción de mallocng.

Preguntas frecuentes

¿Qué versión de ripgrep tiene este bug?

Se confirmó en ripgrep 15.2.0 (revisión e89fff8), compilado para el target x86_64-unknown-linux-musl con PCRE2 10.45. No hay reportes de que builds contra glibc reproduzcan el mismo segfault en musl.

¿Es un bug de ripgrep o de musl?

El código que falla es interno de musl, dentro de mallocng. Ripgrep dispara la condición porque su crate ignore reparte el recorrido de directorios entre muchos hilos que llaman opendir() de forma simultánea y sostenida.

¿Afecta a otras herramientas además de ripgrep?

El propio reportante lo encontró primero en el binario rg embebido en OpenAI Codex, confirmado idéntico byte a byte al release oficial. Cualquier herramienta que empaquete un rg estático contra musl y lo corra con alta concurrencia sobre árboles grandes puede exponerse al mismo problema.

¿Cómo lo reproduzco yo mismo?

Necesitás un árbol de unos 20 GiB con 1,8 millones de archivos y correr rg en loop buscando una cadena inexistente, en una máquina con núcleos y RAM suficientes para cachear el árbol completo. El detalle está en la sección Cómo probarlo más arriba.

¿Hay un parche disponible?

Al momento de esta publicación el issue #3494 sigue abierto y no hay advisory de musl.libc.org que confirme un fix. Como mitigación práctica, se puede evitar el target musl o bajar la concurrencia con –threads.

¿Por qué se usan binarios musl en herramientas como Codex?

Los binarios estáticos musl no dependen de la versión de glibc instalada en el sistema destino, así que el mismo ejecutable corre igual en cualquier distro de Linux o dentro de un contenedor mínimo, algo valioso para herramientas que se distribuyen embebidas dentro de otras apps.

Referencias

📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.

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


Andrés Morales

Desarrollador e investigador en inteligencia artificial. Escribe sobre modelos de lenguaje, frameworks, herramientas para devs y lanzamientos open source. Cubre papers de ML, ecosistema de startups tech y tendencias de programación.

0 Comentarios

Deja un comentario

Marcador de posición del avatar

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.