Detecting C++ Memory Errors with AddressSanitizer
AddressSanitizer (ASan) is a fast runtime memory error detector for C/C++ that uses compiler instrumentation and shadow memory to catch heap, stack, and global out‑of‑bounds accesses, use‑after‑free, double‑free and leaks, with detailed reports and configurable options for debugging and CI.
1. What issues can ASan detect?
ASan can find typical memory bugs such as:
Use after free:
// heap-use-after-free
int* p = new int[10];
delete[] p;
return p[0]; // use after freeHeap buffer overflow:
// heap-buffer-overflow
int* p = new int[10];
p[10] = 1; // out‑of‑bounds writeStack buffer overflow:
// stack-buffer-overflow
int a[10];
a[10] = 1; // out‑of‑bounds writeDouble free:
// double-free
int* p = new int;
delete p;
delete p;ASan is a runtime detector, so it only reports errors on execution paths that are actually taken.
2. Basic principle
During compilation, the compiler inserts a check before each memory access. For example, the original code x = *p; is transformed into roughly:
if (address_is_poisoned(p)) {
report_error();
}
x = *p;The function address_is_poisoned consults a separate "shadow memory" that records the accessibility of each byte.
Shadow Memory
ASan maintains a shadow copy of the program's memory, marking bytes as accessible or poisoned. For a heap allocation of 16 bytes, the runtime may layout memory as: [redzone][16 bytes usable][redzone] Redzones are deliberately placed before and after objects. An out‑of‑bounds write such as p[16] = 'x'; hits the right‑hand redzone, the shadow memory marks the address as poisoned, and ASan reports a heap-buffer-overflow .
Use‑after‑free detection
When memory is freed, ASan marks it as poisoned and may place it in a quarantine queue. Subsequent accesses like <code>free(p); *p = 1;</code> are caught because the shadow memory still indicates the region is invalid.
Stack and global variables
For stack variables, ASan inserts redzones between locals; for global variables, it adds redzones around each global object. Thus it can detect out‑of‑bounds accesses in heap, stack, and global memory.
3. Basic usage
Clang / GCC compilation
clang++ -g -O1 -fsanitize=address -fno-omit-frame-pointer main.cpp -o main
./main g++ -g -O1 -fsanitize=address -fno-omit-frame-pointer main.cpp -o main
./mainThe recommended flags are -fsanitize=address for both compile and link steps, together with -g and -fno-omit-frame-pointer for better stack traces. The Clang documentation suggests using at least -O1 for reasonable performance; GCC also recommends -g and mentions that ASAN_OPTIONS can control runtime behavior. Typical debug/CI flags
CXXFLAGS="-g -O1 -fsanitize=address -fno-omit-frame-pointer"
LDFLAGS="-fsanitize=address"Both compilation and linking must include -fsanitize=address , otherwise the ASan runtime will not be linked. CMake usage
cmake -DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer" \
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" ..
makeOr set options per target:
target_compile_options(my_app PRIVATE
-fsanitize=address
-fno-omit-frame-pointer
-g)
target_link_options(my_app PRIVATE
-fsanitize=address)Runtime options ASan behavior can be tuned with the ASAN_OPTIONS environment variable, e.g.:
ASAN_OPTIONS=detect_leaks=1 ./main
ASAN_OPTIONS=abort_on_error=1 ./main
ASAN_OPTIONS=halt_on_error=0 ./main
ASAN_OPTIONS=symbolize=1 ./mainIf reports lack function names or source line numbers, ensure the binary was compiled with -g and that llvm-symbolizer is reachable (the ASAN_SYMBOLIZER_PATH variable can point to it).
4. How to read ASan reports
An example report:
ERROR: AddressSanitizer: heap-use-after-free
READ of size 4 at 0x...
#0 main example.cc:10
freed by thread T0 here:
#0 operator delete[]
#1 main example.cc:9
previously allocated by thread T0 here:
#0 operator new[]
#1 main example.cc:8Key parts to examine:
The error type line (e.g., heap-use-after-free) tells what kind of bug was found.
The READ of size 4 line indicates whether the operation was a read or write and how many bytes were accessed.
The stack traces show where the memory was allocated, freed, and incorrectly accessed.
ASan reports are richer than a plain crash because they provide the full lifecycle of the offending memory.
5. A complete example
#include <iostream>
int main(int argc, char** argv){
int* arr = new int[4];
delete[] arr;
std::cout << arr[0] << std::endl; // use‑after‑free
return 0;
}Compile and run:
clang++ -g -O1 -fsanitize=address -fno-omit-frame-pointer uaf.cpp -o uaf
./uafThe execution prints an error similar to the one shown above, and the source line numbers in the report guide the fix.
6. Common considerations
ASan is intended for testing environments; it should not be linked into production binaries because its runtime is not designed for production‑level safety and adds noticeable overhead (Clang documentation cites a typical ~2× slowdown). The usual practice is to enable ASan for development, unit tests, CI, and fuzzing, and disable it for release builds. ASan only detects errors on executed paths, so untested code remains unchecked. It also does not replace other sanitizers: ThreadSanitizer (TSan) for data races, UndefinedBehaviorSanitizer (UBSan) for undefined behavior, and MemorySanitizer (MSan) for uninitialized memory reads.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Big Data Technology Tribe
Focused on computer science and cutting‑edge tech, we distill complex knowledge into clear, actionable insights. We track tech evolution, share industry trends and deep analysis, helping you keep learning, boost your technical edge, and ride the digital wave forward.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
