Notas introdutórias à ferramenta Address Sanitizer
A Address Sanitizer, ASan, é uma ferramenta que permite identificar erros de memória em tempo de execução. Em outras palavras, quando executamos nossos programa e ocorre algum erro de memória: a ASan acusa a ocorrência do problema, bem como interrompendo a sua execução.
Segundo o descrito no paper "AddressSanitizer: A Fast Address Sanity Checker", ela é:
a new memory error detector. Our tool finds out-of-bounds accesses to heap, stack, and global objects, as well as use-after-free bugs. It employs a specialized memory allocator and code instrumentation that is simple enough to be implemented in any compiler, binary translation system, or even in hardware.
Por meio dela é possível identificar os seguintes erros:
Use after free (dangling pointer dereference)
Heap buffer overflow
Stack buffer overflow
Global buffer overflow
Use after return
Use after scope
Initialization order bugs
Memory leaks
Ela já vem disponível nos dois compiladores mais utilizados GCC e CLANG. Sua utilização é por meio de flags definidas durante a compilação. Assim, a ASan insere no código da aplicação, que está sendo compilada, mecanismos que permite "identificar diversos problemas de acesso à memória, incluindo o uso de memória desalocada, estouros de buffer no stack e no heap, acesso à memória não inicializada e vazamentos de memória" (PRADO, 2018).
A sua forma de atuação pode ser consultada na documentação. Mas, basicamente, a ASan atua trocando as chamadas malloc/free
do código pelas suas próprias versões de gerenciadores de acesso/uso de memória.
A utilização dessa ferramenta é fundamental para identificar problemas/erros escondidos. Por que escondidos? Pois, pode ocorrer que o programa seja executado em sua totalidade com aparente estado de normalidade, todavia, sem que seja disparado algum erro ou interrompido a sua execução. Sendo que, há alguns erros que podem estarem ocorrendo e que terão consequências em outras execuções ou exporem o software à alguma vulnerabilidade.
Deve-se ressaltar que o seu uso provoca uma sobrecarga na execução e processamento da aplicação. Em razão disso, é recomenddo a sua utilização apenas em versões de desenvolvimento e testes.
Uso
Para habilitar o ASan, adicione a seguinte flag quando estiver compilando o progama.
-fsanitize=address (clang)
gcc app.c -fsanitize=address -o app
Tem outras flgas que podem serem consultadas na documentação.
Exemplos de usos
Erro Stack buffer overflow
"Buffer Overflow, também chamado de Buffer Overrun, é uma anomalia em um programa em que, ao escrever dados em um buffer (armazenamento temporário de dados), a escrita ultrapassa o tamanho do buffer e começa a escrever nos espaços adjacentes."
Veja-se o exemplo abaixo:
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[2];
str[0] = 'O';
str[1] = 'l';
str[2] = 'a';
str[3] = '\0';
printf("%s\n", str);
return (0);
}
Quando compilado e executado, não é apresentado qualquer erro;
> gcc test.c -o test
> ./test
> Ola
Agora, quando compilado com a flag -fsanitize=address
e executado, a ASan entra em ação para apresentar os erros encontrados e interromper a respectiva execução.
> gcc test.c -fsanitize=address -o test
> ./test
=================================================================
==389==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe948a7662 at pc 0x55d86850528f bp 0x7ffe948a7630 sp 0x7ffe948a7628
WRITE of size 1 at 0x7ffe948a7662 thread T0
#0 0x55d86850528e in main (/home/senhor/School42/ft_printf/test+0x128e)
#1 0x7f06f5c9cd09 in __libc_start_main ../csu/libc-start.c:308
#2 0x55d8685050b9 in _start (/home/senhor/School42/ft_printf/test+0x10b9)
Address 0x7ffe948a7662 is located in stack of thread T0 at offset 34 in frame
#0 0x55d868505184 in main (/home/senhor/School42/ft_printf/test+0x1184)
This frame has 1 object(s):
[32, 34) 'str' (line 6) <== Memory access at offset 34 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/senhor/School42/ft_printf/test+0x128e) in main
Shadow bytes around the buggy address:
0x10005290ce70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005290ce80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005290ce90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005290cea0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005290ceb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10005290cec0: 00 00 00 00 00 00 00 00 f1 f1 f1 f1[02]f3 f3 f3
0x10005290ced0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005290cee0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005290cef0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005290cf00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005290cf10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==389==ABORTING
Erro Memory leaks
Outro exemplo é com Memory leaks. Memory leaks ou vazamento de memória, é segundo a Wikipédia, "um fenômeno que ocorre quando um programa de computador gerencia incorretamente alocações de memória de maneira que certa memória não é liberada quando não é mais necessária".
Veja-se o exemplo abaixo:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char *buffer;
buffer = malloc(1024);
sprintf(buffer, "%s", argv[1]);
printf("%s\n", buffer);
return (0);
}
Quando compilado e executado, novamente, não é apresentado qualquer erro;
> gcc test.c -o test
> ./test hello
> hello
Agora, quando compilado com a flag -fsanitize=address
e executado, a ASan entra em ação para apresentar os erros encontrados.
> gcc test.c -fsanitize=address -o test
> ./test hello hello ================================================================= ==822==ERROR: LeakSanitizer: detected memory leaks Direct leak of 1024 byte(s) in 1 object(s) allocated from: #0 0x7f5f09215e8f in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145 #1 0x5624c33ff1bd in main (/home/senhor/School42/ft_printf/test+0x11bd) #2 0x7f5f08fbbd09 in __libc_start_main ../csu/libc-start.c:308 SUMMARY: AddressSanitizer: 1024 byte(s) leaked in 1 allocation(s).
Referências:
https://www.usenix.org/system/files/conference/atc12/atc12-final39.pdf
https://sergioprado.org/identificando-problemas-de-acesso-memoria-com-o-addresssanitizer/
https://girishjoshi.io/post/using-address-sanitizer-with-gcc/
https://developer.apple.com/documentation/xcode/diagnosing-memory-thread-and-crash-issues-early
https://gitbook.ganeshicmc.com/pwning/buffer-overflow-e-a-stack
Subscribe to my newsletter
Read articles from Plínio Hávila directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Plínio Hávila
Plínio Hávila
Programador Entusiasta