AddressSanitizer(ASan)는 C/C++ 용 메모리 버그를 감지하는 컴파일러 기반 도구이다. AddressSanitizer는 프로그램을 실행하는 동안 메모리 오류를 식별하여 버그를 조기에 발견하고 수정할 수 있도록 도와준다. 이 글에서는 AddressSanitizer 사용법에 대해 알아본다.
ASan 장점
- 조기 감지: ASan은 프로그램을 실행하는 동안 메모리 오류를 식별하여 버그를 조기에 발견하고 수정할 수 있도록 도와준다.
- 자세한 보고서: ASan은 발생한 오류에 대한 자세한 보고서를 제공하여 개발자가 문제를 신속하게 진단하고 해결할 수 있도록 도와준다.
- 낮은 오버헤드: ASan은 강력한 검사를 수행하지만 실행 시간 성능에 미치는 오버헤드가 낮아 실제 환경에서 사용하기에 비교적 적합하다.
감지하는 메모리 오류
AddressSanitizer가 인지하는 메모리 오류는 다음과 같다.
- 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
동작 방식
ASan은 프로그램을 컴파일할 때 코드에 추가적인 보조 코드를 삽입하여 메모리 접근을 검사한다. 다음은 ASan의 동작 방식이다.
- 메모리 주입: ASan은 컴파일 중에 프로그램의 코드에 메모리 접근을 검사하는 추가적인 코드를 삽입한다.
- 섀도 메모리: ASan은 메모리 상태를 추적하는 별도의 섀도 메모리를 사용한다. 섀도 메모리의 각 바이트는 프로그램 메모리의 바이트와 대응되며 해당 바이트의 메타데이터를 포함한다.
- 런타임 검사: 프로그램이 메모리를 읽거나 쓸 때 ASan은 해당 접근을 가로채고 섀도 메모리를 확인하여 접근이 유효한지 확인한다.
- 오류 보고: ASan은 메모리 오류가 발생할 때 오류 유형과 메모리 주소, 스택 추적 등 자세한 정보를 보고(report)한다.
ASan 사용법
ASan을 사용하려면 코드를 컴파일할 때 -fsanitize=address
플래그를 사용하여 ASan을 활성화해야 한다. 예를 들어:
gcc -o my_program -fsanitize=address my_program.c
컴파일된 이후에는 프로그램을 실행하면 ASan이 메모리 오류를 검출하고 자세한 보고서를 출력한다.
Use after free 예제:
다음은 free 이후에 액세스 하는 예제이다.
#include <stdlib.h>
int main() {
char *x = (char*)malloc(10 * sizeof(char*));
free(x);
return x[5];
}
ASan 적용을 위해 컴파일 옵션으로 -fsanitize=address
를 주고, 스택 트레이스를 위해 -fno-omit-frame-pointer
역시 준다.
$ gcc use-after-free.c -fsanitize=address -fno-omit-frame-pointer
실행 결과:
실행 결과는 heap-use-after-free 를 detect 한 내용을 보여준다.

GDB 사용
일반적으로 GDB를 사용하여 ASan에서 빌드한 바이너리를 디버깅할 수 있다. ASan 버그를 발견하면 발견한 __asan_report_{load,store}{1,2,4,8,16}
함수 중 하나를 호출하여 __asan::ReportGenericError
가 호출되도록 한다.
만약 ASan가 에러를 리포트하기 전에 stop을 원한다면, __asan::ReportGenericError
에 breakpoint를 설정한다.
그렇지 않고, 에러 리포트 후에 stop을 원한다면, __sanitizer::Die
에 breakpoint를 설정하던가, 바이너리 실행시에 ASAN_OPTIONS=abort_on_error=1
를 환경설정하여 실행한다.
gdb 내에서 메모리 위치에 대해 설명하도록 요청할 수 있는데, 다음과 같다.
(gdb) set overload-resolution off
(gdb) p __asan_describe_address(0x7ffff73c3f80)
0x7ffff73c3f80 is located 0 bytes inside of 10-byte region [0x7ffff73c3f80,0x7ffff73c3f8a)
freed by thread T0 here:
...