Install
openclaw skills install rust-unsafe-auditorAudit Rust code for unsafe block usage — verify safety invariants, check FFI boundaries, review raw pointer operations, validate Send/Sync implementations, a...
openclaw skills install rust-unsafe-auditorDeep audit of unsafe code in Rust projects. Analyzes every unsafe block, function, trait impl, and FFI boundary for soundness. Verifies safety invariants are documented, raw pointer operations are bounded, and Send/Sync implementations are correct.
Use when: reviewing a Rust crate before publishing, auditing dependencies, preparing for security review, or establishing unsafe policies.
cat Cargo.toml 2>/dev/null | head -20
grep -i "libc\|winapi\|bindgen\|cc\|ffi\|sys\b" Cargo.toml 2>/dev/null | head -10
find . -name "*.rs" -not -path '*/target/*' | wc -l
# Project-level unsafe policy
grep -rn 'forbid(unsafe_code)\|deny(unsafe_code)' --include="*.rs" . 2>/dev/null | grep -v 'target/' | head -5
# Census
echo "=== Unsafe Census ==="
echo -n "Blocks: "; grep -rn 'unsafe\s*{' --include="*.rs" . 2>/dev/null | grep -v 'target/' | wc -l
echo -n "Functions: "; grep -rn 'unsafe\s\+fn' --include="*.rs" . 2>/dev/null | grep -v 'target/' | wc -l
echo -n "Trait impls: "; grep -rn 'unsafe\s\+impl' --include="*.rs" . 2>/dev/null | grep -v 'target/' | wc -l
# SAFETY comments (Rust convention)
grep -B1 -A3 'unsafe' --include="*.rs" . 2>/dev/null | grep -i 'SAFETY' | head -20
# Unsafe blocks WITHOUT safety comments
for f in $(grep -rl 'unsafe\s*{' --include="*.rs" . 2>/dev/null | grep -v 'target/'); do
grep -n 'unsafe\s*{' "$f" | while read match; do
line=$(echo "$match" | cut -d: -f1); prev=$((line - 1))
if ! sed -n "${prev}p" "$f" | grep -qi 'safety'; then echo "UNDOCUMENTED: $f:$line"; fi
done
done | head -20
# Unsafe functions without safety docs
for f in $(grep -rl 'unsafe\s\+fn' --include="*.rs" . 2>/dev/null | grep -v 'target/'); do
grep -n 'unsafe\s\+fn' "$f" | while read match; do
line=$(echo "$match" | cut -d: -f1); prev=$((line - 1))
if ! sed -n "${prev}p" "$f" | grep -q '///\|//!'; then echo "UNDOC_FN: $f:$line"; fi
done
done | head -15
Flag:
// SAFETY: comment: every unsafe block must explain why invariants hold (Clippy lint: undocumented_unsafe_blocks)# Safety section on pub unsafe fn: callers need to know the contractgrep -rn 'as \*const\|as \*mut' --include="*.rs" . 2>/dev/null | grep -v 'target/' | head -20
grep -rn '\.offset(\|\.add(\|\.sub(' --include="*.rs" . 2>/dev/null | grep -v 'target/' | head -15
grep -rn 'from_raw\|into_raw\|from_raw_parts' --include="*.rs" . 2>/dev/null | grep -v 'target/' | head -15
grep -rn 'ManuallyDrop\|MaybeUninit\|mem::forget\|mem::transmute' --include="*.rs" . 2>/dev/null | grep -v 'target/' | head -15
For each raw pointer operation, verify:
&T and &mut T to same data simultaneouslyfrom_raw/into_raw pairs must be 1:1 (double-free or leak otherwise)grep -rn 'extern\s*"C"' --include="*.rs" . 2>/dev/null | grep -v 'target/' | head -15
grep -rn '#\[no_mangle\]' --include="*.rs" . 2>/dev/null | grep -v 'target/' | head -10
grep -rn 'CString\|CStr\|c_char\|c_int\|c_void' --include="*.rs" . 2>/dev/null | grep -v 'target/' | head -15
find . -name "bindings.rs" -o -name "*_ffi.rs" -not -path '*/target/*' 2>/dev/null | head -5
Check each FFI boundary for:
extern "C" are UB — must use catch_unwindCString/CStr, check for interior nulls#[repr(C)] required for structs passed to/from Cint is platform-dependent — use c_int, not i32# Manual Send/Sync implementations
grep -rn 'unsafe impl.*Send\|unsafe impl.*Sync' --include="*.rs" . 2>/dev/null | grep -v 'target/' | head -15
# Atomic operations
grep -rn 'AtomicBool\|AtomicUsize\|AtomicPtr\|Ordering::' --include="*.rs" . 2>/dev/null | grep -v 'target/' | head -10
# Transmute (most dangerous operation)
grep -rn 'mem::transmute\|transmute(' --include="*.rs" . 2>/dev/null | grep -v 'target/' | head -10
# mem::zeroed / mem::uninitialized (UB for many types)
grep -rn 'mem::zeroed\|mem::uninitialized' --include="*.rs" . 2>/dev/null | grep -v 'target/' | head -10
# Union types
grep -rn 'union\s\+[A-Z]' --include="*.rs" . 2>/dev/null | grep -v 'target/' | head -5
For Send/Sync, verify:
UnsafeCell makes type !Sync by default)Ordering (common: Relaxed where Acquire/Release needed)For transmute, flag:
0u8 to bool, invalid enum discriminants — instant UBNonNull, bool, &T, enum is UBMaybeUninit# Rust Unsafe Audit — [Crate Name]
## Summary
- Files: N | Edition: 2021 | Unsafe blocks: N | Functions: N | Trait impls: N
- Undocumented unsafe: N (target: 0)
- Audit verdict: PASS / CONDITIONAL / FAIL
## Unsafe Inventory
| # | File:Line | Category | Documented | Verdict |
|---|-----------|----------|-----------|---------|
| 1 | src/lib.rs:45 | Raw pointer deref | Yes | Sound |
| 2 | src/ffi.rs:23 | extern "C" call | No | REVIEW |
| 3 | src/pool.rs:89 | Send impl | Yes | Sound |
| 4 | src/convert.rs:12 | transmute | No | UNSOUND |
## Critical Findings (Potential UB)
### [C1] Transmute Creates Invalid Enum Value
- **File**: src/convert.rs:12
- **Code**: `unsafe { mem::transmute::<u8, MyEnum>(byte) }` — unchecked byte
- **Fix**: Match on byte value, return `Result<MyEnum, InvalidValue>`
### [C2] Panic in extern "C" Callback
- **File**: src/ffi.rs:67
- **Code**: `.unwrap()` in extern "C" fn
- **Fix**: Replace with match + error code, or wrap in `catch_unwind`
## Documentation Gaps
| File | Line | Type | Missing |
|------|------|------|---------|
| src/lib.rs:45 | unsafe block | `// SAFETY:` comment |
| src/ffi.rs:23 | unsafe fn | `# Safety` doc section |
## Recommendations
1. Add `// SAFETY:` comments to N undocumented unsafe blocks
2. Fix N instances of potential UB
3. Add `catch_unwind` to N extern "C" callbacks
4. Run `cargo +nightly miri test` to detect UB dynamically
5. Add `#![deny(unsafe_op_in_unsafe_fn)]` to require scoped unsafe in unsafe fns
6. Consider `#![forbid(unsafe_code)]` for crates that don't need unsafe
| Current Unsafe | Safe Alternative |
|---|---|
mem::transmute for enum conversion | TryFrom<u8> implementation |
| Raw pointer array indexing | slice::get_unchecked (still unsafe but bounds-checkable) |
from_raw_parts for buffer views | bytemuck::cast_slice (safe, zero-cost) |
Manual Send/Sync impl | Wrap inner type in Arc<Mutex<T>> |
cargo +nightly miri test to dynamically detect undefined behaviorcargo clippy -- -W clippy::undocumented_unsafe_blocks to enforce safety commentscargo geiger to count unsafe across the dependency treecargo audit to check for known vulnerabilitiesNonNull<T> over *mut T to encode non-null invariant in the type systembytemuck for safe type punning of POD types#![deny(unsafe_op_in_unsafe_fn)] (Rust 2024 default)