Search code examples
rustsegmentation-faultraw-pointer

Unexpected segfault when working with raw pointers


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);
    }
}

Playground.

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?


Solution

  • You've got a use-after-free. In push(), you assign a temporary to self.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):

    error: Undefined Behavior: pointer to alloc1786 was dereferenced after this allocation got freed
      --> src/main.rs:29:26
       |
    29 |         println!("{:?}", *(*heap.root).value);
       |                          ^^^^^^^^^^^^^^^^^^^ pointer to alloc1786 was dereferenced after this allocation got freed
       |
       = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
       = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
               
       = note: inside `main` at src/main.rs:29:26
       = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
    
    note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
    

    Since you've got UB, the program can do anything, and any change may affect what it does, even if it seems unrelated.