X
BLOG

Cómo encontrar el abuso de puntero en C

Cómo encontrar el abuso de puntero en C Tiempo de leer: 3 minutos

Los punteros son la fuerza y ​​el talón de Aquiles de la programación en C y C ++. Si bien los punteros le permiten ser muy creativo y flexible en la forma en que aborda la solución de un problema en particular, es muy fácil introducir defectos en su código sin saberlo. Los problemas con los punteros se encuentran entre los más difíciles que encuentran los programadores de C. Hace una generación, las técnicas de fuerza bruta, como insertar sentencias de impresión en el código, solían ser la mejor estrategia para tratar de encontrar estos problemas.

Hoy en día, herramientas de detección de errores de memoria como Asegurar ++ puede detectar problemas relacionados con el puntero automáticamente a medida que se detecta el código, lo que ahorra mucho tiempo y dolor de cabeza. Insure ++ encuentra problemas en las siguientes categorías:

  • Operaciones en punteros NULL. Operaciones en punteros no inicializados.
  • Operaciones sobre punteros que en realidad no apuntan a datos válidos.
  • Operaciones que intentan comparar o relacionar punteros que no apuntan al mismo objeto de datos.
  • Llamadas a funciones a través de punteros de función que en realidad no apuntan a funciones.
  • Muchas otras causas de posible comportamiento indefinido o comportamiento definido por implementación.

Insure ++ utiliza un analizador de código de última generación, junto con cientos de heurísticas, para analizar el código de la aplicación, durante el cual informa sobre varias posibles violaciones estáticas. Mientras analiza el código, escribe un nuevo archivo de código fuente, con la instrumentación adecuada insertada en "puntos problemáticos" (por ejemplo, desreferencia del puntero, salida del alcance, etc.), y el archivo fuente resultante se compila automáticamente y todos los archivos de código objeto resultantes se vinculan en un nuevo programa ejecutable.

Ejemplo

A continuación se muestra el código de un programa "Hola mundo" que utiliza la asignación de memoria dinámica:

    
/ * * Archivo: hello.c * / #include #incluir int main (int argc, char * argv []) {char * string, * string_so_far; int i, longitud; longitud = 0; para (i = 0; i
    

La idea básica de este programa es que hacemos un seguimiento del tamaño de la cadena actual en la longitud variable. A medida que se procesa cada nuevo argumento, agregamos su longitud a la variable de longitud y asignamos un bloque de memoria del nuevo tamaño. Observe que el código tiene cuidado de incluir el carácter NULL final al calcular la longitud de la cadena (línea 14) y también el espacio entre las cadenas. Ambos son errores fáciles de cometer. Es un ejercicio interesante ver qué tan rápido puede encontrar un error de este tipo con una herramienta de detección de errores de memoria como Parasoft Insure ++.

El código copia el argumento en el búfer o lo agrega, dependiendo de si esta es la primera pasada del ciclo o no. Finalmente, el puntero string_so_far apunta a la nueva cadena más larga.

Si compila y ejecuta este programa bajo Insure ++, verá errores de "puntero no inicializado" reportados para el código "strcpy (string, string_so_far)". Esto se debe a que la variable string_so_far no se ha establecido en nada antes del primer viaje a través del bucle de argumentos. En una pequeña muestra de código como esta, este problema es obvio, pero incluso si el error está oculto en un montón de cientos de miles de líneas de código y es mucho más sutil que el error anterior, Insure ++ lo encontrará cada vez.

Insure ++ Reports

Insure ++ informa cualquier problema encontrado. Los informes de Insure ++ incluyen información detallada, que incluye: sobre el tipo de error, el archivo fuente y el número de línea, el contenido real de la línea del código fuente, la expresión que causó el problema, con informes que incluyen:

  • El tipo de error (por ejemplo, EXPR_UNRELATED_PTRCMP)
  • El archivo de origen y el número de línea (por ejemplo, foo.cc:42)
  • El contenido real de la línea del código fuente, (por ejemplo, "while (p <g) {")
  • La expresión que causó el problema (por ejemplo, "p <g")
  • Información sobre todos los punteros y bloques de memoria involucrados en el error:
    • Los valores del puntero
    • Los bloques de memoria apuntados (si los hay) y cualquier desplazamiento
    • La información de asignación de bloques:
      • Seguimiento de pila si se asigna dinámicamente.
      • Ubicación de la declaración de bloque, (archivo de origen y número de línea), si se asigna en la pila o globalmente.
      • Seguimiento de pila de la desasignación del bloque, si corresponde.
    • El seguimiento de la pila que muestra cómo llegó el programa a la ubicación del error.

Para probar Parasoft Insure ++, visite https://www.parasoft.com/products/parasoft-insure/insure-request-a-demo/.

Fotografía: oskay

Escrito por

Arthur Hicken

Arthur ha estado involucrado en seguridad de software y automatización de pruebas en Parasoft durante más de 25 años, ayudando a investigar nuevos métodos y técnicas (incluidas 5 patentes) mientras ayuda a los clientes a mejorar sus prácticas de software.

Reciba las últimas noticias y recursos sobre pruebas de software en su bandeja de entrada.

Prueba Parasoft