1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
//! A memory-safe buffer overflow.
//!
//! We use a soundness hole in lifetimes to cast an arbitrary lifetime to 'static.
//! See <https://github.com/rust-lang/rust/issues/25860>.
//!
//! We allocate a 10-byte slice on the heap, full of zeroes, then use the soundness
//! hole to obtain a mutable, 'static reference to it, and drop it. We then allocate
//! a new 5-byte slice on the heap, full of ones, and print the contents of it and the
//! old slice. We then write 3s to the original 10-byte slice and once again print
//! the contents of both slices, proving that we wrote out of bounds on the 5-byte slice.
//!
//! This completely memory safe, not-an-exploit works because the two smart pointers
//! to two different buffers get placed at the same location. The first gets dropped,
//! but we maintain a reference to it. When we create the second smart pointer, it
//! gets placed at the same location the first pointer used to be at, meaning our reference
//! now points to the new buffer. The reference stores the size of the old buffer -
//! 10 bytes - meaning when we write to all the bytes from our reference, we write
//! 10 bytes to our 5 byte buffer.
use {crate::lifetime_expansion, core::hint::black_box};
/// Uses the lifetime exploit to obtain a 'static mutable reference to a dropped
/// box, which points to a 10-byte slice.
///
/// This has to be in its own function and not aligned, because otherwise the
#[inline(never)]
#[allow(clippy::borrowed_box)]
fn get_dropped_buffer() -> &'static mut Box<[u8; 10]> {
let mut original_buffer = black_box(Box::new([0; 10]));
black_box(lifetime_expansion::expand_mut(&mut original_buffer))
}
/// We create a 'static reference to a dropped, 10-byte buffer, then create a 5-byte
/// buffer in its place. By writing to the buffer from our old reference, Rust thinks
/// the buffer is still 10 bytes, and writes 10 bytes to our 5-byte buffer.
pub fn buffer_overflow() {
println!(
"\
Source code for this file is available here: \
https://github.com/Speykious/cve-rs/blob/main/src/buffer_overflow.rs
"
);
let buffer_ref = get_dropped_buffer();
let new_buffer = black_box(Box::new([1_u8; 5]));
println!(
"Before write:\nNew buffer (small): {new_buffer:?}\nOld buffer (large): {buffer_ref:?}"
);
for byte in buffer_ref.iter_mut() {
*byte = 3;
}
println!(
"After write:\nNew buffer (small): {new_buffer:?}\nOld buffer (large): {buffer_ref:?}"
);
}