C++ Compiler Sanitizers

“Sanitizers” is a project by Google (Sanitizers github project) where the compiler instruments your code to signal various errors. Most prominently (in my opinion) are the sanitizers for memory addressing and leakage errors, which are described below. Other sanitziers can detect undefined behavior and race conditions.

Address Sanitizer Usage

Just build the code with extra compiler (and linker) flag -fsanitize=address:

g++ -fsanitize=address -Og -ggdb -fno-omit-frame-pointer source.cpp -o my_executable

then run ./my_executable. The flags -Og, -ggdb, and -fno-omit-frame-pointer are not necessary but should improve the debug output of the sanitizers (e.g. code files and line numbers).

Now, executing the code will terminate on addressing errors with debug information being printed. This happens on many errors that would go unnoticed otherwise.

Usage with CMake

For CMake, make sure to include the fsanitizer flag to compiler and linker. In CMakeLists.txt, this can be done as follows:

target_compile_options(<targetname> PRIVATE -fsanitize=address)
target_link_libraries (<targetname> PRIVATE -fsanitize=address)

Performance

The project page of the address sanitizer says

This tool is very fast. The average slowdown of the instrumented program is ~2x (see AddressSanitizerPerformanceNumberspage).

Useful Environment Variable Options

I like to have a core dump on crash, so I set

ASAN_OPTIONS="disable_coredump=0:unmap_shadow_on_exit=1:abort_on_error=1"

The cryptic second option avoids dumping the sanitizers house-keeping data, which would increase the size of the core dump.

Examples

asan.cpp

 1  #include <iostream>
 2  ///Built: g++ -g -fno-omit-frame-pointer -fsanitize=address asan.cpp -o asan
 3  
 4  int main(int argc, char* argv[]) {
 5    if(argc != 2) {
 6      std::cout << "Usage: " << argv[0] << "<flag>\n";
 7      std::cout << "where <flag> is one of\n";
 9      std::cout << "'s' to trigger Stack Sanitizer\n";
 9      std::cout << "'h' to trigger Heap  Sanitizer\n";
10      std::cout << "'a' to trigger Address Sanitizer on SEGV\n";
11      std::cout << "'l' to trigger Leak  Sanitizer (default)\n";
12    }
13    else {
14      char my_stack_array[10];
15      char* my_heap_array = new char[10];//triggers leak sanitizer
16  
17      switch(argv[1][0]){
18        //triggers stack-buffer-overflow
19        case 's': std::cerr << "Stack: " << my_stack_array[12] << "\n"; break;
20        //triggers heap-buffer-overflow
21        case 'h': std::cerr << "Heap:  " << my_heap_array[12] << "\n"; break;
22        //Triggers SEGV, sanitizer provides stack trace
23        case 'a': std::cerr << "Param 900:" << argv[900] << "\n"; break;
24        //Leak sanitizer is triggered automatically on leaving main
25        case 'l':
26        default: break;
27      }
28    }
29    return 0;
30  }
Leaking memory on heap (line 15):
$ ./asan l
=================================================================
==15659==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 10 byte(s) in 1 object(s) allocated from:
    #0 0x7f9d77ae9608 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe0608)
    #1 0x55804c0d7336 in main /home/user/code/asan.cpp:15
    #2 0x7f9d772b0b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)

SUMMARY: AddressSanitizer: 10 byte(s) leaked in 1 allocation(s).
Accessing heap array beyond end:
$ ./asan h
Heap:  =================================================================
==15665==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000001c at pc 0x556be9267649 bp 0x7ffe8ab6c690 sp 0x7ffe8ab6c680
READ of size 1 at 0x60200000001c thread T0
    #0 0x556be9267648 in main /home/user/code/asan.cpp:21
    #1 0x7f0d62ed9b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #2 0x556be9267139 in _start (/home/user/code/asan+0x1139)

0x60200000001c is located 2 bytes to the right of 10-byte region [0x602000000010,0x60200000001a)
allocated by thread T0 here:
    #0 0x7f0d63712608 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe0608)
    #1 0x556be9267336 in main /home/user/code/asan.cpp:15
    #2 0x7f0d62ed9b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/user/code/asan.cpp:21 in main
Shadow bytes around the buggy address:
  0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa 00[02]fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
==15665==ABORTING
Accessing stack array beyond end:
$ ./asan s
Stack: =================================================================
==15718==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcd2bd5c6c at pc 0x55d9eda9d63f bp 0x7ffcd2bd5ba0 sp 0x7ffcd2bd5b90
READ of size 1 at 0x7ffcd2bd5c6c thread T0
    #0 0x55d9eda9d63e in main /home/user/code/asan.cpp:19
    #1 0x7f1ea1e41b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #2 0x55d9eda9d139 in _start (/home/user/code/asan+0x1139)

Address 0x7ffcd2bd5c6c is located in stack of thread T0 at offset 172 in frame
    #0 0x55d9eda9d283 in main /home/user/code/asan.cpp:4

  This frame has 3 object(s):
    [32, 33) '__c'
    [96, 97) '__c'
    [160, 170) 'my_stack_array' <== Memory access at offset 172 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/user/code/asan.cpp:19 in main
Shadow bytes around the buggy address:
  0x10001a572b30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001a572b40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001a572b50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001a572b60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001a572b70: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 01 f2 f2 f2
=>0x10001a572b80: f2 f2 f2 f2 01 f2 f2 f2 f2 f2 f2 f2 00[02]f2 f2
  0x10001a572b90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001a572ba0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001a572bb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001a572bc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001a572bd0: 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
==15718==ABORTING
Accessing parameter array (which is usually stored on the stack) grossly beyond end:
Param 900:ASAN:DEADLYSIGNAL
=================================================================
==17283==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f27e05785c7 bp 0x7ffd69376660 sp 0x7ffd69375dd8 T0)
==17283==The signal is caused by a READ memory access.
==17283==Hint: address points to the zero page.
    #0 0x7f27e05785c6  (/lib/x86_64-linux-gnu/libc.so.6+0x18e5c6)
    #1 0x7f27e0bb557b  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x5157b)
    #2 0x7f27e08eeee8 in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x113ee8)
    #3 0x563735f1b3d6 in main /home/uic13796/Documents/CoP/memory-presentation/asan.cpp:23
    #4 0x7f27e040bb96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #5 0x563735f1b139 in _start (/home/uic13796/Documents/CoP/memory-presentation/asan+0x1139)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/lib/x86_64-linux-gnu/libc.so.6+0x18e5c6) 
==17283==ABORTING


Home