Detectar daños en la memoria en C y C ++
Por Arthur Hiken
24 de mayo de 2012
3 min leer
Un vistazo rápido a por qué la corrupción de la memoria de C y C ++ es tan difícil de encontrar a través de la inspección de código, y cómo usar una herramienta de detección de errores de memoria para ayudar (y salvarlo de esas sesiones de depuración de toda la noche).
Los programadores continúan usando lenguajes de programación C y C ++ debido a su potencia, rendimiento y eficiencia. Sin embargo, estos lenguajes son propensos a tener problemas de memoria sutiles como pérdidas de memoria, desbordamiento de búfer, desbordamiento numérico y más. Desafortunadamente, es muy común que tales errores permanezcan ocultos durante las pruebas normales. El software con problemas sutiles, como la corrupción de la memoria, puede ejecutarse sin problemas en una máquina, pero bloquearse en otra, o puede funcionar bien durante un cierto período de tiempo, solo para bloquearse inesperadamente cuando el sistema ha estado funcionando durante una gran cantidad de días.
Este tipo de corrupción de la memoria, así como otros errores comunes como problemas de manipulación de cadenas, inicialización incorrecta y errores de puntero, provocan fallas en la producción. Con el aumento del software integrado en aviones, automóviles, dispositivos médicos y el creciente mercado de IoT, las consecuencias del software defectuoso se han convertido en algo más que clientes insatisfechos, pero pueden poner en peligro la vida.
Comprender la corrupción de la memoria
Corrupción de la memoria errores como esos son desagradables, especialmente si están bien disfrazados. Cuando se manifiestan, pueden ser engañosamente difíciles de reproducir y rastrear. Como ejemplo de lo que puede suceder, considere el programa que se muestra a continuación.
Este programa concatena los argumentos dados en la línea de comando e imprime la cadena resultante:
/ * * Archivo: hello.c * / #include #incluir int main (argc, argv) int argc; char * argv []; {int i; char str [16]; str [0] = '\ 0'; para (i = 0; i
Si compila y ejecuta este programa con su compilador normal, probablemente no verá nada interesante. Por ejemplo:
c: \ fuente> cc -o hola hola.cc:\ fuente> hola Ingresaste: hola c: \ fuente> hola mundo Ingresaste: hola mundo c: \ fuente> hola mundo cruel Ingresaste: hola mundo cruel
Si este fuera el alcance de sus procedimientos de prueba, probablemente concluiría que este programa funciona correctamente, a pesar de que tiene un error de corrupción de memoria muy grave, simplemente no se manifestó produciendo una salida incorrecta. Esto es común con los problemas de memoria: a menudo pueden pasar desapercibidos porque es posible que no afecten la salida directamente y, por lo tanto, no serán detectados por pruebas unitarias normales o pruebas funcionales.
Este tipo de error parece bastante simple cuando se encuentra en un pequeño programa de ejemplo donde no se pasará por alto, pero cuando se oculta dentro de un código complicado con cientos de miles de líneas y mucha asignación dinámica, puede evitar fácilmente la detección hasta después del lanzamiento.
Detección y visualización de errores en tiempo de ejecución y memoria con Parasoft Insure ++
Detectar errores de memoria
La mejor manera de abordar la búsqueda de defectos de memoria complejos es utilizar un herramienta de detección de errores de memoria (o "depurador en tiempo de ejecución"). Es fácil de usar: simplemente reemplace el nombre de su compilador (cc) con "asegurar", por lo que es decir
cc -o hola hola c
se convierte en
asegurar -o hola hola.c
y luego simplemente ejecuta el programa. Si tiene un archivo MAKE bien formateado, puede usar Parasoft Insure ++ configurando el comando del compilador para asegurar:
hacer CC = asegurar hola
Una vez que haya compilado con el depurador en tiempo de ejecución, puede ejecutar el comando:
hola mundo cruel
y generará los errores que se muestran a continuación, porque la cadena que se está concatenando es más larga que los 16 caracteres asignados en la declaración en la línea 11:
[hola.c: 14] ** WRITE_OVERFLOW ** >> strcat (str, argv [i]); La escritura desborda la memoria: bbbbbbbbbbbbbbbbbbbbbbbbbb | 1 | 16 | wwwwwwwwwwwwwwwwwwwwwwwwwwww Escritura (w): 2xbfffeed0 a 0xbfffeee0 (1 bytes) Para bloquear (b): 18xbfffeed0 a 0xbfffeedf (0 bytes) str, declarado en hello.c, 16 Seguimiento de pila donde ocurrió el error: strcat () (interfaz) principal ( ) hola.c, 11 ** Memoria dañada. El programa puede fallar !! ** [hola.c: 14] ** READ_OVERFLOW ** >> printf ("Ingresaste:% s \ n", str); La cadena no es nula terminada dentro del rango: str Lectura: 17xbfffeed0 Desde el bloque: 0xbfffeed0 a 0xbfffeedf (0 bytes) str, declarado en hello.c, 16 Seguimiento de la pila donde ocurrió el error: main () hello.c, 11 Ingresaste: hola Mundo cruel
Probablemente haya notado algo interesante en la salida, a saber, que en realidad hay dos errores derivados de este problema: uno es el desbordamiento de escritura cuando intenta colocar demasiados bytes en el búfer de cadena, y luego un desbordamiento de lectura cuando lee de la cadena buffer. Como puede ver, el error puede manifestarse de diferentes maneras en diferentes lugares, así que imagine lo que puede suceder dentro de un programa real. Es casi axiomático que todos los programas C y C ++ en funcionamiento tengan pérdidas de memoria y otros errores de memoria.
Si desea encontrar estos errores sin pasar semanas persiguiendo problemas oscuros, eche un vistazo a Parasoft Insure ++. Puede encontrar todos los problemas relacionados con sobrescribir la memoria o leer más allá de los límites legales de un objeto, independientemente de si está asignado estáticamente (es decir, una variable global), localmente en la pila, dinámicamente (con malloc o new), o incluso como un bloque de memoria compartida. Incluso puede detectar situaciones en las que un puntero cruza de un bloque de memoria a otro y comienza a sobrescribir la memoria allí, incluso si los bloques de memoria son adyacentes. La detección de errores en tiempo de ejecución con Insure ++ fortalecerá su aplicación y lo mantendrá alejado de esas sesiones de depuración de toda la noche.