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:?}"
	);
}