// --- std::alloc --- pub fn test_alloc(mode: i32) { let layout = std::alloc::Layout::new::(); unsafe { let m1 = std::alloc::alloc(layout); // *mut u8 let m2 = m1 as *mut i64; // *mut i64 *m2 = 1; // GOOD let v1 = *m1; // GOOD let v2 = *m2; // GOOD let v3 = std::ptr::read::(m1); // GOOD let v4 = std::ptr::read::(m2); // GOOD println!(" v1 = {v1}"); println!(" v2 = {v2}"); println!(" v3 = {v3}"); println!(" v4 = {v4}"); std::alloc::dealloc(m1, layout); // $ Source[rust/access-invalid-pointer]=dealloc // (m1, m2 are now dangling) match mode { 0 => { // reads let v5 = *m1; // $ Alert[rust/access-invalid-pointer]=dealloc let v6 = *m2; // $ MISSING: Alert println!(" v5 = {v5} (!)"); // corrupt in practice println!(" v6 = {v6} (!)"); // corrupt in practice // test repeat reads (we don't want lots of very similar results for the same dealloc) let _v5b = *m1; let _v5c = *m1; }, 100 => { // more reads let v7 = std::ptr::read::(m1); // $ Alert[rust/access-invalid-pointer]=dealloc let v8 = std::ptr::read::(m2); // $ MISSING: Alert println!(" v7 = {v7} (!)"); // corrupt in practice println!(" v8 = {v8} (!)"); // corrupt in practice }, 101 => { // writes *m1 = 2; // $ Alert[rust/access-invalid-pointer]=dealloc *m2 = 3; // $ MISSING: Alert }, 102 => { // more writes std::ptr::write::(m1, 4); // $ Alert[rust/access-invalid-pointer]=dealloc std::ptr::write::(m2, 5); // $ MISSING: Alert }, _ => {} } } } pub fn test_alloc_array(mode: i32) { let layout = std::alloc::Layout::new::<[u8; 10]>(); unsafe { let m1 = std::alloc::alloc(layout); let m2 = m1 as *mut [u8; 10]; (*m2)[0] = 4; // GOOD (*m2)[1] = 5; // GOOD let v1 = (*m2)[0]; // GOOD let v2 = (*m2)[1]; // GOOD println!(" v1 = {v1}"); println!(" v2 = {v2}"); std::alloc::dealloc(m2 as *mut u8, layout); // $ Source[rust/access-invalid-pointer]=dealloc_array // m1, m2 are now dangling match mode { 0 => { // read let v3 = (*m2)[0]; // $ Alert[rust/access-invalid-pointer]=dealloc_array println!(" v3 = {v3} (!)"); // corrupt in practice }, 110 => { // another read let v4 = (*m2)[1]; // $ Alert[rust/access-invalid-pointer]=dealloc_array println!(" v4 = {v4} (!)"); // corrupt in practice }, 111 => { // write (*m2)[0] = 3; // $ Alert[rust/access-invalid-pointer]=dealloc_array }, 112 => { // another write (*m2)[1] = 4; // $ Alert[rust/access-invalid-pointer]=dealloc_array }, 113 => { // more writes std::ptr::write::(m1, 5); // $ MISSING: Alert std::ptr::write::<[u8; 10]>(m2, [6; 10]); // $ Alert[rust/access-invalid-pointer]=dealloc_array }, _ => {} } } } // --- libc::malloc --- pub fn test_libc() { unsafe { let my_ptr = libc::malloc(256) as *mut i64; *my_ptr = 10; let v1 = *my_ptr; // GOOD println!(" v1 = {v1}"); libc::free(my_ptr as *mut libc::c_void); // $ Source[rust/access-invalid-pointer]=free // (my_ptr is now dangling) let v2 = *my_ptr; // $ Alert[rust/access-invalid-pointer]=free println!(" v2 = {v2} (!)"); // corrupt in practice } } // --- std::ptr --- pub fn test_ptr_invalid(mode: i32) { let p1: *const i64 = std::ptr::dangling(); // $ Source[rust/access-invalid-pointer]=dangling let p2: *mut i64 = std::ptr::dangling_mut(); // $ Source[rust/access-invalid-pointer]=dangling_mut let p3: *const i64 = std::ptr::null(); // $ Source[rust/access-invalid-pointer]=null if mode == 120 { unsafe { // (a segmentation fault occurs in the code below) let v1 = *p1; // $ Alert[rust/access-invalid-pointer]=dangling let v2 = *p2; // $ Alert[rust/access-invalid-pointer]=dangling_mut let v3 = *p3; // $ Alert[rust/access-invalid-pointer]=null println!(" v1 = {v1} (!)"); println!(" v2 = {v2} (!)"); println!(" v3 = {v3} (!)"); } } } struct MyObject { value: i64 } impl MyObject { fn is_zero(&self) -> bool { self.value == 0 } } pub unsafe fn test_ptr_invalid_conditions(mode: i32) { let layout = std::alloc::Layout::new::(); // --- mutable pointer --- let mut ptr = std::alloc::alloc(layout) as *mut MyObject; (*ptr).value = 0; // good if mode == 121 { // (causes a panic below) ptr = std::ptr::null_mut(); // $ Source[rust/access-invalid-pointer] } if ptr.is_null() { let v = (*ptr).value; // $ Alert[rust/access-invalid-pointer] println!(" cond1 v = {v}"); } else { let v = (*ptr).value; // $ SPURIOUS: Alert[rust/access-invalid-pointer] good - unreachable with null pointer println!(" cond2 v = {v}"); } if mode == 122 { // (causes a panic below) ptr = std::ptr::null_mut(); // $ Source[rust/access-invalid-pointer] } if !(ptr.is_null()) { let v = (*ptr).value; // $ SPURIOUS: Alert[rust/access-invalid-pointer] good - unreachable with null pointer println!(" cond3 v = {v}"); } else { let v = (*ptr).value; // $ Alert[rust/access-invalid-pointer] println!(" cond4 v = {v}"); } if mode == 123 { // (causes a panic below) ptr = std::ptr::null_mut(); // $ Source[rust/access-invalid-pointer] } if ptr.is_null() || (*ptr).value == 0 { // $ SPURIOUS: Alert[rust/access-invalid-pointer] good - deref is protected by short-circuiting println!(" cond5"); } if ptr.is_null() || (*ptr).is_zero() { // $ SPURIOUS: Alert[rust/access-invalid-pointer] good - deref is protected by short-circuiting println!(" cond6"); } if !ptr.is_null() || (*ptr).value == 0 { // $ Alert[rust/access-invalid-pointer] println!(" cond7"); } if mode == 124 { // (causes a panic below) ptr = std::ptr::null_mut(); // $ Source[rust/access-invalid-pointer] } if ptr.is_null() && (*ptr).is_zero() { // $ Alert[rust/access-invalid-pointer] println!(" cond8"); } if mode == 125 { // (causes a panic below) ptr = std::ptr::null_mut(); // $ Source[rust/access-invalid-pointer] } if (*ptr).is_zero() || ptr.is_null() { // $ Alert[rust/access-invalid-pointer] println!(" cond9"); } // --- immutable pointer --- let const_ptr; if mode == 126 { // (causes a panic below) const_ptr = std::ptr::null_mut(); // $ Source[rust/access-invalid-pointer] } else { const_ptr = std::alloc::alloc(layout) as *mut MyObject; (*const_ptr).value = 0; // good } if const_ptr.is_null() { let v = (*const_ptr).value; // $ Alert[rust/access-invalid-pointer] println!(" cond10 v = {v}"); } else { let v = (*const_ptr).value; // $ good - unreachable with null pointer println!(" cond11 v = {v}"); } } // --- drop --- struct MyBuffer { data: Vec } pub fn test_drop() { let my_buffer = MyBuffer { data: vec!(1, 2, 3) }; let p1 = std::ptr::addr_of!(my_buffer); unsafe { let v1 = (*p1).data[0]; // GOOD println!(" v1 = {v1}"); } drop(my_buffer); // explicitly destructs the `my_buffer` variable unsafe { let v2 = (*p1).data[0]; // $ MISSING: Alert println!(" v2 = {v2} (!)"); // corrupt in practice } } pub fn test_ptr_drop(mode: i32) { let layout = std::alloc::Layout::new::>(); unsafe { let p1 = std::alloc::alloc(layout) as *mut Vec; let p2 = p1; *p1 = vec!(1, 2, 3); let v1 = (*p1)[0]; // GOOD let v2 = (*p2)[0]; // GOOD println!(" v1 = {v1}"); println!(" v2 = {v2}"); std::ptr::drop_in_place(p1); // $ Source[rust/access-invalid-pointer]=drop_in_place // explicitly destructs the pointed-to `m2` if mode == 1 { let v3 = (*p1)[0]; // $ Alert[rust/access-invalid-pointer]=drop_in_place println!(" v3 = {v3} (!)"); // corrupt in practice } if mode == 130 { let v4 = (*p2)[0]; // $ MISSING: Alert println!(" v4 = {v4} (!)"); // corrupt in practice } let p3 = std::alloc::alloc(layout) as *mut Vec; std::ptr::drop_in_place((*p3).as_mut_ptr()); // GOOD } } struct MyDropBuffer { ptr: *mut u8, } impl MyDropBuffer { unsafe fn new() -> MyDropBuffer { let layout = std::alloc::Layout::from_size_align(1024, 1).unwrap(); MyDropBuffer { ptr: std::alloc::alloc(layout), } // ... } } impl Drop for MyDropBuffer { fn drop(&mut self) { let layout = std::alloc::Layout::from_size_align(1024, 1).unwrap(); unsafe { _ = *self.ptr; drop(*self.ptr); // $ MISSING: Source[rust/access-invalid-pointer]=drop _ = *self.ptr; // $ MISSING: Alert[rust/access-invalid-pointer]=drop std::alloc::dealloc(self.ptr, layout); } } } // --- qhelp examples --- fn do_something(s: &String) { println!(" s = {}", s); } fn test_qhelp_example_good(ptr: *mut String) { unsafe { do_something(&*ptr); } // ... unsafe { std::ptr::drop_in_place(ptr); } } fn test_qhelp_example_bad(ptr: *mut String) { unsafe { std::ptr::drop_in_place(ptr); // $ Source[rust/access-invalid-pointer]=drop_in_place } // ... unsafe { do_something(&*ptr); // $ Alert[rust/access-invalid-pointer]=drop_in_place } } pub fn test_qhelp_examples() { let layout = std::alloc::Layout::new::<[String; 2]>(); unsafe { let ptr = std::alloc::alloc(layout); let ptr_s = ptr as *mut [String; 2]; let ptr1 = &raw mut (*ptr_s)[0]; let ptr2 = &raw mut (*ptr_s)[1]; *ptr1 = String::from("123"); *ptr2 = String::from("456"); test_qhelp_example_good(ptr1); test_qhelp_example_bad(ptr2); std::alloc::dealloc(ptr, layout); } } // --- Vec --- pub fn test_vec_reserve() { let mut vec1 = Vec::::new(); vec1.push(100); let ptr1 = &raw mut vec1[0]; unsafe { let v1 = *ptr1; println!(" v1 = {}", v1); } vec1.reserve(1000); // $ MISSING: Source[rust/access-invalid-pointer]=reserve // (may invalidate the pointer) unsafe { let v2 = *ptr1; // $ MISSING: Alert[rust/access-invalid-pointer]=reserve println!(" v2 = {}", v2); // corrupt in practice } // - let mut vec2 = Vec::::new(); vec2.push(200); let ptr2 = &raw mut vec2[0]; unsafe { let v3 = *ptr2; println!(" v3 = {}", v3); } for _i in 0..1000 { vec2.push(0); // $ MISSING: Source[rust/access-invalid-pointer]=push // (may invalidate the pointer) } unsafe { let v4 = *ptr2; // $ MISSING: Alert[rust/access-invalid-pointer]=push println!(" v4 = {}", v4); // corrupt in practice } }