Yeda AI Tips · #068

English

Ejecuta tu suite de pruebas nativas bajo sanitizers

En C y C++, perseguir la cobertura de líneas pasa por alto los bugs que de verdad te hacen crashear. Ejecuta tu suite bajo sanitizers. La cobertura de líneas te dice que una línea se ejecutó — no te dice nada del use-after-free, del overflow con signo ni del data race escondido en esa misma línea. Los sanitizers instrumentan el runtime, así que las mismas pruebas que ya tienes empiezan a fallar en los bugs que importan.

Por qué la cobertura de líneas engaña al código nativo

Un reporte de cobertura responde una sola pregunta: ¿se ejecutó esta línea? En lenguajes gestionados es un proxy razonable de "este comportamiento se verificó". En C y C++ no lo es, porque los peores modos de falla son silenciosos en tiempo de ejecución: una lectura fuera de límites devuelve basura que parece válida, un use-after-free lee memoria obsoleta pero plausible, el comportamiento indefinido se optimiza en algo que solo falla con -O2, y un data race pasa 999 de cada 1,000 corridas. Todas esas líneas aparecen verdes en el reporte de cobertura. El bug se lanza igual.

Los tres sanitizers y qué te da cada uno

Cada sanitizer es un flag de compilación y enlazado más un runtime instrumentado. Las mismas pruebas, sin aserciones nuevas.

SanitizerFlagDetectaRalentización típica
AddressSanitizer (ASan)-fsanitize=addressFuera de límites en heap/stack/globales, use-after-free, use-after-return, use-after-scope, double-free, free inválido, fugas (experimental)~2x
UndefinedBehaviorSanitizer (UBSan)-fsanitize=undefinedOverflow de enteros con signo, shifts inválidos, dereferencia de puntero nulo o desalineado, subíndices fuera de límites, división entre ceroBaja
ThreadSanitizer (TSan)-fsanitize=threadData races~5x–15x (5x–10x en memoria)

Empezar es una sola recompilación de tus binarios de prueba:

# ASan + UBSan combine in a single build:
clang++ -fsanitize=address,undefined -fno-sanitize-recover=all \
        -g -O1 -fno-omit-frame-pointer tests.cc -o tests_asan
./tests_asan

# TSan is a separate build — it can't share a binary with ASan:
clang++ -fsanitize=thread -g -O1 tests.cc -o tests_tsan
./tests_tsan

Tres detalles que importan: enlaza con el driver del compilador (clang++), no con ld, para que los runtimes de los sanitizers se incluyan; agrega -g -fno-omit-frame-pointer para que los reportes tengan stack traces útiles; y pasa -fno-sanitize-recover=all para que UBSan termine en el primer hallazgo en vez de imprimir y continuar — en CI, una advertencia que nadie lee es un bug que nadie arregla.

Lo que esto atrapa y la cobertura jamás verá

Las corridas con sanitizers sacan a la luz use-after-free, lecturas fuera de límites, overflows con signo y races que un tablero de cobertura totalmente verde lanzaría a producción sin problema. Y los hallazgos se acumulan: cada reporte de sanitizer trae un stack trace que apunta a un defecto reproducible. Convierte cada uno en una prueba de regresión permanente, y tu suite se fortalece con cada captura. Para código nativo, una suite que corre limpia bajo ASan, UBSan y TSan suele ser una señal más fuerte que otros cinco puntos de cobertura de líneas.

Trucos avanzados

Recursos

¿Construyendo o endureciendo una base de código nativa con AI? Yeda AI diseña, audita y lanza flujos de ingeniería asistidos por AI en producción. Este artículo acompaña al reel #068 de la serie Yeda AI Tips. Read in English.

Talk to us · Read the blog