rust_memory_safety_examples/use_after_free_prevention.rs
1//! Use-after-free prevention through ownership and lifetimes
2
3/// VULNERABLE C CODE (for comparison):
4/// ```c
5/// #include <stdlib.h>
6/// #include <stdio.h>
7///
8/// int* vulnerable_use_after_free() {
9/// int* ptr = malloc(sizeof(int));
10/// *ptr = 42;
11/// free(ptr); // Memory freed
12/// return ptr; // DANGLING POINTER!
13/// }
14///
15/// void exploit() {
16/// int* p = vulnerable_use_after_free();
17/// printf("%d\n", *p); // USE-AFTER-FREE!
18/// }
19/// ```
20///
21/// **Vulnerability:** Accessing memory after it's been freed causes:
22/// - Undefined behavior
23/// - Potential code execution (if attacker controls freed memory)
24/// - Crashes
25///
26/// **CVE Examples:**
27/// - CVE-2020-0796 (SMBGhost): Use-after-free in Windows SMB
28/// - CVE-2019-5786 (Chrome): Use-after-free in FileReader
29///
30
31/// SAFE RUST EQUIVALENT:
32/// Rust's ownership system prevents use-after-free at compile time
33
34/// Example 1: Ownership prevents use-after-free
35pub fn ownership_prevents_uaf() {
36 let data = Box::new(42); // Heap allocation
37 let value = *data; // Copy the value
38
39 drop(data); // Explicitly free memory
40
41 // This would NOT compile:
42 // println!("{}", *data); // Compile error: value moved!
43
44 println!("Copied value: {}", value); // Safe: we copied the value
45}
46
47/// Example 2: References have lifetimes
48pub fn lifetime_prevents_dangling_ref() {
49 let _reference: Option<&String> = None;
50
51 {
52 let data = String::from("temporary");
53 // This would NOT compile:
54 // reference = &data; // Compile error: `data` doesn't live long enough
55 }
56
57 // Cannot use reference here - compiler prevents it
58}
59
60/// Example 3: Borrowing rules prevent use-after-free
61pub fn borrowing_prevents_uaf() {
62 let mut data = vec![1, 2, 3];
63
64 let reference = &data[0]; // Immutable borrow
65
66 // This would NOT compile:
67 // data.clear(); // Compile error: cannot mutate while borrowed!
68
69 println!("First element: {}", reference);
70 // reference is no longer used, borrow ends
71
72 data.clear(); // Now we can mutate
73 println!("Vector cleared");
74}
75
76/// Example 4: Smart pointers provide safe resource management
77use std::rc::Rc;
78
79pub fn shared_ownership_safe() {
80 let data = Rc::new(vec![1, 2, 3]);
81 let clone1 = Rc::clone(&data);
82 let clone2 = Rc::clone(&data);
83
84 println!("Data from clone1: {:?}", clone1);
85 println!("Data from clone2: {:?}", clone2);
86
87 // Memory is freed only when ALL Rc references are dropped
88 drop(clone1);
89 drop(clone2);
90 drop(data); // Now memory is freed
91}
92
93/// Example 5: Real-world pattern - safe object lifecycle
94pub struct SafeObject {
95 data: Vec<i32>,
96}
97
98impl SafeObject {
99 pub fn new(data: Vec<i32>) -> Self {
100 Self { data }
101 }
102
103 pub fn get_data(&self) -> &[i32] {
104 &self.data
105 }
106
107 // Ownership rules prevent use-after-free:
108 // Once this consumes self, the object cannot be used again
109 pub fn consume(self) -> Vec<i32> {
110 self.data
111 }
112}
113
114pub fn safe_object_usage() {
115 let obj = SafeObject::new(vec![1, 2, 3]);
116 let data_ref = obj.get_data();
117 println!("Data: {:?}", data_ref);
118
119 let owned_data = obj.consume(); // obj is moved here
120
121 // This would NOT compile:
122 // obj.get_data(); // Compile error: value used after move!
123
124 println!("Owned data: {:?}", owned_data);
125}
126
127/// Example 6: Demonstration of Rust's compile-time safety
128pub fn compile_time_safety_demo() {
129 struct Resource {
130 id: i32,
131 }
132
133 impl Drop for Resource {
134 fn drop(&mut self) {
135 println!("Resource {} freed", self.id);
136 }
137 }
138
139 let res = Resource { id: 1 };
140 println!("Resource created: {}", res.id);
141
142 // Resource is automatically freed at end of scope
143 // No possibility of use-after-free
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn test_ownership_transfer() {
152 let data = Box::new(42);
153 let moved_data = data;
154
155 // data cannot be used here - ownership transferred
156 assert_eq!(*moved_data, 42);
157 }
158
159 #[test]
160 fn test_reference_lifetime() {
161 let data = String::from("test");
162 let reference = &data;
163
164 assert_eq!(reference, "test");
165 // reference lifetime ends here, data can be used again
166 }
167
168 #[test]
169 fn test_rc_shared_ownership() {
170 let data = Rc::new(vec![1, 2, 3]);
171 let clone = Rc::clone(&data);
172
173 assert_eq!(Rc::strong_count(&data), 2);
174 drop(clone);
175 assert_eq!(Rc::strong_count(&data), 1);
176 }
177
178 #[test]
179 fn test_safe_object() {
180 let obj = SafeObject::new(vec![1, 2, 3]);
181 assert_eq!(obj.get_data(), &[1, 2, 3]);
182
183 let data = obj.consume();
184 assert_eq!(data, vec![1, 2, 3]);
185 }
186}