Initially I wrote a Heap implementation in Rust, but I was getting strange segfaults, so I narrowed down the code to this example, which reproduces the behavior.
use core::fmt::Debug;
pub struct Node<T: Ord + Debug> {
pub value: *const T,
}
pub struct MaxHeap<T: Ord + Debug> {
pub root: *const Node<T>,
}
impl<T: Ord + Debug> MaxHeap<T> {
pub fn push(&mut self, value: *const T) {
self.root = &mut Node { value: value };
}
}
fn main() {
let a = 124i64;
let b = 124i64;
let c = 1i64;
let mut heap = MaxHeap {
root: &mut Node { value: &a },
};
heap.push(&b);
println!("{:?}", &c);
unsafe {
println!("{:?}", *(*heap.root).value);
}
}
The result I get from this is:
1
Segmentation fault (core dumped)
The interesting thing (to me) is that if I remove the print of c, there is no segfault and the correct value is printed for the heap root.
124
I expect that anything happening with c can't affect heap, but it does. What am I missing?
You've got a use-after-free. In
push(), you assign a temporary toself.root. The temporary's lifetime is finished of the statement and you're pointing to freed memory. Any further use will cause undefined behavior.Miri reports it (Tools->Miri in the playground):
Since you've got UB, the program can do anything, and any change may affect what it does, even if it seems unrelated.