
Rust Sanitizers Miri
- 308 installs
- 155 repo stars
- Updated June 27, 2026
- mohitmishra786/low-level-dev-skills
Run Miri and LLVM sanitizers on unsafe or FFI-heavy Rust to catch UB, data races, and memory errors before merging low-level systems code.
About
Teaches running Miri and LLVM sanitizers on Rust crates to detect undefined behavior, invalid references, and data races in unsafe, FFI, and performance-critical code paths prior to shipping.
- Miri for stacked borrows and UB
- AddressSanitizer/ThreadSanitizer flags
- unsafe block audit checklist
- FFI boundary test harnesses
- CI integration for nightly toolchains
Rust Sanitizers Miri by the numbers
- 308 all-time installs (skills.sh)
- +26 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #38 of 121 Rust 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 rust-sanitizers-miriAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 308 |
|---|---|
| repo stars | ★ 155 |
| Last updated | June 27, 2026 |
| Repository | mohitmishra786/low-level-dev-skills ↗ |
What it does
Run Miri and LLVM sanitizers on unsafe or FFI-heavy Rust to catch UB, data races, and memory errors before merging low-level systems code.
Files
Rust Sanitizers and Miri
Purpose
Guide agents through runtime safety validation for Rust: ASan/TSan/MSan/UBSan via RUSTFLAGS, Miri for compile-time UB detection in unsafe code, and interpreting sanitizer reports.
Triggers
- "How do I run AddressSanitizer on Rust code?"
- "How do I use Miri to check my unsafe Rust?"
- "How do I run ThreadSanitizer on a Rust program?"
- "My unsafe Rust might have UB — how do I detect it?"
- "How do I interpret a Rust ASan report?"
- "Can I run Rust sanitizers on stable?"
Workflow
1. Sanitizers in Rust (nightly required)
Rust sanitizers require nightly and a compatible platform:
# Install nightly
rustup toolchain install nightly
rustup component add rust-src --toolchain nightly
# AddressSanitizer (Linux, macOS)
RUSTFLAGS="-Z sanitizer=address" \
cargo +nightly test -Zbuild-std \
--target x86_64-unknown-linux-gnu
# ThreadSanitizer (Linux)
RUSTFLAGS="-Z sanitizer=thread" \
cargo +nightly test -Zbuild-std \
--target x86_64-unknown-linux-gnu
# MemorySanitizer (Linux, requires all-instrumented build)
RUSTFLAGS="-Z sanitizer=memory -Zsanitizer-memory-track-origins" \
cargo +nightly test -Zbuild-std \
--target x86_64-unknown-linux-gnu
# UndefinedBehaviorSanitizer
RUSTFLAGS="-Z sanitizer=undefined" \
cargo +nightly test -Zbuild-std \
--target x86_64-unknown-linux-gnu-Zbuild-std rebuilds the standard library with the sanitizer, which is necessary for accurate results.
2. Stable sanitizer workaround
For stable Rust, use the cross tool with a Docker image that has sanitizers pre-configured, or run cargo test inside a Docker container with a nightly image.
Alternatively, for simpler UB checking without nightly:
# cargo-sanitize (wrapper)
cargo install cargo-sanitize
cargo sanitize address3. Interpreting ASan output in Rust
==12345==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000050
READ of size 4 at 0x602000000050 thread T0
#0 0x401234 in myapp::module::function /src/main.rs:15
#1 0x401567 in myapp::main /src/main.rs:42
0x602000000050 is located 0 bytes after a 40-byte region allocated at:
#0 0x... in alloc::alloc::alloc ...
#1 0x... in myapp::create_buffer /src/main.rs:10Rust-specific patterns:
| ASan error | Likely Rust cause |
|---|---|
heap-buffer-overflow | unsafe slice access past bounds |
use-after-free | unsafe pointer use after Vec realloc |
stack-use-after-return | Returning reference to local |
heap-use-after-free | Use after drop() or Box::from_raw |
4. Miri — interpreter for undefined behaviour
Miri interprets Rust MIR and detects UB that sanitizers might miss:
# Install Miri (requires nightly)
rustup +nightly component add miri
# Run tests under Miri
cargo +nightly miri test
# Run specific test
cargo +nightly miri test test_name
# Run a binary under Miri
cargo +nightly miri run
# Run with Stacked Borrows model (strict aliasing)
MIRIFLAGS="-Zmiri-strict-provenance" cargo +nightly miri test
# Disable isolation (allow file I/O, randomness)
MIRIFLAGS="-Zmiri-disable-isolation" cargo +nightly miri test5. What Miri detects
// 1. Dangling pointer use
unsafe {
let x = Box::new(42);
let ptr = Box::into_raw(x);
let _ = Box::from_raw(ptr); // drop
let _val = *ptr; // Miri: use of dangling pointer
}
// 2. Invalid enum discriminant
let x: u8 = 3;
let e = unsafe { std::mem::transmute::<u8, MyEnum>(x) };
// Miri: enum value has invalid tag
// 3. Uninitialized memory read
let uninit: MaybeUninit<u32> = MaybeUninit::uninit();
let val = unsafe { uninit.assume_init() }; // Miri: reading uninitialized bytes
// 4. Stacked borrows violation
let mut x = 5u32;
let ptr = &mut x as *mut u32;
let _ref = &x; // shared reference
unsafe { *ptr = 10; } // Miri: mutable access while shared borrow exists
// 5. Data races (with threads)
// Miri simulates sequential execution and detects races via Stacked Borrows6. ThreadSanitizer for Rust
RUSTFLAGS="-Z sanitizer=thread" \
RUST_TEST_THREADS=8 \
cargo +nightly test -Zbuild-std \
--target x86_64-unknown-linux-gnu 2>&1 | head -50TSan output:
WARNING: ThreadSanitizer: data race (pid=12345)
Write of size 4 at 0x7f... by thread T2 (mutexes: write M1):
#0 myapp::counter::increment src/counter.rs:10
Previous read of size 4 at 0x7f... by thread T1:
#0 myapp::counter::get src/counter.rs:57. Miri configuration via MIRIFLAGS
| Flag | Effect |
|---|---|
-Zmiri-disable-isolation | Allow I/O, clock, randomness |
-Zmiri-strict-provenance | Strict pointer provenance (stricter than LLVM) |
-Zmiri-symbolic-alignment-check | Stricter alignment checking |
-Zmiri-check-number-validity | Check float/int validity |
-Zmiri-num-cpus=N | Simulate N CPUs (for concurrency) |
-Zmiri-seed=N | Seed for random scheduling |
-Zmiri-ignore-leaks | Suppress memory leak errors |
-Zmiri-tag-raw-pointers | Track raw pointer provenance |
8. CI integration
# GitHub Actions
- name: Miri
run: |
rustup toolchain install nightly
rustup +nightly component add miri
cargo +nightly miri test
env:
MIRIFLAGS: "-Zmiri-disable-isolation"
- name: ASan (nightly)
run: |
rustup component add rust-src --toolchain nightly
RUSTFLAGS="-Z sanitizer=address" \
cargo +nightly test -Zbuild-std \
--target x86_64-unknown-linux-gnuRelated skills
- Use
skills/rust/rust-debuggingfor GDB/LLDB debugging of Rust panics - Use
skills/runtimes/sanitizersfor C/C++ sanitizer usage and comparison - Use
skills/rust/rust-unsafefor unsafe Rust patterns and review checklist - Use
skills/runtimes/fuzzingto generate inputs that trigger sanitizer errors
Miri UB Patterns Reference
Undefined Behaviour Caught by Miri
Pointer provenance violations
// Wrong: reusing pointer after reallocation
let mut v: Vec<u32> = Vec::with_capacity(4);
let ptr = v.as_ptr();
v.push(1); v.push(2); v.push(3); v.push(4);
v.push(5); // triggers reallocation
let val = unsafe { *ptr }; // UB: dangling pointer after realloc
// Miri: pointer must be in-bounds at offset 0
// Correct: re-derive pointer after push
v.push(5);
let ptr = v.as_ptr(); // fresh pointerTransmutation errors
// UB: invalid enum discriminant
#[repr(u8)]
enum Color { Red = 0, Green = 1, Blue = 2 }
let x: u8 = 99;
let c = unsafe { std::mem::transmute::<u8, Color>(x) }; // UB
// Miri: enum value has invalid tag
// UB: bool with non-0/1 value
let x: u8 = 2;
let b = unsafe { std::mem::transmute::<u8, bool>(x) }; // UB
// UB: reference to unaligned data
let data = [0u8; 5];
let ptr = data[1..].as_ptr() as *const u32; // misaligned
let val = unsafe { *ptr }; // UB on most platformsStacked Borrows violations
// UB: reborrow violation
let mut x = 5u32;
let raw = &mut x as *mut u32;
let r = unsafe { &mut *raw }; // reborrow of raw ptr
let _ = unsafe { *raw }; // UB: accessing raw while reborrow active
// Miri (strict provenance): tag violation
// Safe pattern: reborrow scope ended before raw access
{
let r = unsafe { &mut *raw };
*r = 10;
}
let _ = unsafe { *raw }; // Now fine — reborrow endedUninitialized memory
use std::mem::MaybeUninit;
// UB: reading before init
let mut uninit: MaybeUninit<u64> = MaybeUninit::uninit();
let ptr = uninit.as_mut_ptr();
let val = unsafe { ptr.read() }; // UB: reading uninitialized
// Miri: using uninitialized data
// Correct: initialize first
unsafe { ptr.write(42) };
let val = unsafe { ptr.read() }; // OK
// UB: partial initialization
let mut buf = MaybeUninit::<[u8; 4]>::uninit();
let ptr = buf.as_mut_ptr() as *mut u8;
unsafe { ptr.write(1) };
// Only first byte initialized; reading all 4 is UB
let arr = unsafe { buf.assume_init() }; // UBLifetime extension
// UB: returning reference to local
fn bad<'a>() -> &'a u32 {
let x = 42u32;
unsafe { &*(&x as *const u32) } // UB: dangling after return
}
// Miri: pointer to alloc is danglingMIRIFLAGS Quick Reference
# Development (most permissive)
MIRIFLAGS="-Zmiri-disable-isolation" cargo +nightly miri test
# CI (strict)
MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-check-number-validity" \
cargo +nightly miri test
# Concurrency testing with randomized scheduling
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-seed=42 -Zmiri-num-cpus=4" \
cargo +nightly miri test
# Ignore intentional leaks (e.g., global objects)
MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" \
cargo +nightly miri testMiri Limitations
Miri cannot run:
- Code using FFI/
extern "C"functions not supported by Miri's shims - Assembly (
asm!) blocks - Platform-specific syscalls not implemented in Miri
- Very long-running programs (interpreter overhead ~100x)
Workarounds:
// Mock FFI functions for Miri testing
#[cfg(not(miri))]
use real_ffi::dangerous_function;
#[cfg(miri)]
fn dangerous_function(x: u32) -> u32 {
x // stub for Miri
}Sanitizer Comparison for Rust
| Tool | Detects | Requires | Overhead |
|---|---|---|---|
| Miri | UB in safe+unsafe Rust | nightly, pure Rust | ~100x |
| ASan | Memory errors at runtime | nightly for Rust build | 2x |
| TSan | Data races at runtime | nightly for Rust build | 5-15x |
| MSan | Uninit reads at runtime | nightly, all-instrumented | 3x |
| UBSan | Integer UB, null, etc. | nightly | <2x |
cargo check | Type, lifetime errors | stable | fast |
| Clippy | Common bug patterns | stable | fast |