Memory Technologies Development Only ASan - antimetal/system-agent GitHub Wiki
AddressSanitizer (ASan) is a fast memory error detector that consists of a compiler instrumentation module and a run-time library. It is part of the LLVM/GCC compiler suites and provides comprehensive memory safety checking at compile-time through instrumentation.
Key Characteristics:
- Compile-time instrumentation for memory errors
- Part of LLVM/GCC compiler suites
- 2-3x memory overhead, ~2x CPU slowdown
- Detects buffer overflows, use-after-free, memory leaks
- Development and testing tool (not production-ready)
- Exits on first detected error by design
Metric | Impact |
---|---|
CPU Overhead | 200% (2x slowdown) |
Memory Overhead | 2-3x application memory |
Binary Size | Increased (inline instrumentation) |
Accuracy | High (no false positives) |
False Positives | Very Low (essentially none) |
Production Ready | No (development/testing only) |
Requires | Recompilation with flags |
- Shadow Memory: 1 byte per 8 bytes of application memory
- Virtual Address Space: Maps 16+ TB on 64-bit platforms (but doesn't reserve)
- Stack Memory: Up to 3x increase observed
- Heap Overhead: Depends on allocation sizes (smaller allocations = higher overhead)
AddressSanitizer works through compile-time instrumentation where the compiler inserts additional code around memory operations to track and validate memory access patterns.
- Mapping Ratio: 8 bytes of application memory → 1 byte of shadow memory
- Shadow Location: Dedicated memory region at high addresses
- Granules: Memory chunks of 8 bytes (aligned to their size)
- State Tracking: Each shadow byte encodes accessibility of corresponding memory granule
Shadow Memory Values:
- 0: All 8 bytes accessible
- 1-7: First N bytes accessible, rest poisoned
- Negative: Completely inaccessible (heap redzone, stack redzone, etc.)
- Purpose: Detect buffer overflows
- Placement: Between objects in memory (stack variables, heap allocations)
- Size: Configurable (minimum 16 bytes, must be power of two)
- Detection: Immediate crash when accessing redzone memory
- Mechanism: Freed memory placed in quarantine queue
- Duration: Configurable time before memory can be reallocated
- Purpose: Detect use-after-free errors
- Trade-off: Higher memory usage vs. better error detection
# Development configuration
CMAKE_BUILD_TYPE=Debug
CFLAGS="-fsanitize=address -g -fno-omit-frame-pointer -O1"
CXXFLAGS="-fsanitize=address -g -fno-omit-frame-pointer -O1"
# GitHub Actions example
- name: Build with AddressSanitizer
run: |
cmake -DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_C_FLAGS="-fsanitize=address -g" \
-DCMAKE_CXX_FLAGS="-fsanitize=address -g" \
..
make -j$(nproc)
- name: Run tests with ASAN
env:
ASAN_OPTIONS: "verbosity=1:halt_on_error=1:abort_on_error=1"
run: make test
- Static linking: Not supported by design
- Performance: 2x slowdown unacceptable for production
- Memory usage: 2-3x overhead too high
- Purpose: Bug detection tool only
# Testing environment setup
export ASAN_OPTIONS="detect_leaks=1:check_initialization_order=1:strict_init_order=1"
export MSAN_OPTIONS="print_stats=1"
# Run comprehensive test suite
./run_tests_with_sanitizers.sh
# Basic compilation with GCC
gcc -fsanitize=address -g -O1 -fno-omit-frame-pointer source.c -o program
# With additional options
gcc -fsanitize=address \
-fsanitize-address-use-after-scope \
-g -O1 -fno-omit-frame-pointer \
-fno-optimize-sibling-calls \
source.c -o program
# Basic compilation with Clang
clang -fsanitize=address -g -O1 -fno-omit-frame-pointer source.c -o program
# With leak detection
clang -fsanitize=address -fsanitize-address-use-after-scope \
-g -O1 -fno-omit-frame-pointer source.c -o program
# Ensure AddressSanitizer runtime is linked
# Use clang/gcc for linking (not ld directly)
clang -fsanitize=address -g source1.o source2.o -o program
# Static linking (where supported)
clang -fsanitize=address -static-libasan source.c -o program
- -O1: Recommended minimum for reasonable performance
- -fno-omit-frame-pointer: Better stack traces in error reports
- -fno-optimize-sibling-calls: Perfect stack traces (with -O1)
- -g: Debug symbols for line numbers in error reports
// Detected: Reading/writing beyond allocated heap memory
char *buffer = malloc(10);
buffer[10] = 'x'; // ASAN will catch this
// Detected: Array bounds violations on stack
void function() {
char buffer[10];
buffer[10] = 'x'; // ASAN will catch this
}
// Detected: Accessing freed memory
char *ptr = malloc(10);
free(ptr);
*ptr = 'x'; // ASAN will catch this (quarantine system)
// Detected with -fsanitize-address-use-after-return
char* function() {
char buffer[10];
return buffer; // ASAN can detect access to returned stack memory
}
// Detected: Memory not freed before program exit
void function() {
char *ptr = malloc(100);
// Missing free(ptr) - LSAN will report this
}
// Detected: Accessing beyond global arrays
char global_buffer[10];
void function() {
global_buffer[10] = 'x'; // ASAN will catch this
}
// test_program.c
#include <stdlib.h>
#include <string.h>
int main() {
char *buffer = malloc(10);
strcpy(buffer, "Hello World"); // Buffer overflow - ASAN will detect
free(buffer);
return 0;
}
# Compile with AddressSanitizer
clang -fsanitize=address -g -O1 test_program.c -o test_program
# Run with basic options
ASAN_OPTIONS="verbosity=2:halt_on_error=1" ./test_program
# Run with symbolization
ASAN_OPTIONS="symbolize=1:print_stacktrace=1" ./test_program
# CMakeLists.txt
if(ENABLE_ASAN)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -g -fno-omit-frame-pointer")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -g -fno-omit-frame-pointer")
set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} -fsanitize=address")
endif()
# Usage:
# cmake -DENABLE_ASAN=ON ..
# Basic configuration
export ASAN_OPTIONS="verbosity=1:halt_on_error=1:abort_on_error=1"
# Development configuration
export ASAN_OPTIONS="verbosity=2:symbolize=1:print_stacktrace=1:check_initialization_order=1"
# CI/CD configuration
export ASAN_OPTIONS="halt_on_error=1:abort_on_error=1:print_stats=1:atexit=1"
# Quarantine settings
export ASAN_OPTIONS="quarantine_size_mb=256:mmap_limit_mb=2048"
# Reduce memory usage (Android-style)
export ASAN_OPTIONS="mmap_limit_mb=512:quarantine_size_mb=64:fast_unwind_on_malloc=0"
# Leak detection
export ASAN_OPTIONS="detect_leaks=1:leak_check_at_exit=1"
# Detailed error reporting
export ASAN_OPTIONS="symbolize=1:print_stacktrace=1:print_module_map=1"
# Continue after error (requires -fsanitize-recover=address)
export ASAN_OPTIONS="halt_on_error=0:abort_on_error=0"
# Save crash dumps (Windows/cloud workflows)
export ASAN_SAVE_DUMPS="crash_dump.dmp"
- 0: Minimal output
- 1: Standard error reporting
- 2: Detailed debugging information
- 3: Extensive internal information
# Enable symbolization for readable stack traces
export ASAN_OPTIONS="symbolize=1"
# Requires addr2line or llvm-symbolizer in PATH
export ASAN_SYMBOLIZER_PATH="/usr/bin/llvm-symbolizer"
=================================================================
==12345==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000eff4
WRITE of size 1 at 0x60200000eff4 thread T0:
#0 0x4007a8 in main /path/to/source.c:10:5
#1 0x7f8c3c4d5f44 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21f44)
0x60200000eff4 is located 4 bytes to the right of 10-byte region [0x60200000efe0,0x60200000efea)
allocated by thread T0 here:
#0 0x4006d8 in malloc (/path/to/program+0x4006d8)
#1 0x40077c in main /path/to/source.c:8:18
SUMMARY: AddressSanitizer: heap-buffer-overflow /path/to/source.c:10:5 in main
Shadow bytes around the buggy address:
0x0c047fff9df0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e00: 00 00 fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c047fff9e10: 00 02[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Legend: fa=red zone, 00=accessible, 02=partially accessible
==12345==ABORTING
-
Error Type:
heap-buffer-overflow
- Address: Exact memory location of violation
- Access Type: READ/WRITE and size
- Stack Trace: Where error occurred
- Allocation Info: Where memory was allocated
- Shadow Memory: Visual representation of memory layout
- Legend: Shadow byte meanings
# Improve stack traces
export ASAN_OPTIONS="fast_unwind_on_malloc=0:symbolize=1"
# Perfect stack traces (slower)
# Compile with: -O1 -fno-optimize-sibling-calls -fno-omit-frame-pointer
Shadow Memory Legend:
fa = red zone (poisoned)
00 = accessible
01-07 = partially accessible (N accessible bytes)
# Ensure symbolizer is available
which llvm-symbolizer
# or
which addr2line
# Set symbolizer path if needed
export ASAN_SYMBOLIZER_PATH="/usr/local/bin/llvm-symbolizer"
# Enable AddressSanitizer option
option(ENABLE_ASAN "Enable AddressSanitizer" OFF)
if(ENABLE_ASAN)
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(WARNING "AddressSanitizer requires Debug build type")
endif()
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address -fno-omit-frame-pointer")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fno-omit-frame-pointer")
set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fsanitize=address")
endif()
# Create sanitizer configuration
set(CMAKE_BUILD_TYPE_ASAN "ASAN" CACHE STRING "AddressSanitizer build type")
set(CMAKE_C_FLAGS_ASAN "-fsanitize=address -fno-omit-frame-pointer -g -O1"
CACHE STRING "C flags for AddressSanitizer")
set(CMAKE_CXX_FLAGS_ASAN "-fsanitize=address -fno-omit-frame-pointer -g -O1"
CACHE STRING "CXX flags for AddressSanitizer")
set(CMAKE_LINKER_FLAGS_ASAN "-fsanitize=address"
CACHE STRING "Linker flags for AddressSanitizer")
# Usage: cmake -DCMAKE_BUILD_TYPE=ASAN ..
# BUILD file
cc_binary(
name = "my_program",
srcs = ["main.c"],
copts = ["-fsanitize=address", "-g", "-fno-omit-frame-pointer"],
linkopts = ["-fsanitize=address"],
)
# .bazelrc
# AddressSanitizer configuration
build:asan --copt=-fsanitize=address
build:asan --copt=-fno-omit-frame-pointer
build:asan --copt=-g
build:asan --linkopt=-fsanitize=address
build:asan --action_env=ASAN_OPTIONS
# Usage: bazel build --config=asan //path/to:target
# Makefile
CC := clang
CFLAGS := -Wall -Wextra -std=c11
LDFLAGS :=
# AddressSanitizer configuration
ifdef ENABLE_ASAN
CFLAGS += -fsanitize=address -fno-omit-frame-pointer -g -O1
LDFLAGS += -fsanitize=address
export ASAN_OPTIONS := verbosity=1:halt_on_error=1:abort_on_error=1
endif
# Usage: make ENABLE_ASAN=1
name: AddressSanitizer Testing
on: [push, pull_request]
jobs:
asan-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y clang llvm
- name: Build with AddressSanitizer
run: |
cmake -DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_C_FLAGS="-fsanitize=address -g -fno-omit-frame-pointer" \
-DCMAKE_CXX_FLAGS="-fsanitize=address -g -fno-omit-frame-pointer" \
..
make -j$(nproc)
- name: Run tests with ASAN
env:
ASAN_OPTIONS: "verbosity=1:halt_on_error=1:detect_leaks=1:check_initialization_order=1"
run: |
make test
# Run specific test suite
./run_integration_tests.sh
# .gitlab-ci.yml
asan-test:
stage: test
image: ubuntu:22.04
before_script:
- apt-get update -qq && apt-get install -y -qq clang cmake build-essential
script:
- mkdir build && cd build
- cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_ASAN=ON ..
- make -j$(nproc)
- export ASAN_OPTIONS="verbosity=1:halt_on_error=1"
- make test
artifacts:
reports:
junit: build/test-results.xml
Feature | AddressSanitizer | Valgrind |
---|---|---|
Performance | 2x slowdown | 10-50x slowdown |
Memory Usage | 2-3x | 3-5x |
Compilation | Requires recompilation | Works with existing binaries |
Error Detection | Compile-time + runtime | Pure runtime |
Platform Support | Limited to supported compilers | Broader platform support |
Accuracy | Very high | High |
Ease of Use | Moderate (requires rebuild) | Easy (no rebuild) |
When to use AddressSanitizer over Valgrind:
- Development/CI environments where rebuilding is feasible
- Performance is critical (faster feedback loop)
- Modern codebase with LLVM/GCC support
When to use Valgrind over AddressSanitizer:
- Cannot recompile (proprietary binaries)
- Need broader error detection (uninitialized memory)
- Platform not supported by ASan
Feature | AddressSanitizer | LeakSanitizer |
---|---|---|
Scope | Memory errors + leaks | Leaks only |
Performance | 2x slowdown | Minimal overhead |
Memory Usage | 2-3x | Low overhead |
Integration | Built into ASan | Standalone or with ASan |
Detection | Comprehensive | Leak-focused |
Relationship: LSAN is integrated into AddressSanitizer but can also run standalone.
Feature | AddressSanitizer | Static Analysis |
---|---|---|
Detection Time | Runtime | Compile-time |
False Positives | Very low | Can be high |
Coverage | Actual execution paths | All possible paths |
Performance Impact | Runtime overhead | No runtime impact |
Setup Complexity | Moderate | Low to high |
Path Coverage | Execution-dependent | Comprehensive |
Combined Approach: Use static analysis for broad coverage, ASan for precise runtime validation.
# Developer workflow
export ASAN_OPTIONS="detect_leaks=1:check_initialization_order=1"
# Build debug version with ASan
make debug-asan
# Run specific tests
./test_memory_operations
./test_buffer_handling
// test_with_asan.c
#include <CUnit/CUnit.h>
void test_buffer_operations() {
// ASan will catch any memory errors in these tests
char *buffer = malloc(10);
// Test various buffer operations
CU_ASSERT(buffer != NULL);
free(buffer);
}
# Build for fuzzing with AddressSanitizer
export CC=afl-clang-fast
export CXX=afl-clang-fast++
export AFL_USE_ASAN=1
# Configure and build
./configure --enable-debug
make clean && make -j$(nproc)
# Run fuzzing with ASan
afl-fuzz -i input_corpus -o findings -- ./target_program @@
// fuzz_target.c
#include <stdint.h>
#include <stddef.h>
// LibFuzzer will call this function with various inputs
// ASan will detect any memory errors
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size < 4) return 0;
// Process fuzzing input - ASan will catch errors
char *buffer = malloc(size);
memcpy(buffer, data, size);
process_data(buffer, size);
free(buffer);
return 0;
}
# Build fuzzer with AddressSanitizer
clang -fsanitize=address,fuzzer fuzz_target.c -o fuzz_target
# Run fuzzing
./fuzz_target -max_len=1024 -timeout=10
# Build application with all sanitizers
export CFLAGS="-fsanitize=address,undefined -g -fno-omit-frame-pointer"
export CXXFLAGS="-fsanitize=address,undefined -g -fno-omit-frame-pointer"
# Enhanced error reporting for security testing
export ASAN_OPTIONS="abort_on_error=1:print_stacktrace=1:check_initialization_order=1:strict_init_order=1"
# Build and test
./configure --enable-debug
make && make check
// Security test cases
void test_buffer_overflow_detection() {
// These should all be caught by ASan
char buffer[10];
// Stack buffer overflow
for (int i = 0; i <= 10; i++) {
buffer[i] = 'A'; // ASan will catch buffer[10]
}
// Heap buffer overflow
char *heap_buf = malloc(10);
heap_buf[10] = 'B'; // ASan will catch this
free(heap_buf);
}
# Advanced CI configuration
stages:
- build
- test-asan
- test-valgrind
- security-scan
asan-regression:
stage: test-asan
script:
# Build with multiple sanitizers
- cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_SANITIZERS=ON ..
- make -j$(nproc)
# Run comprehensive test suite
- export ASAN_OPTIONS="halt_on_error=1:detect_leaks=1:check_initialization_order=1"
- ctest --output-on-failure
# Run specific security tests
- ./run_security_tests.sh
# Memory stress tests
- ./run_memory_stress_tests.sh
artifacts:
when: always
reports:
junit: build/test_results.xml
paths:
- build/sanitizer_logs/
#!/bin/bash
# performance_regression_check.sh
# Baseline without sanitizers
make clean && make release
time ./benchmark_suite > baseline_results.txt
# Build with AddressSanitizer
make clean && make debug-asan
export ASAN_OPTIONS="detect_leaks=0:quarantine_size_mb=64"
time ./benchmark_suite > asan_results.txt
# Compare performance (should be ~2x slower)
python3 compare_performance.py baseline_results.txt asan_results.txt
- Source Code Access: Must have access to application source code
- Build System: Need to modify build configuration
- Dependencies: All libraries should ideally be built with ASan
- Deployment: Separate build pipeline for ASan-enabled versions
- Performance: ~2x CPU overhead unacceptable for production
- Memory: 2-3x memory usage can exhaust system resources
- Binary Size: Increased due to instrumentation code
- Startup Time: Additional initialization overhead
- Static Linking: Not supported by design
- Runtime Library: Must be available on target systems
- Error Handling: Exits on first error (crash-stop behavior)
- Security: Runtime detection information could aid attackers
# Compare binary sizes
$ ls -la program*
-rwxr-xr-x 1 user user 50K program_release
-rwxr-xr-x 1 user user 150K program_asan # 3x larger
# Reduce binary size with outlined instrumentation
$ clang -fsanitize=address -fsanitize-address-outline-instrumentation \
-g -O1 source.c -o program_asan_outlined
$ ls -la program_asan_outlined
-rwxr-xr-x 1 user user 80K program_asan_outlined # Smaller but slower
- Compiler Requirements: Requires modern GCC/Clang
- Platform Support: Limited compared to Valgrind
- Cross Compilation: Complex setup for embedded systems
- Architecture: May not support all target architectures
# Issues with containerized environments
# Docker containers may need additional configuration
docker run --cap-add=SYS_PTRACE your-image # For some ASan features
# ulimit may not work as expected due to virtual memory usage
ulimit -v 1000000 # May not limit ASan effectively
# Shared library loading issues
export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libasan.so.6
- Debuggers: GDB integration requires special handling
- Profilers: May interfere with performance profiling
- Other Sanitizers: Cannot combine with all sanitizers simultaneously
- JIT Compilers: Limited support for Just-In-Time compiled code
- Enable Early: Integrate ASan into development builds from project start
- CI Integration: Always run ASan tests in continuous integration
- Regular Testing: Run ASan-enabled test suite on all code changes
- Team Training: Ensure all developers understand ASan output
# Recommended ASAN_OPTIONS for different scenarios
# Development (maximum detection)
export ASAN_OPTIONS="detect_leaks=1:check_initialization_order=1:strict_init_order=1:detect_stack_use_after_return=1:print_stacktrace=1"
# CI/CD (fast feedback)
export ASAN_OPTIONS="halt_on_error=1:abort_on_error=1:detect_leaks=1:quarantine_size_mb=128"
# Fuzzing (performance optimized)
export ASAN_OPTIONS="detect_leaks=0:quarantine_size_mb=64:mmap_limit_mb=512:fast_unwind_on_malloc=1"
- Fix Immediately: Address ASan errors as soon as detected
- Root Cause: Understand underlying issue, not just the symptom
- Test Coverage: Add specific tests for the fixed issue
- Documentation: Document common error patterns and solutions
- BCC MemLeak Tool - eBPF-based memory leak detection
- Hardware Performance Counters - PMC-based memory monitoring
- jemalloc Profiling - Allocator-based memory analysis
- PSI Metrics - Pressure Stall Information for memory pressure