Rust's safety guarantees only apply to safe Rust. Unsafe Rust introduces raw pointers, mutable statics, FFI, and inline assembly — where memory safety is the programmer's responsibility. This note covers how to audit unsafe code, detect soundness bugs, fuzz for vulnerabilities, and understand real CVEs — for security researchers, pentesters auditing Rust codebases, and engineering teams hardening production Rust.
Zero-cost abstraksi (Vec, HashMap, Arc — semua pake unsafe internals)
SIMD intrinsics yang tidak di-cover compiler
Kernel/driver programming
Embedded systems (register mapping)
2. Soundness Bugs — When Safe Wrappers Lie
Soundness adalah properti: safe API tidak boleh menyebabkan UB, bahkan dengan input yang tidak valid. Jika safe function memanggil unsafe code dan input tertentu menyebabkan UB, itu soundness bug — severity tinggi (CVSS 7+).
2.1 Pattern: Lifetime Extension via Unsafe
// UNSOUND — contoh klasikstruct Ref<'a>(&'a str);unsafe impl<'a> Send for Ref<'a> {} // Send tanpa memastikan thread-safety// Eksploitasi:fn unsound() { let s = Ref(&"hello"); std::thread::spawn(move || { println!("{}", s.0); // use-after-free jika outer scope drop });}
2.2 Pattern: Provenance Violation
// UNSOUND — integer to pointer provenanceunsafe { let addr: usize = 0xdeadbeef; let ptr = addr as *const i32; // provenance tidak jelas println!("{}", *ptr); // UB: invalid provenance}
2.3 Pattern: Buffer Overflow via Incorrect Bounds
// UNSOUND — pada library ekstensi Rust-C#[no_mangle]pub unsafe extern "C" fn process(data: *const u8, len: usize) { // Tidak ada bounds check! let slice = std::slice::from_raw_parts(data, len); // Jika len > actual allocation → buffer overflow}
Audit tip: Cari unsafe fn yang parameter-nya *const T + len tanpa validasi — ini hotspot soundness bug.
3. Undefined Behavior in Rust
UB Type
Contoh
Detection
Null pointer deref
*ptr = 1 di mana ptr.is_null()
Miri, ASan
Buffer overflow
slice::from_raw_parts dengan len > actual
ASan, cargo-fuzz
Use-after-free
Drop lalu pakai ref via raw pointer
Miri (-Zmiri-tag-raw-pointers)
Data race
Send diimplementasikan tanpa sync
ThreadSanitizer (TSan)
Invalid alignment
core::mem::transmute::<[u8;3], u32>
Miri, alignment check
Mutable aliasing
&mut dan & ke memory sama
Miri (-Zmiri-check-aliasing)
Incorrect enum discriminant
core::mem::transmute ke enum invalid
Miri, validation check
# UB detection pipelinecargo miri test # dynamic UB detectioncargo test --target x86_64-unknown-linux-gnu -- --test-threads=1 # race detectionRUSTFLAGS="-Z sanitizer=address" cargo test # ASan
4. Code Review Checklist — 15-Point Unsafe Audit
Critical (Severity: Critical)
#
Check
Rationale
1
unsafe fn atau unsafe impl tidak memvalidasi parameter?
Buffer overflow/UB via safe wrapper
2
Raw pointer di-dereference tanpa null check?
Null pointer UB
3
slice::from_raw_parts dengan len dari external?
Buffer overflow
4
transmute antara tipe berbeda size?
Undefined layout
5
Send/Sync diimplementasikan untuk tipe dengan interior mutability?
Data race
High (Severity: High)
#
Check
Rationale
6
unsafe block terlalu besar (wrap seluruh function)?
Missed invariant
7
*const T → *mut T cast?
Mutable aliasing
8
FFI function extern "C" tanpa #[no_mangle] check?
Symbol collision
9
Pin<P> guarantee tidak dipertahankan?
Self-referential struct corruption
10
ManuallyDrop digunakan dengan benar?
Double free
Medium (Severity: Medium)
#
Check
Rationale
11
std::mem::zeroed() untuk tipe non-zero?
Invalid bit pattern
12
#[repr(packed)] di struct dengan reference?
Misalignment UB
13
GlobalAlloc implementasi mengikuti contract?
Heap corruption
14
#![deny(unsafe_op_in_unsafe_fn)] ada?
Unsafe call dalam unsafe fn
15
inline(always) pada function dengan asm?
Compiler misoptimization
# Tool: cargo-geiger — hitung jumlah unsafe usagecargo install cargo-geigercargo geiger --all-features# Output: Safety Report — total unsafe functions, unsafe blocks per crate
5. Fuzzing Rust — cargo-fuzz & libfuzzer
5.1 Setup
# Install cargo-fuzzcargo install cargo-fuzz# Init fuzz target di projectcargo fuzz init# Buat fuzz target di fuzz_targets/fuzz_target_1.rs# Add seed corpusmkdir fuzz/corpus/fuzz_target_1/echo "seed_input" > fuzz/corpus/fuzz_target_1/seed.txt
5.2 Fuzz Target Example
// fuzz_targets/fuzz_target_1.rs#![no_main]use libfuzzer_sys::fuzz_target;fuzz_target!(|data: &[u8]| { // Panic = bug found if data.len() < 4 { return; } let len = u32::from_ne_bytes([data[0], data[1], data[2], data[3]]) as usize; if len > data.len().saturating_sub(4) { return; // guard prevent overflow — test our guard logic } let payload = &data[4..4+len]; // Call unsafe function dengan fuzzed input unsafe { process_raw(payload.as_ptr(), payload.len()); }});
5.3 Run Fuzzer
# Run dengan 4 corecargo fuzz run fuzz_target_1 --jobs 4# Dengan sanitizer (ASan + UBSan)RUSTFLAGS="-Z sanitizer=address -Z sanitizer=undefined" cargo fuzz run fuzz_target_1# Coverage reportcargo fuzz coverage fuzz_target_1# Buka hasil di: fuzz/coverage/fuzz_target_1/coverage/index.html# Minimize crash inputcargo fuzz tmin fuzz_target_1 crash-xxxxxxxx
Fuzzing strategy untuk unsafe code:
Fuzz from_raw_parts dengan panjang random (0..4096)
Fuzz transmute dengan invalid discriminant values
Fuzz Offset pada raw pointer dengan large offset
Fuzz FFI boundary — kirim malformed data ke C library
6. Miri — MIR Interpreter for UB Detection
Miri menjalankan Rust code di interpreted MIR level — bisa mendeteksi UB yang tidak terdeteksi runtime biasa.
# Basic Miri testcargo +nightly miri test# Miri dengan raw pointer provenance trackingMIRIFLAGS="-Zmiri-tag-raw-pointers" cargo +nightly miri test# Miri dengan strict provenanceMIRIFLAGS="-Zmiri-strict-provenance" cargo +nightly miri test# Miri backtrace on UBMIRIFLAGS="-Zmiri-backtrace=1" cargo +nightly miri test# Run specific testcargo +nightly miri run --example my_example
# Cargo.toml — disable unsafe di seluruh crate[package]# Tidak bisa disable unsafe per-crate — tapi bisa allow di Cargo# Alternatif: #![forbid(unsafe_code)] di lib.rs# Guns safe subsetcargo-geiger --all-features # audit all unsafecargo-deny --offline # deny crates with unsafe > threshold