Rust Patterns

32+ vulnerability patterns for Rust applications focusing on unsafe code, memory safety, and concurrency issues.

Memory Safety by Default

Rust's ownership system prevents many vulnerabilities at compile time. Bloodhound focuses on unsafe blocks, FFI boundaries, and logic errors.

Overview

While Rust provides strong memory safety guarantees, unsafe code blocks, FFI, and logic errors can still introduce vulnerabilities.

6
Critical
10
High
9
Medium
7
Low

Unsafe Code

Critical

Unsafe Transmute

std::mem::transmute can create invalid values or violate memory safety.

Vulnerable
Rust
1// Vulnerable: Transmuting between incompatible types
2unsafe {
3 let bytes: [u8; 4] = get_user_input();
4 let num: i32 = std::mem::transmute(bytes);
5}
Secure
Rust
1// Secure: Use safe conversion methods
2let bytes: [u8; 4] = get_user_input();
3let num = i32::from_ne_bytes(bytes);
Critical

Raw Pointer Dereference

Dereferencing raw pointers without proper validation.

Vulnerable
Rust
1// Vulnerable: Unchecked raw pointer dereference
2unsafe fn get_value(ptr: *const i32) -> i32 {
3 *ptr // May be null or invalid
4}
Secure
Rust
1// Secure: Use Option and references
2fn get_value(opt: Option<&i32>) -> Option<i32> {
3 opt.copied()
4}

Memory Safety

Critical

Use After Free (unsafe)

Accessing memory after it has been freed in unsafe code.

Vulnerable
Rust
1// Vulnerable: Use after free in unsafe block
2unsafe {
3 let ptr = Box::into_raw(Box::new(42));
4 drop(Box::from_raw(ptr));
5 println!("{}", *ptr); // Use after free!
6}
Secure
Rust
1// Secure: Let Rust manage ownership
2let boxed = Box::new(42);
3println!("{}", *boxed);
4// Automatically freed when boxed goes out of scope
Critical

Buffer Overflow

Writing beyond buffer bounds using unsafe slice operations.

Vulnerable
Rust
1// Vulnerable: Unchecked slice indexing
2unsafe {
3 let slice = std::slice::from_raw_parts_mut(ptr, len);
4 slice[user_index] = value; // No bounds check
5}
Secure
Rust
1// Secure: Use checked indexing
2let slice = &mut data[..];
3if let Some(elem) = slice.get_mut(user_index) {
4 *elem = value;
5}

Concurrency Issues

High

Data Race

Concurrent mutable access without proper synchronization.

Vulnerable
Rust
1// Vulnerable: Sharing mutable state unsafely
2static mut COUNTER: i32 = 0;
3
4fn increment() {
5 unsafe { COUNTER += 1; } // Data race!
6}
Secure
Rust
1// Secure: Use atomic types or Mutex
2use std::sync::atomic::{AtomicI32, Ordering};
3static COUNTER: AtomicI32 = AtomicI32::new(0);
4
5fn increment() {
6 COUNTER.fetch_add(1, Ordering::SeqCst);
7}
Medium

Potential Deadlock

Acquiring locks in inconsistent order can cause deadlocks.

Vulnerable
Rust
1// Vulnerable: Inconsistent lock ordering
2fn transfer(from: &Mutex<i32>, to: &Mutex<i32>) {
3 let _a = from.lock().unwrap();
4 let _b = to.lock().unwrap(); // Deadlock if called with swapped args
5}
Secure
Rust
1// Secure: Consistent lock ordering by address
2fn transfer(a: &Mutex<i32>, b: &Mutex<i32>) {
3 let (first, second) = if a as *const _ < b as *const _ {
4 (a, b)
5 } else {
6 (b, a)
7 };
8 let _a = first.lock().unwrap();
9 let _b = second.lock().unwrap();
10}

Cryptographic Errors

High

Weak Random Number Generation

Using non-cryptographic RNG for security-sensitive operations.

Vulnerable
Rust
1// Vulnerable: Non-crypto RNG for secrets
2use rand::Rng;
3let mut rng = rand::thread_rng();
4let secret: [u8; 32] = rng.gen();
Secure
Rust
1// Secure: Use crypto-secure RNG
2use rand::rngs::OsRng;
3let mut rng = OsRng;
4let secret: [u8; 32] = rng.gen();
Medium

Timing Attack in Comparison

Non-constant-time comparison leaks information through timing.

Vulnerable
Rust
1// Vulnerable: Early exit comparison
2fn verify_token(input: &[u8], expected: &[u8]) -> bool {
3 input == expected // Short-circuits on mismatch
4}
Secure
Rust
1// Secure: Constant-time comparison
2use subtle::ConstantTimeEq;
3fn verify_token(input: &[u8], expected: &[u8]) -> bool {
4 input.ct_eq(expected).into()
5}