Memory Technologies Development Only ASan - antimetal/system-agent GitHub Wiki

AddressSanitizer (ASAN)

Overview

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

Performance Characteristics

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

Memory Usage Details

  • 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)

How It Works

Compiler Instrumentation

AddressSanitizer works through compile-time instrumentation where the compiler inserts additional code around memory operations to track and validate memory access patterns.

Shadow Memory Architecture

  • 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

Memory State Encoding

Shadow Memory Values:
- 0: All 8 bytes accessible
- 1-7: First N bytes accessible, rest poisoned
- Negative: Completely inaccessible (heap redzone, stack redzone, etc.)

Poisoned Red Zones

  • 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

Quarantine System

  • 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

System-Agent Implementation Plan

Development Builds Only

# Development configuration
CMAKE_BUILD_TYPE=Debug
CFLAGS="-fsanitize=address -g -fno-omit-frame-pointer -O1"
CXXFLAGS="-fsanitize=address -g -fno-omit-frame-pointer -O1"

CI/CD Integration

# 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

Never for Production

  • 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 Deployment

# 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

Compilation

GCC Compilation

# 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

Clang Compilation

# 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

Link Flags

# 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

Optimization Considerations

  • -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

Features

Memory Error Detection

Heap Buffer Overflow

// Detected: Reading/writing beyond allocated heap memory
char *buffer = malloc(10);
buffer[10] = 'x';  // ASAN will catch this

Stack Buffer Overflow

// Detected: Array bounds violations on stack
void function() {
    char buffer[10];
    buffer[10] = 'x';  // ASAN will catch this
}

Use-After-Free

// Detected: Accessing freed memory
char *ptr = malloc(10);
free(ptr);
*ptr = 'x';  // ASAN will catch this (quarantine system)

Use-After-Return

// Detected with -fsanitize-address-use-after-return
char* function() {
    char buffer[10];
    return buffer;  // ASAN can detect access to returned stack memory
}

Memory Leaks (via LSAN)

// Detected: Memory not freed before program exit
void function() {
    char *ptr = malloc(100);
    // Missing free(ptr) - LSAN will report this
}

Global Buffer Overflow

// Detected: Accessing beyond global arrays
char global_buffer[10];
void function() {
    global_buffer[10] = 'x';  // ASAN will catch this
}

Code Examples

Basic Usage

// 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;
}

Compilation and Execution

# 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

CMake Integration

# 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 ..

Runtime Options

ASAN_OPTIONS Environment Variable

Essential Options

# 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"

Memory Management Options

# 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"

Error Reporting Options

# 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"

Verbosity Levels

  • 0: Minimal output
  • 1: Standard error reporting
  • 2: Detailed debugging information
  • 3: Extensive internal information

Symbolization

# 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"

Error Reports

Understanding Output

Typical Error Report Structure

=================================================================
==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

Key Components Explained

  1. Error Type: heap-buffer-overflow
  2. Address: Exact memory location of violation
  3. Access Type: READ/WRITE and size
  4. Stack Trace: Where error occurred
  5. Allocation Info: Where memory was allocated
  6. Shadow Memory: Visual representation of memory layout
  7. Legend: Shadow byte meanings

Stack Traces

# 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 Dumps

Shadow Memory Legend:
fa = red zone (poisoned)
00 = accessible
01-07 = partially accessible (N accessible bytes)

Symbolization Requirements

# Ensure symbolizer is available
which llvm-symbolizer
# or
which addr2line

# Set symbolizer path if needed
export ASAN_SYMBOLIZER_PATH="/usr/local/bin/llvm-symbolizer"

Integration with Build Systems

CMake Configuration

Basic Setup

# 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()

Advanced CMake Setup

# 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 ..

Bazel Setup

Basic Configuration

# BUILD file
cc_binary(
    name = "my_program",
    srcs = ["main.c"],
    copts = ["-fsanitize=address", "-g", "-fno-omit-frame-pointer"],
    linkopts = ["-fsanitize=address"],
)

Bazel Config for Sanitizers

# .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

Make Integration

Makefile Setup

# 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

CI/CD Pipelines

GitHub Actions

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

# .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

Comparison with Alternatives

vs Valgrind

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

vs LeakSanitizer (LSAN)

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.

vs Static Analysis

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.

Use Cases

Development Testing

Local Development

# 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

Unit Testing Integration

// 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);
}

Fuzzing Campaigns

AFL++ Integration

# 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 @@

libFuzzer Integration

// 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

Security Testing

Penetration Testing Setup

# 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

Buffer Overflow Detection

// 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);
}

CI/CD Validation

Regression Testing

# 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/

Performance Regression Detection

#!/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

Limitations

Requires Recompilation

  • 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

High Overhead

  • 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

Not for Production

  • 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

Binary Size Increase

# 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

Platform and Compiler Support

  • 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

Runtime Environment Constraints

# 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

Interaction with Other Tools

  • 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

Best Practices Summary

Development Workflow

  1. Enable Early: Integrate ASan into development builds from project start
  2. CI Integration: Always run ASan tests in continuous integration
  3. Regular Testing: Run ASan-enabled test suite on all code changes
  4. Team Training: Ensure all developers understand ASan output

Configuration Optimization

# 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"

Error Remediation

  1. Fix Immediately: Address ASan errors as soon as detected
  2. Root Cause: Understand underlying issue, not just the symptom
  3. Test Coverage: Add specific tests for the fixed issue
  4. Documentation: Document common error patterns and solutions

See Also

References

⚠️ **GitHub.com Fallback** ⚠️