Rust source review
When it applies
Reviewing Rust (a service, a CLI, a library). Safe Rust removes most memory bugs, so the review
focuses on the places safety is opted out of, the ways Rust code still panics or mis-handles input,
and ordinary injection/logic in web handlers.
Why it works
Rust's guarantees hold only outside unsafe and only for memory safety — they don't stop SQL built
by string, a command run via a shell, a .unwrap() that panics on attacker input (DoS), or a logic
error. Concentrating on those boundaries finds the real bugs efficiently.
Sinks & patterns (grep, then reason about the boundary)
unsafe blocks: every one is a manual proof obligation — raw pointer deref, get_unchecked,
slice::from_raw_parts, mem::transmute, uninitialised memory. Check the invariant it assumes.
- FFI:
extern "C"/bindgen boundaries — lengths, lifetimes, and NUL handling across the C edge
(the C side has none of Rust's guarantees; pair with code-review-cpp).
- Panics as DoS:
unwrap/expect/panic!/indexing v[i]/unreachable! on
attacker-controlled input; integer as casts that truncate; arithmetic overflow (panics in debug,
wraps in release — both can be bugs). Prefer ?/checked ops.
- Injection: SQL via
format! into a query instead of parameter binding (sqlx/diesel);
std::process::Command with sh -c and concatenated input; Command arg vs shell form.
- Path / SSRF: user paths joined without canonicalisation + prefix check; HTTP clients (reqwest)
fetching user URLs.
- Deserialization / web:
serde into types from untrusted data (resource exhaustion, unexpected
variants); actix/axum/rocket extractors bound to over-broad structs (mass assignment); missing
auth middleware on state-changing routes.
Method
- Run
cargo audit (known-vuln deps), cargo clippy, and semgrep; miri for unsafe UB where feasible.
rg 'unsafe|unwrap\(\)|expect\(|transmute|Command::new|format!\(.*(SELECT|INSERT|UPDATE)' → review each.
- Justify every
unsafe block's invariant; if you can't, that's a finding.
- Confirm exploitable injection/logic with the matching runtime skill.
Gotchas
- Idiomatic Rust is genuinely safe — don't invent memory bugs in safe code; spend effort on
unsafe,
FFI, panics, deps, and logic.
- Release-mode integer overflow wraps silently — a
checked_*/saturating_* audit matters for
size/index math.
cargo audit flags vulnerable crates you'd never see by reading — always run it.
References
Rustonomicon (unsafe); RustSec advisory DB / cargo-audit; Clippy lint set; Secure Rust Guidelines (ANSSI).
1---2name: code-review-rust3description: Security review of Rust code — where a memory-safe language still has real bugs: `unsafe`, FFI, panics, and the usual injection/logic sinks. Load when reviewing Rust source/PR, on .rs / Cargo.toml in scope, or "review this Rust". Signals: Cargo.toml, unsafe blocks, extern "C", unwrap/expect, actix/axum/rocket handlers.4---56# Rust source review78## When it applies9Reviewing Rust (a service, a CLI, a library). Safe Rust removes most memory bugs, so the review10focuses on the places safety is opted out of, the ways Rust code still panics or mis-handles input,11and ordinary injection/logic in web handlers.1213## Why it works14Rust's guarantees hold only outside `unsafe` and only for memory safety — they don't stop SQL built15by string, a command run via a shell, a `.unwrap()` that panics on attacker input (DoS), or a logic16error. Concentrating on those boundaries finds the real bugs efficiently.1718## Sinks & patterns (grep, then reason about the boundary)19- **`unsafe` blocks**: every one is a manual proof obligation — raw pointer deref, `get_unchecked`,20 `slice::from_raw_parts`, `mem::transmute`, uninitialised memory. Check the invariant it assumes.21- **FFI**: `extern "C"`/`bindgen` boundaries — lengths, lifetimes, and NUL handling across the C edge22 (the C side has none of Rust's guarantees; pair with `code-review-cpp`).23- **Panics as DoS**: `unwrap`/`expect`/`panic!`/indexing `v[i]`/`unreachable!` on24 attacker-controlled input; integer `as` casts that truncate; arithmetic overflow (panics in debug,25 wraps in release — both can be bugs). Prefer `?`/checked ops.26- **Injection**: SQL via `format!` into a query instead of parameter binding (sqlx/diesel);27 `std::process::Command` with `sh -c` and concatenated input; `Command` arg vs shell form.28- **Path / SSRF**: user paths joined without canonicalisation + prefix check; HTTP clients (reqwest)29 fetching user URLs.30- **Deserialization / web**: `serde` into types from untrusted data (resource exhaustion, unexpected31 variants); actix/axum/rocket extractors bound to over-broad structs (mass assignment); missing32 auth middleware on state-changing routes.3334## Method351. Run `cargo audit` (known-vuln deps), `cargo clippy`, and `semgrep`; `miri` for `unsafe` UB where feasible.362. `rg 'unsafe|unwrap\(\)|expect\(|transmute|Command::new|format!\(.*(SELECT|INSERT|UPDATE)'` → review each.373. Justify every `unsafe` block's invariant; if you can't, that's a finding.384. Confirm exploitable injection/logic with the matching runtime skill.3940## Gotchas41- Idiomatic Rust is genuinely safe — don't invent memory bugs in safe code; spend effort on `unsafe`,42 FFI, panics, deps, and logic.43- Release-mode integer overflow wraps silently — a `checked_*`/`saturating_*` audit matters for44 size/index math.45- `cargo audit` flags vulnerable crates you'd never see by reading — always run it.4647## References48Rustonomicon (unsafe); RustSec advisory DB / cargo-audit; Clippy lint set; Secure Rust Guidelines (ANSSI).