1 puntos por GN⁺ 3 시간 전 | 1 comentarios | Compartir por WhatsApp
  • El binario x86_64-unknown-linux-musl de Ripgrep 15.2.0 termina de forma intermitente con SIGSEGV al buscar en árboles de archivos grandes con alta concurrencia
  • El fallo ocurre dentro de calloc, llamado por opendir, y el punto de verificación de integridad de metadatos del heap de musl mallocng aparece en la parte superior del seguimiento de pila
  • El entorno de reproducción consiste en un árbol de aproximadamente 20 GiB y 1.8 millones de archivos, donde se busca repetidamente con rg una cadena inexistente
  • En un sistema de 24 núcleos, si hay suficiente RAM para que el árbol de búsqueda quepa en la caché de bloques del kernel, el problema suele aparecer en aproximadamente 1 minuto
  • Se reprodujo de forma independiente no solo con el rg incluido en OpenAI Codex, sino también con un binario idéntico byte a byte al lanzamiento oficial, por lo que se confirmó que es un problema no relacionado con las dependencias de Codex

Entorno afectado

  • La versión usada es ripgrep 15.2.0 rev e89fff8, e incluye la función +pcre2
    • SIMD en compilación: +SSE2,-SSSE3,-AVX2
    • SIMD en ejecución: +SSE2,+SSSE3,+AVX2
    • PCRE2 10.45 y JIT están disponibles
  • El sistema operativo es OpenSUSE Tumbleweed Linux x86_64
  • El rg incluido en OpenAI Codex donde se descubrió inicialmente es idéntico byte a byte al lanzamiento oficial x86_64-unknown-linux-musl
  • También se reprodujo con el binario oficial, independientemente de Codex; el binario de análisis se compiló con símbolos de depuración mediante el siguiente comando
    • CROSS_CONTAINER_ENGINE=podman CARGO_PROFILE_RELEASE_DEBUG=true ~/.cargo/bin/cross build --release --target x86_64-unknown-linux-musl

Pasos para reproducirlo

  • generate_repro_tree.py genera un árbol de archivos aleatorio que imita las estadísticas del repositorio donde apareció originalmente el problema
    • Este programa fue escrito con un LLM
    • El resultado generado tiene un tamaño aproximado de 20 GiB y 1.8 millones de archivos
  • Desde la raíz del árbol generado, buscar repetidamente una cadena arbitraria inexistente
    • while true; do rg tnoheueunotshisnthukoethnsueothnsiuothonesuioseuinth; done
  • Se observó que un árbol de búsqueda suficientemente grande es esencial para reproducirlo
  • En un sistema de 24 núcleos, si hay suficiente RAM libre para que todo el árbol entre en la caché de bloques del kernel, normalmente falla después de aproximadamente 1 minuto

Punto del fallo

  • El resultado real es un SIGSEGV que deja un volcado de core
  • La parte superior del seguimiento de pila es get_meta de musl mallocng, y el fallo ocurre en el punto de verificación de integridad de los metadatos del heap
  • El flujo de llamadas es que opendir llama a calloc, y luego continúa con el recorrido de directorios de la biblioteca estándar de Rust y los workers de ignore::walk de ripgrep
    • get_meta__malloc_allzeropcallocopendir
    • std::fs::read_dirignore::walk::Work::read_dirignore::walk::Worker::run
  • Como material de análisis se adjuntaron el volcado de core y el binario rg correspondiente

Comportamiento esperado y estado actual

  • El comportamiento esperado es que se ejecute sin errores de segmentación incluso en búsquedas grandes y con alta concurrencia
  • El contenido proporcionado no incluye una causa definitiva, una corrección, resultados de revisión ni si el problema quedó resuelto

