Skip to content
rusthacks.dev
Security

What Rust Memory Safety Does and Does Not Protect Explained

Safe Rust prevents memory corruption but not logic errors, panics, leaks, deadlocks or overflow. See exactly what is guaranteed, what is not and where unsafe code and dependencies reopen the gap.

3 min read
Summarize in AI
Flat illustration for the post “What Rust Memory Safety Does and Does Not Protect Explained”, composed around memory.

"Memory safe" is a precise term, and the precision is why it is so often misquoted. It means that a specific set of bugs cannot occur in safe code. Everything else still can. Knowing the boundary tells you where to point your review effort. This post draws it with an example for each side. It is part of the threat model in security for Rust developers.

Rust 1.90, edition 2024.

What is guaranteed

In safe code, the compiler and the runtime checks rule out:

  • Use after free and double free (ownership and lifetimes)
  • Out-of-bounds reads and writes (bounds checks panic instead of reading)
  • Data races on shared memory (Send and Sync, explained here)
  • Use of uninitialized memory (all bindings are initialized before use)
  • Null pointer dereference (no null references; Option makes absence explicit)
  • Invalid enum or bool values created by safe code
rust
fn main() {
    let v = vec![1, 2, 3];
    let i = 7;
    println!("{}", v[i]);
}

In C this reads past the buffer. In Rust it panics with index out of bounds: the len is 3 but the index is 7: defined behavior, and no memory is disclosed.

What is not guaranteed

Panics are a denial of service

The panic above stops the thread. If a request handler indexes with a user-supplied number, an attacker can crash workers at will, and with panic = "abort" the whole process. Memory safe, and still a vulnerability.

Leaks are allowed

rust
use std::rc::Rc;
use std::cell::RefCell;

struct Node { next: Option<Rc<RefCell<Node>>> }

fn main() {
    let a = Rc::new(RefCell::new(Node { next: None }));
    let b = Rc::new(RefCell::new(Node { next: Some(a.clone()) }));
    a.borrow_mut().next = Some(b.clone());
}

The cycle is never freed. Rust treats leaks as safe because they cannot corrupt memory. A long running service that leaks per request still dies of memory exhaustion. See Weak for breaking cycles.

Overflow in release builds

Integer arithmetic wraps silently in release by default, and a wrapped length or price is a logic vulnerability. Use checked arithmetic where input controls the numbers: integer overflow bugs in release builds.

Logic and race conditions

Two requests that each check a balance and then withdraw can both succeed. That is a race on application state, not on memory. Deadlocks are likewise safe.

Injection and path bugs

format!("SELECT ... {}", input) is as injectable in Rust as in any language (SQL injection and typed queries). Joining a user path onto a base directory can escape it (path traversal).

Where the guarantee ends

  • **unsafe blocks.** The compiler trusts you (unsafe Rust explained).
  • Dependencies with unsafe. Soundness bugs in crates have been reported through RustSec; check your tree with cargo geiger.
  • FFI. C and C++ libraries have no such guarantees.
  • Compiler bugs. Rare, and tracked as soundness issues.

Using this in review

When reviewing Rust code, skip the questions the compiler answers and spend the time on: reachable panics from input, unbounded allocation, unsafe blocks and their comments, authorization logic, and what the dependencies do. The Rust project's own statement is in the Rustonomicon on safe and unsafe.

Share

Keep reading

Flat illustration for the post “Request Limits and Slow Client Attacks in Rust Services”, composed around web.
Security

Request Limits and Slow Client Attacks in Rust Services

Slow clients and oversized requests exhaust connections and memory. Set header and body timeouts, body size limits, concurrency limits and keep-alive bounds at the proxy and in the Axum service with Tower layers.

5 min read

Get new posts by email

We send one email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.