AddressSanitizer for C++: The Complete Guide to Memory Error Detection
1. What It Is and Why You Need It
AddressSanitizer (ASan) isn't a library in the traditional sense — it's a powerful dynamic analysis tool built directly into the Clang and GCC compilers. It detects memory-related bugs at runtime. Developed by Google, ASan has become the de facto standard for catching dangerous defects like buffer overflows, use-after-free errors, double-frees, and memory leaks.
Why does this matter? Memory bugs are among the trickiest in C++. They can lurk for years, causing random crashes, data corruption, or security vulnerabilities. Traditional debuggers often fail because the program may crash far from the actual bug location. ASan solves this by instrumenting code at compile time: it adds checks around every memory operation (malloc, new, free, delete, pointer reads/writes). When it detects an anomaly, ASan immediately halts the program and outputs a detailed stack trace with the exact line of code, error type, and memory state.
For the PythonLib educational site, here's the key takeaway: even though Python manages memory automatically, knowing ASan is invaluable for C++ developers building high-performance or security-critical applications. ASan integrates into CI/CD pipelines, catching bugs before they hit production. It runs on Linux, macOS, Windows, and FreeBSD, supporting C, C++, and Objective-C.
2. Installation
AddressSanitizer isn't installed separately — it's part of the compiler. You need Clang (version 3.1+) or GCC (version 4.8+).
Installing the compiler (Ubuntu/Debian example):
sudo apt update
sudo apt install clang-14 # or g++-11
For macOS: Xcode Command Line Tools include Clang with ASan.
xcode-select --install
For Windows: Use Visual Studio 2019+ (enable the /fsanitize=address flag) or install Clang via LLVM.
Verify support:
clang++ -fsanitize=address --version
g++ -fsanitize=address --version
3. Quick Start — Minimal Working Example
Create a file example.cpp with a deliberate bug (use-after-free):
#include <iostream>
int main() {
int* p = new int(42);
delete p;
std::cout << *p << std::endl; // Bug: use-after-free
return 0;
}
Compile and run with ASan:
clang++ -fsanitize=address -g -o example example.cpp
./example
You'll see a detailed report with the exact line, error type, and call stack. Without ASan, the program might print 42 or crash with a segmentation fault.
4. Key Methods/Functions/Classes
AddressSanitizer doesn't provide an API to call from your code — it works automatically. However, there are helper macros and functions for fine-tuning and integration. Here are 8 key elements.
4.1. __asan_address_is_poisoned()
Signature: int __asan_address_is_poisoned(void const *addr);
What it does: Checks whether a memory region is marked as "poisoned" (inaccessible). Returns 1 if the address is in a poisoned zone, 0 otherwise.
Example:
#include <sanitizer/asan_interface.h>
#include <iostream>
int main() {
int* arr = new int[10];
arr[5] = 100;
// Check if address arr+10 is poisoned (out of bounds)
if (__asan_address_is_poisoned(arr + 10)) {
std::cout << "Address is poisoned (out of bounds)" << std::endl;
} else {
std::cout << "Address is safe" << std::endl;
}
delete[] arr;
return 0;
}
When to use: For custom allocators or debugging complex data structures where you need to check memory accessibility manually.
4.2. __asan_poison_memory_region()
Signature: void __asan_poison_memory_region(void const volatile *addr, size_t size);
What it does: Manually marks a memory region as poisoned. Any subsequent read/write access to this region will trigger an ASan error.
Example:
#include <sanitizer/asan_interface.h>
#include <iostream>
int main() {
int* buffer = new int[100];
// Poison the first 10 elements
__asan_poison_memory_region(buffer, 10 * sizeof(int));
// This will trigger an error
buffer[5] = 42; // ASan error: accessing poisoned memory
delete[] buffer;
return 0;
}
When to use: For implementing custom memory pools, freelists, or debugging memory corruption in specific regions.
4.3. __asan_unpoison_memory_region()
Signature: void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
What it does: Reverses the effect of __asan_poison_memory_region(), making the memory accessible again.
Example:
#include <sanitizer/asan_interface.h>
int main() {
int* buffer = new int[100];
__asan_poison_memory_region(buffer, 10 * sizeof(int));
// Unpoison the region
__asan_unpoison_memory_region(buffer, 10 * sizeof(int));
buffer[5] = 42; // Now safe
delete[] buffer;
return 0;
}
When to use: In custom allocators when you need to temporarily mark memory as safe after poisoning it for debugging.
4.4. __asan_describe_address()
Signature: void __asan_describe_address(void *addr);
What it does: Prints a detailed description of the given address, including which allocation it belongs to, its size, and whether it's currently poisoned.
Example:
#include <sanitizer/asan_interface.h>
#include <iostream>
int main() {
int* arr = new int[20];
__asan_describe_address(arr + 5); // Prints info about this address
delete[] arr;
return 0;
}
When to use: During debugging to understand the state of a suspicious pointer or memory region.
4.5. __asan_set_error_report_callback()
Signature: void __asan_set_error_report_callback(void (*callback)(const char*));
What it does: Registers a custom callback function that ASan calls when it detects an error. The callback receives the error report as a string.
Example:
#include <sanitizer/asan_interface.h>
#include <iostream>
void myErrorHandler(const char* report) {
std::cerr << "ASan Error: " << report << std::endl;
// You could log to a file, send an alert, etc.
}
int main() {
__asan_set_error_report_callback(myErrorHandler);
int* p = new int(10);
delete p;
*p = 20; // Triggers callback
return 0;
}
When to use: For custom error logging, integration with testing frameworks, or automated bug reporting in CI/CD pipelines.
4.6. __asan_get_current_allocated_bytes()
Signature: size_t __asan_get_current_allocated_bytes();
What it does: Returns the total number of bytes currently allocated and tracked by ASan. Useful for detecting memory leaks.
Example:
#include <sanitizer/asan_interface.h>
#include <iostream>
int main() {
std::cout << "Before allocation: " << __asan_get_current_allocated_bytes() << " bytes" << std::endl;
int* arr = new int[1000];
std::cout << "After allocation: " << __asan_get_current_allocated_bytes() << " bytes" << std::endl;
delete[] arr;
std::cout << "After deallocation: " << __asan_get_current_allocated_bytes() << " bytes" << std::endl;
return 0;
}
When to use: For monitoring memory usage in long-running applications or detecting leaks during testing.
4.7. __asan_set_death_callback()
Signature: void __asan_set_death_callback(void (*callback)(void));
What it does: Registers a callback that ASan calls just before terminating the program due to a detected error. The callback receives no arguments.
Example:
#include <sanitizer/asan_interface.h>
#include <iostream>
void onDeath() {
std::cerr << "ASan detected a fatal error. Saving crash dump..." << std::endl;
// Save state, generate core dump, etc.
}
int main() {
__asan_set_death_callback(onDeath);
int* p = new int(5);
delete p;
*p = 10; // Triggers death callback
return 0;
}
When to use: For cleanup operations, saving diagnostic data, or generating crash reports before the program exits.
4.8. __asan_report_error()
Signature: void __asan_report_error(void *pc, void *bp, void *sp, void *addr, int is_write, size_t access_size);
What it does: Manually triggers an ASan error report. This is useful for testing ASan's behavior or simulating errors in custom allocators.
Example:
#include <sanitizer/asan_interface.h>
int main() {
int dummy = 0;
// Simulate a write error at address &dummy
__asan_report_error(__builtin_return_address(0), 0, 0, &dummy, 1, sizeof(dummy));
return 0;
}
When to use: For testing ASan integration, validating error handling logic, or debugging custom memory management code.
5. Best Practices and Common Pitfalls
- Always compile with debug symbols (-g): Without them, stack traces won't show line numbers.
- Use ASan in debug builds only: It adds significant overhead (2x-3x slowdown) and memory usage. Never ship ASan-instrumented code to production.
- Combine with other sanitizers: Use
-fsanitize=address -fsanitize=undefinedfor comprehensive coverage. - Suppress false positives: Use
suppressions.txtfiles to ignore known issues in third-party libraries. - Integrate with testing: Run ASan-enabled tests in CI to catch regressions early.
- Watch for stack overflow: ASan increases stack usage; use
ulimit -s unlimitedon Linux if needed.
6. Conclusion
AddressSanitizer is an indispensable tool for any C++ developer serious about writing safe, reliable code. It catches memory bugs that might otherwise go undetected for years, saving countless hours of debugging. By integrating ASan into your development workflow — from local builds to CI/CD pipelines — you can dramatically reduce the risk of crashes, data corruption, and security vulnerabilities in your applications.
Start using ASan today: add -fsanitize=address -g to your compiler flags and watch your code quality improve. For more advanced usage, explore the ASan API functions covered in this guide to customize error handling and memory monitoring.
Also in library
JSON Processing in C++: Libraries, Parsing and Data Serialization
Authorization and Authentication in TypeScript: A Complete Guide
Machine Learning in C++: Overview of Libraries and Tools
Internationalization (i18n) in Kotlin: A Complete Guide with Examples