1 comentarios

 
GN⁺ 3 시간 전
Comentarios en Hacker News
  • Hay una parte interesante en el parche del kernel: https://lore.kernel.org/all/CALCETrXbj__SFQMzPZhES5y6-sh4np-...

    Vi un reporte de bug interesante de ripgrep, y un análisis generado por IA diligente pero bastante malo
    Se refiere a https://github.com/dfoxfranke/ripgrep-3494-analysis, y yo también pensé que era demasiado largo para parecer escrito por una persona. Además, este hilo parece haber aparecido justo hoy

    • Fue un suplicio leerlo, y en ninguna parte de ese texto tan verboso pude encontrar que señalara el mismo código o zona que la persona real de lore.kernel descubrió. Me gustaría preguntarle a alguien que entienda mejor a Claude si realmente hay alguna parte donde identifique la causa real.
    • Hasta alrededor del año 2000, la gente que usaba celulares en lugares públicos era vista como presumida y desagradable, y ahora la IA está pasando por una fase de rechazo incómodo parecida
      Hace 2 años, un análisis así se habría visto como el resultado de donar generosamente tiempo a la comunidad, pero ahora, sabiendo que la fuente y el costo de tokens es apenas de 0.06 dólares, ya ni lo quiero leer. También influye que anticipa lo que viene
      En unos años, que una persona investigue un bug directamente será el último recurso, y otros agentes de IA leerán el reporte para validar las correcciones. Igual que ignoramos el ensamblador generado por el compilador y a la gente usando celulares en público, parece que pasaremos de la etapa de burla a la de indiferencia
  • Entiendo usar el asignador predeterminado de musl por comodidad, pero en una aplicación cuyo objetivo es la velocidad misma es raro no cambiarlo por uno más rápido
    mallocng es vulnerable a la contención multihilo. Una aplicación que normalmente estaba limitada por I/O, al compilarla con musl, ya sufría un cuello de botella en malloc con solo 8 hilos, y al cambiar a mimalloc el rendimiento mejoró 20 veces, quedando muy cerca de la configuración predeterminada de glibc, aunque un poco por debajo de glibc+mimalloc
    En efecto hay un problema interesante, pero para empezar no debería haberse manifestado de esta manera

    • ripgrep, de hecho, establece jemalloc como asignador global al compilar para musl de 64 bits: https://github.com/BurntSushi/ripgrep/blob/435f59fc4b43af3ab...
    • Viendo la traza del fallo de segmentación, la asignación ocurre en opendir de musl libc. La forma en que Rust reemplaza el asignador no cambia el asignador de todo el proceso, sino solo el que llama el código Rust
      Aun así, si hay que pasar por un asignador con bloqueo global, parece mejor que ripgrep evite usar opendir de libc
    • Esto es un bug del kernel. Estoy de acuerdo en que los asignadores de libc suelen ser pésimos sin razón aparente, pero este problema parece poder ocurrir con probabilidades similares también en otro código de aplicación, incluyendo mimalloc o glibc
    • La mayoría de los programas reutilizan asignaciones para ganar velocidad. Si no asignas en absoluto, tampoco necesitas un asignador rápido, y el trabajo de ripgrep en sí no requiere intrínsecamente asignaciones frecuentes
    • De hecho, fue gracias a las funciones de endurecimiento que ofrece mallocng de musl que se pudo descubrir el bug del kernel. De otro modo, podría haber estado corrompiendo memoria en silencio durante meses sin llamar la atención
  • Si estás ejecutando ripgrep contra un sistema de archivos de clúster a gran escala en un clúster HPC, deberías detenerte de inmediato y rediseñar tu flujo de trabajo. Este tipo de tarea genera grandes cantidades de I/O pequeño, y ese es el talón de Aquiles de los sistemas de archivos de clúster a gran escala
    Terminas trasladando al plano de metadatos del sistema de archivos un trabajo que debería resolverse en la jerarquía de memoria de alto ancho de banda del clúster, y basta con que unos pocos usuarios lo ejecuten al mismo tiempo para dejar paralizado todo el sistema de archivos de alto ancho de banda

    • No es un clúster HPC, solo es btrfs en mi estación de trabajo
    • Me preguntaba si la causa raíz del deterioro reciente de GitHub no será algo parecido. De pronto están ocurriendo miles de millones de operaciones sobre archivos pequeños, amplificadas por el uso de IA, y los grafos de objetos son inherentemente fragmentados, así que es difícil incluso precargar una página y lograr que una operación típica de Git toque solo los objetos de esa página
      Si aunque sea un poco de lo que dice https://isolveproblems.substack.com/p/how-microsoft-vaporize... es cierto, entonces basta con una sola ruta no optimizada en la abstracción de sistema de archivos de Azure para que un aumento de uso se convierta en un radio de impacto enorme
  • Tal vez sea mejor enlazar directamente el análisis del bug del kernel: https://github.com/dfoxfranke/ripgrep-3494-analysis

    • Intenté leerlo, pero me rendí en el primer párrafo de Headline
      Siguen frases como: “el valor almacenado por un hilo en una página anónima recién page-faulted desaparece en una relectura del mismo hilo unas 10 instrucciones después, y el backing de la página se reemplaza durante la ejecución de la función”, “si lees pagemap en el momento del fault, el backing es la zero page del kernel”, y “el mecanismo se localiza en la interacción entre la ruta rápida de anonymous fault con bloqueo por VMA y el TLB shootdown de un munmap concurrente”
      Es difícil entender qué significa el backing de una página, qué quiere decir freshly-faulted o “unas 10 instrucciones después”, o cómo se supone que un mecanismo se “localiza”. Más que una explicación técnica, parece texto pegando palabras unas con otras
      Un ejemplo de documentación técnica bien hecha sería este: https://yifan.lu/2019/01/11/the-first-f00d-exploit/
    • La parte que dice “overflow still overflows, use-after-free still use-after-frees, and musl mask races still race” me dio risa, suena a poesía computacional
      La conclusión parece ser, más o menos, que hay un problema con la combinación de Linux 7.0 y musl 1.2.5, pero la reproducción sigue logrando éxito solo de forma intermitente bajo carga fuerte en el mismo CPU físico Threadripper, así que no pudieron descartar un problema de hardware
    • Leer reportes de bugs generados por IA es terrible
    • El rastreo en sí es excelente, pero la explicación no tiene sentido. Un flush adicional de TLB no puede ser el error; la CPU puede hacer flush cuando quiera. El error real parece ser que existió una PTE de zero-page cuando no debería haber existido
      Parece una condición de carrera compleja causada por una migración de CPU en un momento incómodo, o un bug donde la ruta de eliminación de tablas de páginas expuso temporalmente la PTE equivocada. Tampoco creo que el PFN de la zero page sea 0
      Si tuviera que adivinar, es posible que durante la eliminación directa de la tabla de páginas se haya permitido que la CPU leyera una tabla ya liberada y reutilizada mediante una entrada en caché de una estructura de paginación superior. Ya tuve que depurar algo así antes y fue realmente horrible
    • Es el típico análisis verboso de basura de LLM. Tal vez el análisis sea correcto, pero es difícil leerlo en detalle, y si una persona hubiera hecho el mismo análisis, lo habría reducido a una quinta parte
  • ¿Por qué el bug ocurre solo en musl libc y no en otras libc?

    • Es pura casualidad, y además ocurre solo en una sola máquina
    • Lo más probable es que sea porque el asignador de musl expone inmediatamente a la aplicación una sola página recién page-faulted. Otros asignadores normalmente preasignan varias páginas de una vez, así que la ventana temporal de la condición de carrera se vuelve más estrecha
  • Normalmente habría sospechado del tamaño de la pila de hilos en musl, pero me pregunto si ya se confirmó que era un bug del kernel