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?

Upvotes: 0

Views: 526

Answers (1)

Chayim Friedman
Chayim Friedman

Reputation: 70820

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.

Upvotes: 2

Related Questions