En entregas anteriores hemos hablado de opciones de GCC que hacen nuestros ejecutables más seguros, en esta entrega en cambio, presentamos opciones de compilación que generan informes, avisando de errores en el código.
AddressSanitizer (ASan) es una opción para Clang (>=3.1) y GCC (>=4.8) que detecta errores de corrupción de memoria. ASan ha sido desarrollado por Google y fue presentado en la conferencia Usenix ATC en 2012. ASan está formado por dos módulos: (a) un módulo de instrumentación de binarios en tiempo de compilación, que es el encargado de detectar si cada byte de la memoria de la aplicación es seguro de acceder mediante el uso de shadow memory, y (b) una biblioteca que en tiempo de ejecución es la que maneja la shadow memory. En líneas generales, sustituye los malloc y free por su propia implementación, controlando que se accedan a bloques de memoria dentro de las regiones adecuadas, que el espacio que se libere haya sido antes reservado, no accediendo a regiones liberadas etc.
Para usar ASan con GCC tenemos que compilar nuestro programa con la opción -fsanitize=address. Como ASan añade cierta sobrecarga al programa, se recomienda compilar con las opciones 01 ó 02 para tratar de optimizar el programa, y para que los errores que nos muestre sean más descriptivos tenemos que especificar que no queremos que GCC omita guardar el frame pointer en un registro, -fno-omit-frame-pointer. Para comprobar cómo funciona ASan tomamos como ejemplo el siguiente programa que presenta un error de tipo use-after-free:
#include <stdlib.h> int main() { char *foo = (char*) malloc(2 * sizeof(char*)); free(foo); return foo[0]; }
Primero compilamos sin ASan y ejecutamos:
$ gcc -o use-after-free use-after-free.c
$ ./use-after-free
y no nos enteramos de que algo va mal con nuestro programa.
Mientras que, compilando con ASan de la siguiente manera:
$ gcc -o use-after-free-asan-no-omit -fsanitize=address -O1 -fno-omit-frame-pointer use-after-free.c
$ ./use-after-free-asan-no-omit
El compilador nos muestra que tipo de error ha encontrado, diciéndonos que hemos hecho un use-after-free en esta posición: #0 0x4007b1 in main (/[OMITTED]/use-after-free-asan-no-omit+0x4007b1). Que había sido liberada en: #1 0x400793 in main (/[OMITTED]/use-after-free-asan-no-omit+0x400793). Y anteriormente reservada en: #1 0x400788 in main (/[OMITTED]/use-after-free-asan-no-omit+0x400788).
Podemos usar addr2line para hacer corresponder las direcciones del informe (0x4007b1, 0x400793, y 0x40078) a números de línea en nuestro programa y así tener una visión más clara de dónde hemos metido la pata.
$ addr2line -e ./use-after-free-asan-no-omit 0x4007b1 […]/use-after-free.c:8
Finalmente, si tenemos LLVM instalado, los informes de ASan tanto en Clang como en GCC podrán utilizar llvm-symbolizer para trasladar directamente las direcciones a número de línea si compilamos adicionalmente con -g y avisamos a ASan de que queremos que use el symbolizer:
$ which llvm-symbolizer-3.8
/usr/bin/llvm-symbolizer-3.8
$ export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.8
$ export ASAN_OPTIONS=symbolize=1
$ gcc -o use-after-free-asan-no-omit -fsanitize=address -O1 -fno-omit-frame-pointer -g use-after-free.c
$ ./use-after-free-asan-no-omit . . . omitido . . .
#0 0x4007b1 in main /[OMITTED]/use-after-free.c:8 . . .
#1 0x400793 in main /[OMITTED]/use-after-free.c:7 . . .
#1 0x400788 in main /[OMITTED]/use-after-free.c:6 . . . omitido . . .
El ejemplo que hemos tomado, y otros muchos se encuentran en la Wiki de ASan, donde se pueden consultar más detalles sobre el funcionamiento interno de ASan, y otros sanitizers, además la presentación de ASan en Usenix está disponible en su página.
¿Es esto suficiente? Lo descubriremos en los siguientes posts.