As a C/C++ developer, you will encounter various problems during development, the most common ones are memory usage issues such as out of bounds, leakage.
Previously the most commonly used memory error detect tool was Valgrind, but the biggest problem with Valgrind is that it will greatly reduce program speed, by 10 times by estimate.
Good news is the Google-developed memory error detect tool, AddressSanitizer (aka ASan), has greatly improved the program slowdown to two times only on average, which is very fast.
This post gives an introduction to AddressSanitizer, covering what it is, how it works, feature comparison between AddressSanitizer and other memory detection tools, as well as some best practices utilizing AddressSanitizer.
Introducing AddressSanitizer
AddressSanitizer is a compiler-based testing tool that detects various memory errors in C/C++ code at runtime.
Strictly speaking, AddressSanitizer is a compiler plug-in. It consists of two modules
- A compiler instrumentation module
- A run-time library that replaces malloc/free
The instrumentation module mainly deals with memory operations such as **store **and load at the compiler level.
The dynamic library mainly provides some complex functions at runtime such as poison/unpoison shadow memory and hooks system calling functions such as malloc/free.
Basic Usage of AddressSanitizer
According to the official Wiki, AddressSanitizer finds:
- 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
In this post we only focus on the basic usage. For more details, please refer to the official compiler usage documents such as Clang.
Example on Use After Free
The following code is a very simple example of Use after free:
Now, compile the code and run the executable. Here you only need to build with the -fsanitize = address option when compiling.
clang++ -O -g -fsanitize=address ./use\_after\_free.cpp ./a.out
Finally we will see the following output:
The output clearly shows when the memory is freed and when it’s reused.
Example on Memory leaks
In the following code, the memory that p points to is not freed.
Now compile and run.
clang -fsanitize=address -g ./leak.c ./a.out
We can see the following outputs:
Note that memory leak detection will only be conducted before exiting the program , which means that if you continuously allocate memory and then free it at run time, AddressSanitizer will not detect memory leak. At this time you need JeMalloc/TCMalloc to help.
Fundamentals of AddressSanitizer
In this post we will give a brief introduction to AddressSanitizer’s fundamentals. If you are interested in knowing the details of the algorithm behind, pleased refer to AddressSanitizer: a fast address sanity checker.
AddressSanitizer replaces all your malloc and free, and then marks the before and after of the memory area that has been allocated (malloc) as poisoned (mainly to deal with overflow). And the freed memory is marked as poisoned (mainly to deal with use after free). Every memory access in your code will be translated to the following by the compiler.
Before:
\*address = ...; // or: ... = \*address;
After:
shadow\_address = MemToShadow(address); if (ShadowIsPoisoned(shadow\_address)) { ReportError(address, kAccessSize, kIsWrite); } \*address = ...; // or: ... = \*address;
First we can see a memory address translating (MemToShadow) process, followed by determining whether the accessed memory area is poisoned. If so, the tool will report an error and exit.
The reason why the translation process is here is that AddressSanitizer divides virtual memory into two parts:
- Main application memory(Mem), the memory used by the current program
- Shadow memory, a memory that holds main memory meta information. For example, those areas of main memory that are poisoned are stored in shadow memory.
Comparison of AddressSanitizer and Other Memory Detection Tools
The following figure is the comparison of AddressSanitizer and other memory detection tools:
Parameter description:
- DBI : dynamic binary instrumentation
- CTI : compile-time instrumentation
- UMR : uninitialized memory reads
- UAF : use-after-free (aka dangling pointer)
- UAR : use-after-return
- OOB : out-of-bounds
- x86 : includes 32- and 64-bit.
We can see that AddressSanitizer only slows down the program by 2 times compared to Valgrind’s 10 times.
AddressSanitizer currently supports GCC since 4.8 release and Clang since 3.1 release.
Tips on Using AddressSanitizer
export ASAN\_OPTIONS= 'abort\_on\_error=1'/
Turn on AddressSanitizer in Nebula Graph
We used AddressSanitizer in Nebula Graph to help us find a lot of problems. Enabling AddressSanitizer in Nebula Graph is very easy. You only need to add the ENABLE_ASAN option in Cmake as below:
It is recommended that all developers turn on AddressSanitizer to run unit tests. By doing so, we can find many memory problems that are not easy to find and save debugging time.
Originally published at https://nebula-graph.io.
Top comments (0)