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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! So far, all our bugs are implemented using a single soundness hole in the Rust compiler.
//!
//! The explanation is detailed in the [`lifetime_expansion`] module.

#![deny(unsafe_code)]

// The actual exploit
pub mod lifetime_expansion;

// The bugs we created with the exploit
pub mod buffer_overflow;
pub mod segfault;
pub mod transmute;
pub mod use_after_free;

pub use lifetime_expansion::*;

pub use buffer_overflow::buffer_overflow;
pub use segfault::segfault;
pub use transmute::transmute;
pub use use_after_free::use_after_free;

/// Construct a [`String`] from a pointer, capacity and length, in a completely safe manner.
///
/// [`String`] is a `Vec<u8>` which is a `(RawVec, usize)` which is a `((Unique, usize), usize)`.
///
/// Rust explicitly says that structs are not guaranteed to have members in order,
/// so instead we determine that order at runtime.
///
/// # Safety
///
/// This function is 100% memory-safe.
///
/// Nevetheless, remember to use [`std::mem::forget`] to deallocate the fake [`String`], otherwise Rust
/// will think the pointer has been allocated by the global allocator and free it the wrong way.
///
/// > As they say: *Trust, but Verify.*
#[inline(always)]
pub fn construct_fake_string(ptr: *mut u8, cap: usize, len: usize) -> String {
	let sentinel_string = crate::transmute::<_, String>([0usize, 1usize, 2usize]);

	let fields = [ptr as usize, cap, len];
	let mut actual_buf = [0usize; 3];
	actual_buf[0] = fields[sentinel_string.as_ptr() as usize];
	actual_buf[1] = fields[sentinel_string.capacity()];
	actual_buf[2] = fields[sentinel_string.len()];

	std::mem::forget(sentinel_string);

	crate::transmute::<_, String>(actual_buf)
}

use std::hint::black_box;

/// Returns a static reference to a dropped `Box<Box<T>>`.
#[inline(never)]
#[allow(clippy::borrowed_box, clippy::redundant_allocation)]
fn get_dropped_box<T: Default>() -> &'static mut Box<Box<T>> {
	let mut boxx = black_box(Box::<Box<T>>::default());
	lifetime_expansion::expand_mut(&mut boxx)
}

/// Not allocate an object. The returned reference is always invalid.
pub fn not_alloc<'a, T: Default + 'static>() -> &'a mut T {
	get_dropped_box::<T>().as_mut().as_mut()
}

/// Good for job security.
#[cfg(feature = "give-up")]
pub fn give_up<T: 'static>() -> Box<T> {
	use std::time::SystemTime;

	let size = std::mem::size_of::<T>();

	let mut v = Vec::with_capacity(size);

	let mut rng = {
		let seed = SystemTime::now()
			.duration_since(SystemTime::UNIX_EPOCH)
			.unwrap();

		oorandom::Rand32::new(seed.as_secs())
	};

	for _ in 0..size {
		v.push((rng.rand_u32() % 256) as u8);
	}

	crate::transmute(v.into_boxed_slice())
}

/// Download more RAM from the internet.
///
/// Cannot control the amount we're gonna get. It's hard to fetch these days.
#[cfg(feature = "download-more-ram")]
pub fn download_more_ram<'a, T: 'static>() -> &'a mut [T] {
	const URL: &str =
		"http://www.randomnumberapi.com/api/v1.0/randomnumber?min=1073741824&max=34359738368&count=2";

	let resp = reqwest::blocking::get(URL).unwrap().text().unwrap();

	let (ptr, len) = resp[1..(resp.len() - 2)].split_once(',').unwrap();

	let downloaded_ram = {
		let sentinel_slice = crate::transmute::<_, &[u8]>([0usize, 1usize]);

		let fields = [ptr.parse::<usize>().unwrap(), len.parse::<usize>().unwrap()];
		let mut actual_buf = [0usize; 2];
		actual_buf[0] = fields[sentinel_slice.as_ptr() as usize];
		actual_buf[1] = fields[sentinel_slice.len()];

		crate::transmute::<_, Box<[T]>>(actual_buf)
	};

	Box::leak(downloaded_ram)
}

#[cfg(test)]
mod tests {
	#[test]
	fn can_give_up() {
		let job_security = crate::give_up::<u64>();
		Box::leak(job_security);
	}

	#[test]
	fn can_download_more_ram() {
		crate::download_more_ram::<u64>();
	}
}