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
//! A memory-safe segmentation fault, using the lifetime expansion exploit.
//!
//! We use this hole to create a 'static reference to a dropped (yes, dropped) smart
//! pointer. The smart pointer exists on the stack, but was dropped, so the reference
//! is borrowing arbitrary data on the stack. We can then fill the stack with 0s, which
//! replaces the smart pointer's address with 0, creating a null pointer in safe Rust.
//! By accessing the contents of the pointer, we force Rust to dereference the null pointer,
//! causing a segfault.

use {crate::lifetime_expansion, std::hint::black_box};

/// Returns a static reference to a dropped (yes, dropped) box.
#[inline(never)]
#[allow(clippy::borrowed_box)]
fn get_dropped_box() -> &'static Box<Box<u8>> {
	let box_ = black_box(Box::new(Box::new(8)));
	lifetime_expansion::expand(&box_)
}

/// Gets a reference to a dropped smart pointer, then fills the stack with 0s.
/// This overrides where the smart pointer used to be and replaces its address
/// with all 0s, creating a null pointer. We then read the data from the box,
/// which forces Rust to dereference the smart pointer, causing it to dereference
/// a null pointer.
///
/// In theory this should work with a normal box, but in practice Rust reads random
/// memory instead of segfaulting on a null pointer. We think this is due to compiler
/// optimisations.
pub fn segfault() {
	println!(
		"\
        Source code for this file is available here: \
        https://github.com/Speykious/cve-rs/blob/main/src/segfault.rs
        "
	);

	let my_ref = get_dropped_box();
	black_box([0; 1024]);
	println!("{my_ref:?}");
}