ASAN을 통해 out-of-bound 와 use-after-free 메모리 버그를 찾아낼 수 있다.
ASAN(Address Sanitizer)는 카이스트 전산학부 2학년들에게 가장 가르쳐주고 싶은 툴 Top 10 안에 들 정도로 굉장히 유용한 툴이라고 생각한다.
이 글은 AddressSanitizer: A Fast Address Sanity Checker 논문에 대한 설명이다. 혹시 툴의 사용법이 궁금한 사람들은 다른 블로그로 가보길 바란다.
64-bit System에서는 모든 메모리 접근 주소(Read/Write)가 8 Bytes에 대해 align되어 있다.
예를 들어, Virtual Address 0x10001에 있는 값을 읽으려고 한다면 0x10000주소부터 8 Bytes을 읽는 방법밖에 없다.
이렇게 8 Bytes으로 정렬되어 있는 이유는 CPU Register의 크기가 64 bits=8 Bytes이기 때문이다. 프로그램에서 메모리 값을 직접 사용하지 않고(예외: DMA), 항상 메모리 값을 Register에 저장한 뒤에 사용한다.
메모리 관련된 디버깅을 하기 위해서는 메모리의 allocation/deallocation에 대한 meta data를 저장할 필요가 있다.
예를 들어, 내가 1234Bytes의 메모리를 Write한 뒤, 같은 주소에서 1235 Bytes의 메모리를 Read하려고 한다. 앞서 말했듯이, 모든 memory access는 8 Bytes 정렬이 되어있기 때문에 에러를 발생시키지 않을 수 있다. 하지만 만약 우리가 1234 Bytes을 Write했다는 것을 어딘가에 적어놓았다면 1235 Bytes을 Read하려고 할 때 out-of-bounds 에러가 발생했다는 것을 알아낼 수 있다.
결국 shadow memory는 메모리를 사용하고, 많은 툴에서 심각한 memory overhead을 발생시키기도 한다.
Address Sanitizer에서는 addressable byte 갯수를 shadow memory에 저장한다.

8 Bytes의 Application Memory에 대해 1Bytes의 shadow memory를 사용한다. 그리고 shadow memory 주소는 (Virtual_Address >> 3) + Offset와 같은 형태로 변환되기 때문에 따로 mapping을 저장할 필요가 없다.
calloc이 발생하면, shadow memory에 값이 쓰인다. 그리고 read/write가 발생할때마다 read_size 혹은 write_size가 shadow_memory에 저장된 addressable_size에 위배되지 않는지 검사한다.
3 Bytes을 calloc 한 뒤, 5 Bytes을 Write하려고 한다면 metadata를 읽었을 때, write_size=5로 3(calloc_size)보다 크기 때문에 Address Sanitizer가 프로그램을 Crash시킨다.
memory chunk를 free시키게 되면 metadata상에는 0이 저장된다. 그 chunk에 대해 1Bytes을 Read하려고 한다면, read_size=1로 0(metadata) 보다 크기 때문에 Address Sanitizer가 프로그램을 Crash시킨다.
이처럼 Address Sanitizer는 프로그램의 memory address을 fragile하게 바꿈으로써 메모리 버그를 감지한다. 많은 CVE들이 Address Sanitizer + Fuzzer를 통해 발견되었다. 메모리 버그를 찾아보려고 한다면 gdb도 좋지만, Address Sanitizer을 사용하는 것을 추천한다!!