Using ASan from inside C - ProkopHapala/FireCore GitHub Wiki
AddressSanitizer (ASan) is best known for runtime detection of memory errors when you compile with -fsanitize=address, but its C/C++ runtime also exposes a small “instrumentation” API. You can call these hooks directly when you want extra diagnostics (e.g., the logging we added around sw_register_buffers). This note walks through the basics.
-
Compile and link with ASan on every target that participates in your run (executables, shared libs):
clang++ -g -O1 -fsanitize=address -fno-omit-frame-pointer \ -o myapp src/*.cppGCC supports the same switches. When mixing static/shared libraries, make sure every library that you want ASan-instrumented is rebuilt.
-
Set runtime options via environment variables when needed (examples):
export ASAN_OPTIONS=halt_on_error=1:detect_leaks=0:abort_on_error=1 export LSAN_OPTIONS=detect_leaks=0
ASan ships with an optional header that declares helper functions. Wrap the include in an __has_include guard so builds still succeed when ASan is disabled:
#if __has_include(<sanitizer/asan_interface.h>)
# include <sanitizer/asan_interface.h>
# define HAVE_ASAN_INTERFACE 1
#else
# define HAVE_ASAN_INTERFACE 0
#endifUse HAVE_ASAN_INTERFACE to compile ASan-only code paths.
| Function | Purpose | Notes |
|---|---|---|
int __asan_address_is_poisoned(void const *addr) |
Returns non-zero if ASan considers addr poisoned. |
Useful for logging whether a pointer is already invalid before you touch it. |
void __asan_poison_memory_region(void *addr, size_t size) |
Marks a region as poisoned so future accesses fault. | Helpful for custom allocators or when guarding manual buffers. |
void __asan_unpoison_memory_region(void *addr, size_t size) |
Clears poison so ASan stops trapping. | Use after you hand memory back to valid code paths. |
void __asan_set_error_report_callback(void (*cb)(char const *msg)) |
Installs a custom crash report hook. | Optional, stick to logging unless you need to intercept reports. |
void __asan_handle_no_return(void) |
Inform ASan a function does not return. | Rare outside low-level signal handlers. |
The interface also exposes lower-level allocator APIs and stack-trace helpers, but the functions above cover most diagnostic needs for “postmortem” logging.
This is exactly what we used around the prefix buffer:
#if HAVE_ASAN_INTERFACE
if (__asan_address_is_poisoned(prefix)) {
printf("prefix %p already poisoned!\n", prefix);
}
#endifIt tells you whether ASan already marked that address as invalid (red zones, freed memory, etc.). If you call this before and after the code you suspect, a change from 0 → 1 means that code poisoned it.
For a scratch buffer that must never be touched after a certain point:
char buf[256];
process(buf, sizeof(buf));
#if HAVE_ASAN_INTERFACE
__asan_poison_memory_region(buf, sizeof(buf));
#endifAny accidental reuse raises an immediate ASan error. When you legitimately need it again:
#if HAVE_ASAN_INTERFACE
__asan_unpoison_memory_region(buf, sizeof(buf));
#endifIf you manage raw memory yourself, poison pages you hand out and unpoison just before returning real data:
void* MyPool::alloc() {
Block* b = freelist.pop();
#if HAVE_ASAN_INTERFACE
__asan_unpoison_memory_region(b, blockSize);
#endif
return b;
}
void MyPool::free(void* p) {
#if HAVE_ASAN_INTERFACE
__asan_poison_memory_region(p, blockSize);
#endif
freelist.push(static_cast<Block*>(p));
}| Task | Recommendation |
|---|---|
| Toggle ASan at runtime | Keep a wrapper (e.g., st.set_debug) so Python can flip verbosity without rebuilding. |
| Leave hooks compiled in | Wrap them in #if HAVE_ASAN_INTERFACE so the same code runs clean when ASan is disabled. |
| Keep logging light | Print only when verbosity or similar debug level is high to avoid noisy output in normal runs. |
-
LLVM AddressSanitizer Documentation
https://clang.llvm.org/docs/AddressSanitizer.html -
GCC AddressSanitizer manual
https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fsanitize-address -
sanitizer/asan_interface.h (installed with LLVM/GCC toolchains) — browse
/usr/include/sanitizer/asan_interface.hfor the full API. -
Chromium’s ASan usage guide (great real-world tips)
https://chromium.googlesource.com/chromium/src/+/HEAD/docs/sanitizer_integration.md
- Rebuild everything with
-fsanitize=address -fno-omit-frame-pointer. - Wrap suspicious bytes in ASan logging or poison/unpoison calls.
- Turn on detailed logging only when you’re instrumenting (verbosity flags help).
- Keep ASan guards under feature macros so production builds stay clean.
- Once fixed, revert the extra prints but leave utility hooks available—you’ll reuse them next time.
This pattern helped us isolate the “poisoned prefix” crash without rewriting allocation logic, and it’s a handy trick any time you suspect a specific pointer is getting clobbered in native code.