- Use after free (dangling pointer dereference)
- buffer overflow
- Stack buffer overflow
- Global buffer overflow
- Use after return
- Initialization order bugs
Also it's much faster than valgrind :-) Unfortunately it's not as easy to use as valgrind since it requires a rebuild of your code.
The basic instructions to use ASAN are:
Use the -fsanitize=address switchtogether with the suggestion of
To get nicer stack traces in error messages add -fno-omit-frame-pointer.
As a simple example let's see what happens when we run
int main(int, char **) { int a[3]; a[3] = 4; return 0; }compiled with
g++ -fno-omit-frame-pointer -fsanitize=address main.cpp
The result:
==8403== ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffbe7c7d8c at pc 0x400779 bp 0x7fffbe7c7d40 sp 0x7fffbe7c7d38 WRITE of size 4 at 0x7fffbe7c7d8c thread T0 #0 0x400778 (/home/tsdgeos_work/test/a.out+0x400778) #1 0x7f2f5cfa6ec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4) #2 0x400638 (/home/tsdgeos_work/test/a.out+0x400638) Address 0x7fffbe7c7d8c is located at offset 44 in frameof T0's stack: This frame has 1 object(s): [32, 44) 'a'
In this case it's pretty obvious what's wrong with just looking at the code and ASAN is even telling us it is a bad write just after the 'a' object, but note that we are not getting the line number of where the error happens. You'll say, that's why you didn't use the -g switch! So let's see what do we get after compiling with
g++ -g3 -fno-omit-frame-pointer -fsanitize=address main.cpp
The result:
==8467== ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff59ccaa2c at pc 0x400779 bp 0x7fff59cca9e0 sp 0x7fff59cca9d8 WRITE of size 4 at 0x7fff59ccaa2c thread T0 #0 0x400778 (/home/tsdgeos_work/test/a.out+0x400778) #1 0x7f6f75601ec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4) #2 0x400638 (/home/tsdgeos_work/test/a.out+0x400638) Address 0x7fff59ccaa2c is located at offset 44 in frameof T0's stack: This frame has 1 object(s): [32, 44) 'a'
Same thing :-/
Here is where people may give up or think to switch to clang and try there, but since clang is sometimes more picky with the code (not bad per se) it may be a lot of work to get your code to compile under clang, if you keep digging you'll read some news about GCC not having a ASAN symbolizer [yet], but clang has one and you can still use it with GCC, so if you
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.4 export ASAN_OPTIONS=symbolize=1and then run the binary again (the one we just compiled with -g3)
The result:
WRITE of size 4 at 0x7fff4f5f0ebc thread T0 #0 0x400778 in main /home/tsdgeos_work/test/main.cpp:5 #1 0x7f3d4dce2ec4 (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4) #2 0x400638 in _start (/home/tsdgeos_work/test/a.out+0x400638) Address 0x7fff4f5f0ebc is located at offset 44 in frameof T0's stack: This frame has 1 object(s): [32, 44) 'a'
And now we have line numbers :-)
Hopefully this is fixed in GCC 4.9, which uses libbacktrace as a replacement for llvm-symbolizer: http://gcc.gnu.org/ml/gcc-patches/2013-11/msg02055.html
ReplyDeleteso there is no need to use an external program. You probably need to use '-g', since libbacktrace relies on debug info, but
export ASAN_OPTIONS=symbolize=1
should be the default in GCC 4.9.
yeeha!!!
ReplyDeleteThank you! This was really helpful, I was about to move to Clang :D
ReplyDeleteAmazing! Thank you very much.
ReplyDelete