rust-unsafe-checker
Skill0 starsChanged 37 days agoMITView on GitHub
Use when writing or reviewing unsafe Rust or FFI.
In this repository15 other files it gives its agents.
- AGENTS.mdAGENTS.md
- rustoriaSkill
- rust-debuggingSkill
- rust-dependenciesSkill
- rust-fundamentalsSkill
- rust-axumSkill
- rust-clapSkill
- rust-dieselSkill
- rust-ratatuiSkill
- rust-rayonSkill
- rust-serdeSkill
- rust-tokioSkill
- rust-productionSkill
- rust-code-auditSkill
- rust-stdSkill
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 1 is distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 103
Every unsafe block must have a // SAFETY: comment stating:
- Distinctive: line 118
Test unsafe invariants: null pointers, misaligned access, partial initialization, aliasing violations — Miri catches what the compiler …
- Very specific: line 140
No blocking in async; no casual unwrap() in production paths.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
rust-unsafe-checker — Unsafe Rust Review
Unsafe Rust is a contract with the compiler: you take responsibility for invariants the compiler normally enforces. This skill is the checklist for writing and reviewing that contract. Authority: The Rustonomicon.
When to use
- Writing or reviewing
unsafeblocks,unsafe fn,unsafe trait,unsafe impl. - FFI boundaries (C ABI,
extern "C",repr(C)). - Raw pointers,
NonNull,MaybeUninit,static mut. - Performance code that reaches for unsafe (atomics, transmute, pointer arithmetic).
The unsafe surface
Minimize it. Every unsafe block should be:
- Necessary — safe Rust cannot express it (FFI, certain layouts, performance-critical primitives).
- Small — wrap a minimal unsafe core in a safe API; never expose
unsafein the public API unless unavoidable. - Documented — a
// SAFETY:comment stating the invariant the caller must uphold. - Reviewed — unsafe code gets extra scrutiny; Miri for validation.
Raw pointers
*const T/*mut T— raw pointers. Creating them is safe; dereferencing isunsafe.- A raw pointer may be null, dangling, misaligned, or alias another pointer's data — the compiler makes no guarantees.
- Dereference only when you can prove: non-null, aligned, initialized, and valid for
Tfor the entire access. - Pointer arithmetic (
offset,add,sub) is safe only within the bounds of the same allocation (or one past the end). NonNull<T>— a non-null raw pointer wrapper; use it in public APIs to encode non-nullness.NonNull::newreturnsOption;new_uncheckedis unsafe.null()/null_mut()for FFI null sentinels;as_ref()/as_mut()areunsafe fn- they check for null and returnOption, but the caller must still guarantee the pointee is valid, aligned, initialized, and lives for the returned lifetime (a dangling pointer passes the null check).
MaybeUninit and initialization
MaybeUninit<T>— a possibly-uninitializedT. Use it for partial initialization, FFI structs, and performance-sensitive construction.MaybeUninit::uninit()creates uninitialized memory;assume_init()is unsafe and valid only after full initialization.write()initializes without dropping the old value;zeroed()is unsafe (not all bit patterns are valid).- Partial initialization: track which fields are initialized; never
assume_inita struct with uninitialized fields. ptr::write,ptr::read,ptr::copy,ptr::copy_nonoverlapping— the safe-ish primitives for manual memory management; each has strict preconditions (alignment, non-overlap, validity).
Layout, alignment, validity
size_of::<T>(),align_of::<T>()— compile-time layout queries.Layout::new::<T>(),Layout::array::<T>(n)— for manual allocation;alloc/deallocare unsafe and require the exact layout used.- Validity invariants: a
boolis 0 or 1; acharis a valid Unicode scalar; references/non-null pointers are aligned and non-null; enums have valid discriminants. Violating these is UB even if the code "works." repr(C)for FFI/ABI layout;repr(transparent)for single-field wrappers;repr(align(N))for alignment. Defaultrepr(Rust)has no layout guarantee.transmuteis almost never the answer — it reinterprets bits with no validity check. PreferFrom/TryFrom,ptr::read, or explicit bit manipulation.
Aliasing and provenance
- Rust's aliasing rules (Stacked Borrows / Tree Borrows, enforced by Miri): a
&mut Tmust be the only live path to its data;&Tmay alias other&Tbut not&mut T. - Creating a
&mutfrom a&(or vice versa) invalidates the original reference — even creating a raw pointer from a&and then writing through it after the&is used again is UB. addr_of!/addr_of_mut!create raw pointers without intermediate references — use them when you need a pointer without forming a reference.- Provenance: pointers carry permission to access;
expose_provenance/with_exposed_provenance(1.84+) for FFI interop. Do not round-trip pointers through integers.
FFI and ABI
extern "C" fn— the C ABI calling convention.#[no_mangle]+pub extern "C"for exported symbols.#[repr(C)]structs for C-compatible layout;#[repr(C, packed)]only when the C header demands it (unaligned reads are UB).CString/CStrfor string boundaries — never pass&strdirectly;CString::newfails on interior nulls.catch_unwindat FFI boundaries — a Rust panic unwinding into C is UB.Send/Syncacross FFI:extern "C"functions are not automatically thread-safe; document thread-safety contracts.bindgenfor generating Rust bindings from C headers;cbindgenfor the reverse.
Send, Sync, and unsafe traits
unsafe trait— the trait itself has invariants implementors must uphold (e.g.,Send,Sync,TrustedLen).unsafe impl— you assert the type upholds the trait's invariant.unsafe impl Send for MyTypeis a promise thatMyTypeis safe to move across threads;unsafe impl Syncthat it's safe to share.Send= safe to move to another thread.Sync= safe to share&Tacross threads.RefCell<T>is!Sync(runtime borrow checking is not thread-safe) butSendwhenT: Send.Rc<T>is neither.static mut- restricted in edition 2024:static_mut_refs(taking references to astatic mut) is deny-by-default (an error unless narrowly#[allow]ed); take raw pointers with&raw const/&raw mutinstead, or useAtomicUsize,OnceLock, orMutex.
unsafe_op_in_unsafe_fn and edition 2024
- Edition 2024 makes
unsafe_op_in_unsafe_fna lint (deny by default): anunsafe fnno longer implies anunsafeblock. Write explicitunsafe {}blocks insideunsafe fn— this is the correct pattern in all editions. - Edition 2024 also:
genblocks (unstable),unsafe externblocks,#[unsafe(no_mangle)]attribute syntax. Prefer edition 2021 unless the project opts into 2024.
SAFETY comments
Every unsafe block must have a // SAFETY: comment stating:
// SAFETY: `ptr` is non-null, aligned, and points to a valid, initialized `T`
// for the duration of this call, and no other references alias it.
unsafe { *ptr = value; }The comment is the contract. If you cannot write a convincing SAFETY comment, the unsafe is not justified.
Miri and testing unsafe
- Miri — an interpreter that detects UB (aliasing violations, invalid values,
uninitialized reads). Run
cargo +nightly miri teston unsafe code. - Miri is slow and requires nightly; run it on the unsafe core, not the whole workspace.
- Test unsafe invariants: null pointers, misaligned access, partial initialization, aliasing violations — Miri catches what the compiler cannot.
cargo fuzz(libFuzzer) for fuzzing unsafe parsers and FFI boundaries.- Property tests for unsafe data structures (invariants hold under arbitrary operation sequences).
Review checklist
- Is the unsafe necessary? Can safe Rust express this?
- Is the unsafe surface minimal and wrapped in a safe API?
- Does every
unsafeblock have a// SAFETY:comment? - Are raw pointers non-null, aligned, initialized, and valid for
T? - Are aliasing rules respected (no
&mutaliasing, no invalidation)? - Is initialization complete before
assume_init? - Are FFI types
repr(C)and ABI-correct? - Are
Send/Syncimplications ofunsafe impljustified? - Is
static mutavoided (edition 2024)? - Has Miri been run on the unsafe core?
- Are unsafe traits/impls documented with their invariants?
House rules
- Native first:
stdbefore crates. - No blocking in async; no casual
unwrap()in production paths. - Version-sensitive advice names its version.
- Examples use edition 2021.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

