AD3037: RustEnableSanitizers¶
Summary¶
| Property | Value |
|---|---|
| ID | AD3037 |
| Name | RustEnableSanitizers |
| Category | Security |
| Severity | Note |
| Applies to | ELF (Linux/Unix) |
Description¶
Sanitizers are powerful dynamic analysis tools that detect various classes of bugs at runtime. For Rust binaries, sanitizers can catch issues that even Rust's strong type system cannot prevent.
Why This Matters¶
Rust's ownership system prevents most memory safety issues in safe code, but sanitizers catch bugs that slip through: unsafe code, FFI boundaries, concurrency issues, and memory leaks that the borrow checker can't detect.
What Sanitizers Catch in Rust¶
| Sanitizer | Catches | Rust Safety Gap |
|---|---|---|
| AddressSanitizer | Buffer overflow, use-after-free | Unsafe blocks |
| MemorySanitizer | Uninitialized reads | FFI return values |
| ThreadSanitizer | Data races | Interior mutability |
| LeakSanitizer | Memory leaks | Rc/Arc cycles |
Unsafe Code Detection¶
unsafe {
let ptr: *mut i32 = some_allocation();
*ptr = 42;
free(ptr);
*ptr = 100; // Use-after-free: ASan catches this!
}
FFI Boundary Issues¶
extern "C" {
fn get_data() -> *mut Data; // What if C returns uninitialized?
}
let data = unsafe { get_data() };
// MSan detects if the returned memory is uninitialized
Concurrency Bugs¶
use std::cell::UnsafeCell;
struct RacyCounter(UnsafeCell<i32>);
unsafe impl Sync for RacyCounter {} // User said it's fine...
// TSan catches the actual data race at runtime
Memory Leaks¶
use std::rc::Rc;
use std::cell::RefCell;
struct Node {
next: Option<Rc<RefCell<Node>>>,
}
// Rc cycle = memory leak
// LSan detects the leak
When to Use Sanitizers¶
| Build Type | Sanitizers | Purpose |
|---|---|---|
| Development | Optional | Catch bugs early |
| CI/CD testing | Recommended | Automated detection |
| Fuzzing | Essential | Find edge cases |
| Production | No | Too much overhead |
Performance Overhead¶
| Sanitizer | Runtime Overhead | Memory Overhead |
|---|---|---|
| ASan | 2x | 2-3x |
| MSan | 3x | 2-3x |
| TSan | 5-15x | 5-10x |
| LSan | Low (on exit) | Low |
CI Integration Value¶
CI Pipeline with Sanitizers:
1. Build with ASan
2. Run test suite
3. Any sanitizer report = build failure
4. Bugs caught before merge
Without Sanitizers:
Bugs may reach production
Only crash reports reveal issues
Sanitizers are valuable for: - AddressSanitizer (ASan): Detects memory errors like buffer overflows, use-after-free, and memory leaks - MemorySanitizer (MSan): Detects uninitialized memory reads - ThreadSanitizer (TSan): Detects data races in multithreaded code - LeakSanitizer (LSan): Detects memory leaks
While Rust's ownership system prevents many memory safety issues, sanitizers are still valuable for:
- Detecting bugs in unsafe code blocks
- Finding issues in FFI calls to C/C++ libraries
- Catching subtle concurrency bugs
- Identifying memory leaks in complex ownership patterns
Note: Sanitizers add significant runtime overhead and should typically only be used in debug/test builds.
Category¶
Security
Resolution¶
Enable sanitizers when building Rust binaries for testing:
# AddressSanitizer
RUSTFLAGS="-Z sanitizer=address" cargo +nightly build
# MemorySanitizer (requires nightly and special setup)
RUSTFLAGS="-Z sanitizer=memory" cargo +nightly build
# ThreadSanitizer
RUSTFLAGS="-Z sanitizer=thread" cargo +nightly build
# LeakSanitizer (part of ASan, or standalone)
RUSTFLAGS="-Z sanitizer=leak" cargo +nightly build
For CI/CD integration:
# GitHub Actions example
- name: Run tests with sanitizers
run: |
RUSTFLAGS="-Z sanitizer=address" cargo +nightly test
Detection¶
This rule detects sanitizers by looking for their runtime library symbols:
| Sanitizer | Detected Symbols |
|---|---|
| AddressSanitizer | __asan_init, __asan_report_* |
| MemorySanitizer | __msan_init, __msan_warning |
| ThreadSanitizer | __tsan_init, __tsan_read, __tsan_write |
| UBSan | __ubsan_handle_* |
| LeakSanitizer | __lsan_init, __lsan_do_leak_check |
| HWAddressSanitizer | __hwasan_init, __hwasan_check |
Applicability¶
This rule applies to:
- ELF binaries compiled with Rust (detected via rustc producer or Rust-specific symbols)
- Both executables and shared libraries
Result Interpretation¶
This rule is informational: - Pass (Note): Reports which sanitizers are detected in the binary - Note: Reports when no sanitizers are detected, with a suggestion to consider using them for test builds
Examples¶
Pass (Sanitizers Detected)¶
Note (No Sanitizers)¶
'myapp' is a Rust binary without sanitizer instrumentation. For debug/test builds,
consider using '-Zsanitizer=address' or other sanitizers to detect memory safety issues.
Notes¶
- Sanitizers require nightly Rust (
cargo +nightly) - Some sanitizers (like MSan) require all dependencies to be built with instrumentation
- ASan typically adds 2-3x memory overhead and slows execution by 2x
- TSan adds significant memory overhead (~5-10x)
- Production binaries should NOT include sanitizer instrumentation
Related Rules¶
- AD5014: EnableAddressSanitizer - Mach-O sanitizer detection
- AD3036: EnableControlFlowIntegrity - CFI for control flow protection
- AD3033: RustEnableCET - Hardware-based control flow protection