Rust’s type system and ownership model eliminate whole classes of vulnerabilities at compile time - which is exactly why teams assume “it compiles, so it’s safe.” In our AppSec reviews, the borrow checker is never the problem. The findings cluster around the handful of things Rust doesn’t catch for you. Here are the three we flag most often.
Memory Safety by Default🔗
Rust’s ownership system eliminates data races and memory corruption at compile time:
fn safe_example() {
let data = vec![1, 2, 3];
// Compiler prevents use-after-free
process_data(&data);
println!("Data: {:?}", data); // Still accessible
}
fn process_data(data: &Vec<i32>) {
// Borrow checker ensures no data races
for item in data {
println!("{}", item);
}
}The Three We Keep Finding🔗
Even with Rust’s safety guarantees, these are the recurring findings from our reviews:
1. unsafe Blocks Without Documented Invariants🔗
The unsafe keyword bypasses Rust’s safety checks. The problem we see isn’t its use - it’s unsafe blocks with no comment stating why they’re sound, so the next reader (or auditor) can’t verify the invariant still holds:
// Avoid this unless absolutely necessary
unsafe {
// Your code here
}2. Integer Overflow🔗
Rust checks for integer overflow in debug mode but not in release mode by default:
// This will panic in debug mode
let x: u8 = 255;
let y = x + 1; // Wraps to 0 in release mode!Use checked_add(), saturating_add(), or wrapping_add() explicitly.
3. Input Validation🔗
Always validate external input, even in Rust:
fn parse_user_input(input: &str) -> Result<u32, ParseError> {
let value: u32 = input.parse()?;
if value > MAX_ALLOWED {
return Err(ParseError::OutOfRange);
}
Ok(value)
}Key Takeaways🔗
- Use the type system to encode invariants
- Leverage
cargo auditfor dependency scanning - Follow the principle of least privilege
- Test with both debug and release builds
- Document all
unsafeblocks with safety invariants
How We Catch These🔗
On engagements we lean on cargo audit, cargo geiger (to surface unsafe usage across the dependency tree), and Clippy’s pedantic lints in CI - plus a manual pass on every unsafe block and arithmetic path handling untrusted input.
If you want this kind of review on your own codebase, that’s our Application Security service.
Reviewing a Rust codebase and want a second set of eyes? Get in touch.