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}