sanitizers

SKILL.md

Sanitizers

Purpose

Guide agents through choosing, enabling, and interpreting compiler runtime sanitizers for finding memory errors, undefined behaviour, data races, and memory leaks.

Triggers

  • "My program has a memory error — which sanitizer do I use?"
  • "How do I enable ASan?"
  • "How do I interpret an ASan/UBSan/TSan report?"
  • "ASan says heap-buffer-overflow — what does that mean?"
  • "How do I suppress false positives in sanitizers?"
  • "Can I use sanitizers in CI?"

Workflow

1. Decision tree: which sanitizer?

Bug class?
├── Memory OOB, use-after-free, double-free → AddressSanitizer (ASan)
├── Stack OOB, global OOB → ASan (all three covered)
├── Uninitialised reads → MemorySanitizer (MSan, Clang only, requires all-clang build)
├── Undefined behaviour (int overflow, null deref, bad cast) → UBSan
├── Data races (multi-thread) → ThreadSanitizer (TSan)
├── Memory leaks only → LeakSanitizer (LSan, standalone or via ASan)
└── Multiple classes → ASan + UBSan (common combo); cannot combine with TSan or MSan

2. AddressSanitizer (ASan)

# GCC or Clang
gcc -fsanitize=address -fno-omit-frame-pointer -g -O1 -o prog main.c
# Or
clang -fsanitize=address -fno-omit-frame-pointer -g -O1 -o prog main.c

Runtime options (via ASAN_OPTIONS):

ASAN_OPTIONS=detect_leaks=1:abort_on_error=1:log_path=/tmp/asan.log ./prog
ASAN_OPTIONS key Effect
detect_leaks=0/1 Enable LeakSanitizer (default 1 on Linux)
abort_on_error=1 Call abort() instead of _exit() (for core dumps)
log_path=path Write report to file
symbolize=1 Symbolize addresses (needs llvm-symbolizer in PATH)
fast_unwind_on_malloc=0 More accurate stacks (slower)
quarantine_size_mb=256 Delay reuse of freed memory

Interpreting ASan output:

==12345==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000050
READ of size 4 at 0x602000000050 thread T0
    #0 0x401234 in foo /home/user/src/main.c:15
    #1 0x401567 in main /home/user/src/main.c:42

0x602000000050 is located 0 bytes after a 40-byte region
[0x602000000028, 0x602000000050) allocated at:
    #0 0x7f12345 in malloc ...
    #1 0x401234 in main /home/user/src/main.c:10

Reading: the top frame in WRITE/READ is the access site; the allocated at stack shows the allocation. The region is 40 bytes at [start, end) and the access is at end = one byte past the end (classic off-by-one).

3. UndefinedBehaviorSanitizer (UBSan)

gcc -fsanitize=undefined -g -O1 -o prog main.c
# More complete: add specific checks
gcc -fsanitize=undefined,integer -g -O1 -o prog main.c

Common UBSan checks:

  • signed-integer-overflow
  • unsigned-integer-overflow (not in undefined by default)
  • null — null pointer dereference
  • bounds — array index OOB (compile-time knowable bounds)
  • alignment — misaligned pointer access
  • float-cast-overflow — float-to-int conversion overflow
  • vptr — C++ vtable type mismatch
  • shift-exponent — shift >= bit width
# Enable everything including integer overflow
gcc -fsanitize=undefined \
    -fsanitize=signed-integer-overflow,unsigned-integer-overflow,float-cast-overflow \
    -fno-sanitize-recover=all \   # abort instead of continue
    -g -O1 -o prog main.c

-fno-sanitize-recover=all: makes UBSan abort on first error (important for CI).

Interpreting UBSan output:

src/main.c:15:12: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'

4. ThreadSanitizer (TSan)

# Clang or GCC (GCC ≥ 4.8)
clang -fsanitize=thread -g -O1 -o prog main.c

# TSan is incompatible with ASan and MSan

Interpreting TSan output:

WARNING: ThreadSanitizer: data race (pid=12345)
  Write of size 4 at 0x7f... by thread T2:
    #0 increment /home/user/src/counter.c:8
  Previous read of size 4 at 0x7f... by thread T1:
    #0 read_counter /home/user/src/counter.c:3

5. MemorySanitizer (MSan)

MSan detects reads of uninitialised memory. Clang only. Requires all-instrumented build (no mixing of MSan and non-MSan objects).

clang -fsanitize=memory -fno-omit-frame-pointer -g -O1 -o prog main.c
# With origin tracking (slower but shows where uninit value came from)
clang -fsanitize=memory -fsanitize-memory-track-origins=2 -g -O1 -o prog main.c

System libraries must be rebuilt with MSan or substituted with MSan-instrumented wrappers. Use msan-libs toolchain from LLVM.

6. ASan + UBSan combined

gcc -fsanitize=address,undefined -fno-sanitize-recover=all \
    -fno-omit-frame-pointer -g -O1 -o prog main.c

Do not combine with TSan or MSan.

7. Suppressions

# ASan suppression file
cat > asan.supp << 'EOF'
# Suppress leaks from OpenSSL init
leak:CRYPTO_malloc
EOF

LSAN_OPTIONS=suppressions=asan.supp ./prog

# UBSan suppression
cat > ubsan.supp << 'EOF'
signed-integer-overflow:third_party/fast_math.c
EOF
UBSAN_OPTIONS=suppressions=ubsan.supp:print_stacktrace=1 ./prog

8. CMake integration

option(SANITIZE "Enable sanitizers" OFF)
if(SANITIZE)
    set(san_flags -fsanitize=address,undefined -fno-sanitize-recover=all
                  -fno-omit-frame-pointer -g -O1)
    add_compile_options(${san_flags})
    add_link_options(${san_flags})
endif()

9. CI integration

# GitHub Actions example
- name: Build with ASan+UBSan
  run: |
    cmake -S . -B build -DSANITIZE=ON
    cmake --build build -j$(nproc)

- name: Run tests under sanitizers
  run: |
    ASAN_OPTIONS=abort_on_error=1:detect_leaks=1 \
    UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
    ctest --test-dir build -j$(nproc) --output-on-failure

For a quick flag reference, see references/flags.md. For report interpretation examples, see references/reports.md.

Related skills

  • Use skills/profilers/valgrind for Memcheck when ASan is unavailable
  • Use skills/runtimes/fuzzing to auto-generate inputs that trigger sanitizer errors
  • Use skills/compilers/gcc or skills/compilers/clang for build flag context
Weekly Installs
1
First Seen
Today
Installed on
mcpjam1
claude-code1
replit1
junie1
windsurf1
zencoder1