A Rust security checklist should focus on what the compiler does not check: every unsafe block, panics reachable from input, integer behavior in release builds, build scripts and macros that run at compile time, and the crates you depend on. Safe Rust prevents memory errors in safe code. The rest still needs review.
What does Rust actually guarantee?
Safe Rust rules out use after free, data races and out-of-bounds access in code that stays inside the safe subset. The Rustonomicon explains the split: safe code is checked by the compiler, while unsafe code carries a contract that the programmer must uphold. A bug inside an unsafe block can break guarantees for code that looks entirely safe.
So the first checklist item is a map of where unsafe lives. Count the blocks, note which crates contain them (yours and dependencies), and give each one an owner.
How should unsafe code be reviewed?
Ask three questions of every unsafe block. What invariant does it assume? Where is that invariant established? Can any safe caller violate it? Put the answer in a comment above the block. Wrap raw pointer and FFI calls in a small safe function with a narrow interface so the unsafe surface is a few lines, not a module.
Foreign function calls deserve extra care. C code gets no help from the borrow checker, so lengths, nullability, lifetimes and thread safety across the boundary are all your job.
Why do panics matter for security?
An unwrap() or expect() on a value shaped by input turns bad data into a panic. The Rust book on error handling describes panics as the way a program stops when it reaches a state it cannot handle. In a long-running server, a reachable panic is a denial of service, unless you isolate it per request. Indexing a slice with an attacker-controlled number panics too.
Use Result, return errors at trust boundaries, and prefer get for indexing with untrusted indices. Reserve unwrap for cases you can prove, with a comment that says why.
What about integer overflow?
In debug builds, arithmetic overflow panics. In release builds it wraps silently unless you enable the overflow-checks profile setting. That difference has hidden bugs that only appear in production binaries. For sizes, lengths and money, use checked_add, checked_mul and friends, or saturating variants when clamping is correct, and convert between integer widths with try_from instead of as.
What runs at build time?
Build scripts (build.rs) and procedural macros are ordinary programs that execute on the developer's or CI machine during compilation, with that user's permissions. A malicious or compromised dependency can use them. Treat a new crate that ships a build script or proc macro as higher risk, and build in an environment without credentials.
How do you manage crates?
Cargo's default version requirement is a caret range, so a plain 1.2 accepts compatible newer releases when the lockfile is updated. Commit Cargo.lock for applications, build with the locked file, and update deliberately. Check dependencies against the RustSec advisory database with cargo audit, and consider cargo deny for license and source rules. Review crates that are new, thinly maintained or near-duplicates of a well-known name.
What to do
- Inventory unsafe blocks in your code and dependencies, and assign owners.
- Require a safety comment on every unsafe block, and wrap FFI in small safe APIs.
- Ban
unwrapandexpecton input-derived values with a lint, and test with malformed input. - Enable overflow checks in release for services that handle sizes or money, or use checked arithmetic.
- Fuzz parsers and decoders.
- Commit the lockfile, build locked, and run
cargo auditin CI. - Review new build scripts and proc macros, and build without secrets.
- Generate an SBOM per release.
Safeguard's dependency scanning reads Cargo.lock and the CLI can run in CI, so advisories surface as part of the same review as code.
Quick check
Is a Rust program memory safe if it has no unsafe blocks?
Your own code, yes, but dependencies may still contain unsafe code, and the standard library uses it internally behind safe interfaces. Memory safety depends on the whole program.
Does cargo audit find zero-day flaws?
No. It matches your dependencies against known advisories in the RustSec database. Unknown flaws need review, fuzzing and monitoring.
Sources
- The Rustonomicon: Meet Safe and Unsafe
- The Rust Programming Language: Error Handling
- The Cargo Book: Specifying Dependencies
- RustSec Advisory Database
Last reviewed: 18 October 2026.