
Sanitizers
- 415 installs
- 155 repo stars
- Updated June 27, 2026
- mohitmishra786/low-level-dev-skills
sanitizers is a Claude Code skill that enables AddressSanitizer, ThreadSanitizer, and UndefinedBehaviorSanitizer in Rust and C/C++ CI runs to catch memory corruption, data races, and undefined behavior for developers shi
About
sanitizers is a low-level development skill from mohitmishra786/low-level-dev-skills that configures AddressSanitizer (ASan), ThreadSanitizer (TSan), and UndefinedBehaviorSanitizer (UBSan) for Rust and C/C++ CI pipelines. The skill helps developers add compiler and linker flags, run sanitizer-instrumented test jobs, and interpret crash reports for heap overflows, use-after-free, data races, and undefined behavior before release. Developers reach for sanitizers when hardening native services, FFI boundaries, or performance-critical C/C++ and Rust codebases that need automated memory and concurrency defect detection in CI. It targets the gap between unit tests and production crashes caused by UB and threading bugs.
- ASan, TSan, UBSan, and MSan flag recipes
- Cargo RUSTFLAGS and C/C++ CMAKE integration
- False positive triage and suppressions
- CI matrix jobs with sanitizer builds
- Interplay with Miri for Rust-specific checks
Sanitizers by the numbers
- 415 all-time installs (skills.sh)
- +30 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #646 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill sanitizersAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 415 |
|---|---|
| repo stars | ★ 155 |
| Last updated | June 27, 2026 |
| Repository | mohitmishra786/low-level-dev-skills ↗ |
How do you enable sanitizers in Rust and C++ CI?
Enable AddressSanitizer, ThreadSanitizer, and UndefinedBehaviorSanitizer in Rust and C/C++ CI runs to catch memory corruption, races, and UB before production release.
Who is it for?
C, C++, and Rust developers adding pre-release sanitizer CI jobs to native codebases with memory-safety or concurrency risk.
Skip if: Pure interpreted-language projects or teams with no native Rust/C/C++ binaries to instrument in CI.
When should I use this skill?
A developer asks to add AddressSanitizer, ThreadSanitizer, or UBSan to CI, debug sanitizer crashes, or catch races and UB before release.
What you get
Sanitizer-enabled CI job configs, compiler flag sets, and crash reports flagging memory, race, and UB defects.
- sanitizer CI configuration
- compiler flag reference
- crash interpretation notes
By the numbers
- Covers three sanitizers: AddressSanitizer, ThreadSanitizer, and UndefinedBehaviorSanitizer
Files
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 MSan2. 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.cRuntime options (via ASAN_OPTIONS):
ASAN_OPTIONS=detect_leaks=1:abort_on_error=1:log_path=/tmp/asan.log ./progASAN_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:10Reading: 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.cCommon UBSan checks:
signed-integer-overflowunsigned-integer-overflow(not inundefinedby default)null— null pointer dereferencebounds— array index OOB (compile-time knowable bounds)alignment— misaligned pointer accessfloat-cast-overflow— float-to-int conversion overflowvptr— C++ vtable type mismatchshift-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 MSanInterpreting 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:35. 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.cSystem 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.cDo 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 ./prog8. 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-failureFor a quick flag reference, see references/flags.md. For report interpretation examples, see references/reports.md.
Related skills
- Use
skills/profilers/valgrindfor Memcheck when ASan is unavailable - Use
skills/runtimes/fuzzingto auto-generate inputs that trigger sanitizer errors - Use
skills/compilers/gccorskills/compilers/clangfor build flag context
Sanitizer Flags Reference
Source: <https://clang.llvm.org/docs/UsersManual.html#controlling-code-generation> Source: <https://clang.llvm.org/docs/AddressSanitizer.html> Source: <https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html> Source: <https://clang.llvm.org/docs/ThreadSanitizer.html> Source: <https://clang.llvm.org/docs/MemorySanitizer.html>
Quick reference table
| Sanitizer | Flag | GCC | Clang | Notes |
|---|---|---|---|---|
| ASan | -fsanitize=address | 4.8+ | 3.1+ | |
| UBSan | -fsanitize=undefined | 4.9+ | 3.3+ | |
| TSan | -fsanitize=thread | 4.8+ | 3.1+ | Incompatible with ASan/MSan |
| MSan | -fsanitize=memory | No | 3.3+ | All-clang build required |
| LSan | -fsanitize=leak | 4.8+ | 3.1+ | Standalone or via ASan |
| CFI | -fsanitize=cfi-* | No | LTO required |
Compiler flags
Required alongside sanitizer flags
-fno-omit-frame-pointer # accurate stack traces in ASan
-g # source locations in reports
-O1 # recommended: keeps code reasonable, better than -O0Recovery control
-fno-sanitize-recover=all # abort on first error (CI recommended)
-fno-sanitize-recover=undefined # abort only on UBSan errors
-fsanitize-recover=all # continue after errors (log all)Individual UBSan checks
# Enable all undefined behaviour
-fsanitize=undefined
# Specific checks (can add to -fsanitize=undefined)
-fsanitize=integer # overflow, division, shift
-fsanitize=signed-integer-overflow
-fsanitize=unsigned-integer-overflow # not in 'undefined' by default
-fsanitize=float-divide-by-zero
-fsanitize=float-cast-overflow
-fsanitize=null
-fsanitize=alignment
-fsanitize=bounds # array index (compile-time bounds only)
-fsanitize=vptr # C++ virtual call type mismatch
-fsanitize=pointer-overflow
-fsanitize=builtin # __builtin_* misuseASan-specific options
-fsanitize-address-use-after-scope # detect use-after-scope bugs
-fsanitize-address-use-after-return # detect use-after-return (slow)Runtime options (environment variables)
ASAN_OPTIONS
ASAN_OPTIONS=key=value:key2=value2 ./prog| Key | Default | Effect |
|---|---|---|
detect_leaks | 1 | Enable LeakSanitizer |
abort_on_error | 0 | abort() instead of _exit() |
exitcode | 1 | Exit code on error |
log_path | stderr | Write report here |
symbolize | 1 | Symbolize (needs llvm-symbolizer) |
fast_unwind_on_malloc | 1 | Fast (less accurate) stack traces |
quarantine_size_mb | 256 | Delay reuse of freed memory |
max_uar_stack_size_log | 20 | Use-after-return stack size |
handle_segv | 1 | ASan handles SIGSEGV |
print_stats | 0 | Print memory stats on exit |
check_initialization_order | 0 | Detect initialisation order bugs |
UBSAN_OPTIONS
| Key | Effect |
|---|---|
print_stacktrace=1 | Include stack trace |
halt_on_error=1 | Stop on first error |
suppressions=file | Load suppression file |
log_path=file | Write to file |
TSAN_OPTIONS
| Key | Effect |
|---|---|
history_size=N | Memory access history (0-7, default 1) |
halt_on_error=1 | Stop on first race |
suppressions=file | Suppress known races |
report_signal_unsafe=0 | Suppress signal-handler race warnings |
LSAN_OPTIONS
| Key | Effect |
|---|---|
suppressions=file | Suppress known leaks |
report_objects=1 | Print leaked object addresses |
max_leaks=N | Max leaks to report |
Sanitizer Report Interpretation
ASan report types
heap-buffer-overflow
ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000050
WRITE of size 4 at 0x602000000050 thread T0
#0 0x401234 in write_past_end main.c:12
#1 0x401567 in main main.c:40
0x602000000050 is located 0 bytes after a 40-byte region
allocated by thread T0 here:
#0 0x7f... in malloc
#1 0x401400 in main main.c:8Diagnosis: Frame #0 in the first stack is where the OOB write happened (line 12). The allocation was at line 8 with size 40 bytes. Access is 0 bytes after the region end — classic off-by-one.
Fix pattern: Check loop bounds; ensure i < n not i <= n; check malloc(n * sizeof(T)) includes all elements.
---
use-after-free
ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000050
READ of size 8 at 0x602000000050 thread T0
#0 0x401234 in use_ptr main.c:20
0x602000000050 is located 8 bytes inside a 40-byte region
freed by thread T0 here:
#0 0x7f... in free
#1 0x401300 in cleanup main.c:15
allocated by thread T0 here:
#0 0x7f... in malloc
#1 0x401200 in init main.c:5Diagnosis: Pointer freed at line 15, read again at line 20. Find ownership mistake between cleanup and use_ptr.
---
stack-buffer-overflow
ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff...
WRITE of size 1 at 0x7fff... thread T0
#0 0x401234 in foo main.c:8
Address 0x7fff... is located at offset 28 in frame <main.c:5:foo>
This frame has 1 object(s):
[0, 28) 'buf' (line 6)Diagnosis: 28-byte stack array buf, write at offset 28 = one past the end. Check gets(), strcpy(), sprintf().
---
double-free
ERROR: AddressSanitizer: attempting double-free on 0x602000000050
#0 0x401234 in bad_free main.c:25
allocated here:
...
freed here (1st time):
...
freed here (2nd time / current):
#0 0x401234 in bad_free main.c:25---
UBSan report types
signed-integer-overflow
src/main.c:15:12: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'Fix: use int64_t or check for overflow before the operation.
null pointer dereference
src/main.c:20:3: runtime error: member access within null pointer of type 'struct Foo'Fix: check pointer != NULL before deref.
shift exponent too large
src/main.c:8:14: runtime error: shift exponent 32 is too large for 32-bit type 'int'Fix: cast to uint64_t before shifting, or use __builtin_expect.
misaligned access
src/main.c:12:10: runtime error: load of misaligned address 0x... for type 'int', which requires 4-byte alignmentFix: use memcpy to read unaligned data; or ensure pointer is correctly aligned.
---
TSan report types
data race
WARNING: ThreadSanitizer: data race (pid=12345)
Write of size 4 at 0x7f... by thread T2:
#0 counter_increment counter.c:8
Previous read of size 4 at 0x7f... by thread T1:
#0 counter_read counter.c:3
Location is global 'g_counter' of size 4 at 0x... (prog+0x...)Diagnosis: g_counter is written by T2 and read by T1 without synchronisation. Fix: Add mutex, atomic, or use __atomic_* / std::atomic.
lock order inversion (deadlock risk)
WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock)
Mutex M1 acquired here while holding M2:
Mutex M2 acquired here while holding M1:Fix: establish a global lock ordering; always acquire M1 before M2.
---
LSan report
==12345==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 40 byte(s) in 1 object(s) allocated from:
#0 0x7f... in malloc
#1 0x401234 in create_thing main.c:10
#2 0x401567 in main main.c:35
SUMMARY: AddressSanitizer: 40 byte(s) leaked in 1 allocation(s).Diagnosis: create_thing allocates 40 bytes that are never freed. Trace who owns the returned object and should call free().
Related skills
FAQ
Which sanitizers does the sanitizers skill configure?
The sanitizers skill configures AddressSanitizer, ThreadSanitizer, and UndefinedBehaviorSanitizer for Rust and C/C++ CI runs to detect memory corruption, data races, and undefined behavior before production release.
When should developers run sanitizers in CI?
Developers should run sanitizers in CI before shipping native Rust or C/C++ code when memory bugs, data races, or undefined behavior would be costly in production and unit tests alone are insufficient.