p1-stats

Address Sanitizer

This tutorial will compile your code with an Address Sanitizer enabled. The Address Sanitizer is very good at finding memory errors, including going off the end of an array or vector.

Table of Contents

If you’re interested, here is more information about the Address Sanitizer, and a comparison of Address Sanitizer vs Valgrind.

Prerequisites

We’re assuming that you already have a folder with starter source code in it, e.g., p1-stats/.

$ pwd
/Users/awdeorio/src/eecs280/p1-stats
$ ls
Makefile      main_test.out.correct  p1_library.h  stats_public_test.cpp
main.cpp      main_test_data.tsv     stats.cpp     stats_tests.cpp
main_test.in  p1_library.cpp         stats.h

You can connect to CAEN Linux. See the CAEN Linux tutorial for help.

Example without Address Sanitizer

Let’s do an example without an address sanitizer. We’ll make a mistake on purpose in main.cpp, going off the end of a vector.

//main.cpp
#include "stats.h"
#include "p1_library.h"
#include <iostream>
#include <vector>
using namespace std;

int main() {
  cout << "hello from main!\n";
  vector<int> v={0, 0};  // v contains 2 items
  v.pop_back();          // v contains 1 item
  cout << v[1] << "\n";  // off the end of v
}

Copy your code to CAEN Linux and then SSH to CAEN Linux.

$ rsync -rtv --exclude '.git*' ./ awdeorio@login.engin.umich.edu:p1-stats-copy/
$ ssh awdeorio@login.engin.umich.edu
Success. Logging you in...

Clean, build and run. Notice that the output of v[1] is 0, however, this operation is undefined because it’s off the end of the vector!

$ make clean
rm -rvf *.exe *~ *.out *.dSYM *.stackdump
$ make main.exe
g++ -Wall -Werror -pedantic -g --std=c++11 main.cpp stats.cpp p1_library.cpp -o main.exe
$ ./main.exe 
hello from main!
0

Try to find the error with Valgrind. It doesn’t find the error.

$ valgrind ./main.exe 
==28736== Memcheck, a memory error detector
==28736== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28736== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==28736== Command: ./main.exe
==28736== 
hello from main!
0
==28736== 
==28736== HEAP SUMMARY:
==28736==     in use at exit: 0 bytes in 0 blocks
==28736==   total heap usage: 1 allocs, 1 frees, 8 bytes allocated
==28736== 
==28736== All heap blocks were freed -- no leaks are possible
==28736== 
==28736== For counts of detected and suppressed errors, rerun with: -v
==28736== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Visual Studio on Windows

Visual Studio provides an address sanitizer with bounds checking automatically with every debug build. You don’t need to do anything different!

Xcode or VS Code on macOS

The default compiler on macOS is Clang. Verify that you’re using Clang. Your version might be different.

$ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 9.0.0 (clang-900.0.39.2)
Target: x86_64-apple-darwin16.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

Before we build, we need to modify the compiler flags. Edit your Makefile and change the compiler flags to add -fsanitize=address.

# Compiler flags
CXXFLAGS = --std=c++11 -Wall -Werror -pedantic -g -fsanitize=address

Build.

$ make main.exe
g++ --std=c++11 -Wall -Werror -pedantic -g -fsanitize=address main.cpp stats.cpp p1_library.cpp -o main.exe

Run. The sanitizer detects the out-of-bounds problem and prints a stack trace.

$ ./main.exe
=================================================================
==95600==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000000f4 at pc 0x00010c799eca bp 0x7fff53466cd0 sp 0x7fff53466cc8
READ of size 4 at 0x6020000000f4 thread T0
    #0 0x10c799ec9 in main main.cpp:12
    #1 0x7fffdcd52234 in start (libdyld.dylib:x86_64+0x5234)

0x6020000000f4 is located 4 bytes inside of 8-byte region [0x6020000000f0,0x6020000000f8)
allocated by thread T0 here:
    #0 0x10c8420ab in wrap__Znwm (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x640ab)
    #1 0x10c79c319 in std::__1::vector<int, std::__1::allocator<int> >::allocate(unsigned long) vector:925
    #2 0x10c799590 in main main.cpp:10
    #3 0x7fffdcd52234 in start (libdyld.dylib:x86_64+0x5234)

SUMMARY: AddressSanitizer: heap-buffer-overflow main.cpp:12 in main
Shadow bytes around the buggy address:
  0x1c03ffffffc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1c03ffffffd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1c03ffffffe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1c03fffffff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1c0400000000: fa fa fd fd fa fa fd fd fa fa 00 00 fa fa 00 06
=>0x1c0400000010: fa fa 00 04 fa fa 00 00 fa fa 00 06 fa fa[04]fa
  0x1c0400000020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400000030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400000040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400000050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400000060: 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
==95600==ABORTING
hello from main!

Any system with GNU GCC

Any Linux-based system with GNU GCC (g++) will work with these instructions. That includes Linux, CAEN Linux, and the Windows Subsystem for Linux (WSL).

Verify that you’re using GNU GCC. Your version might be different.

$ g++ --version
g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Before we build, we need to modify the compiler flags. Edit your Makefile and change the compiler flags to add -D_GLIBCXX_DEBUG.

# Compiler flags
CXXFLAGS = --std=c++11 -Wall -Werror -pedantic -g -D_GLIBCXX_DEBUG

Build.

$ make main.exe
g++ --std=c++11 -Wall -Werror -pedantic -g -D_GLIBCXX_DEBUG main.cpp stats.cpp p1_library.cpp -o main.exe

Run. Notice the helpful error message.

$ ./main.exe 
hello from main!
/usr/um/gcc-7.1.0/include/c++/7.1.0/debug/vector:417:
Error: attempt to subscript container with out-of-bounds index 1, but 
container only holds 1 elements.

Objects involved in the operation:
    sequence "this" @ 0x0xff93f460 {
      type = std::__debug::vector<int, std::allocator<int> >;
    }
Aborted

Debug using Visual Studio Code or GDB.

Next steps

Return to the main set up tutorial.