3

I have a Result<Vec<f64>, _>. When I try to extract a pointer to the actual f64 array, what I observe is that the array dptr points to is a corrupted version of the expected array (the first 10 bytes have been changed).

Why does this happen, and how I can avoid it?

use std::error::Error;

fn main() {
    let res: Result<Vec<f64>, Box<dyn Error>> = Ok(vec![1., 2., 3., 4.]);
    let dptr: *const f64 = match res {
        Ok(v) => &v[0], 
        Err(_) => std::ptr::null(),
    };
    assert_eq!(unsafe { *dptr }, 1.0);
}

Result:

thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `0.0`,
 right: `1.0`', src/main.rs:9:5

Playground

2
  • 3
    I've added a minimal reproducible example and linked to a runnable version on the Rust Playground. This will make it easy for people to see what you're describing for themselves. Does this accurately capture the problem? If not, please feel free to update the code and the Playground link. Commented Dec 19, 2021 at 15:02
  • I think that does capture the gist of my problem, yes. Thanks! Commented Dec 22, 2021 at 16:09

1 Answer 1

7

The behaviour of that program is undefined, which can be seen by running it under Miri, which is a Rust interpreter that can sometimes detect Undefined Behavior. (you can do this in the playground by clicking "Tools" (top right) -> "Miri"):

error: Undefined Behavior: pointer to alloc1039 was dereferenced after this allocation got freed
 --> src/main.rs:9:25
  |
9 |     assert_eq!(unsafe { *dptr }, 1.0);
  |                         ^^^^^ pointer to alloc1039 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

What's happening here is a use-after-free: the Ok(v) => &v[0], line moves data from v (and thus res), causing it to be freed. Later, data used for other variable overwrote the existing data (because using a pointer after the memory it points to is freed is Undefined Behaviour).

If you had tried to read the data out of the res the normal way without unsafe, you would have got a compile-time error for this very issue:

error[E0382]: use of partially moved value: `res`
 --> src/main.rs:9:10
  |
6 |         Ok(v) => &v[0],
  |            - value partially moved here
...
9 |     dbg!(res.unwrap()[0]);
  |          ^^^ value used here after partial move
  |
  = note: partial move occurs because value has type `Vec<f64>`, which does not implement the `Copy` trait
help: borrow this field in the pattern to avoid moving `res.0`
  |
6 |         Ok(ref v) => &v[0],
  |            +++

(playground)

8
  • If you change the match arm to grab the 3rd element, then it works alright though (Ok(v) => &v[2])... What is special about index 0 or 1 that triggers the move? It still gives a Miri error, but I get the expect output (3.0). Commented Dec 19, 2021 at 15:12
  • @jeremymeadows The compiler doesn't clear out memory when it's freed: it just happens that there wasn't a lot of data being stored to memory between the move and the assert, so only the first two elements got overwritten with other data. But it's still Undefined Behaviour: the compiler gets to do whatever it wants in this case. If you turn on optimizations, then you do get an error when the 2nd element is used in the assert.
    – loops
    Commented Dec 19, 2021 at 15:19
  • @jeremymeadows Some allocators fill the initial portion of the freed block with a specific pattern that makes it easier to detect corruption. Perhaps the allocator Rust uses on your system works like that. Commented Dec 19, 2021 at 18:50
  • @Smitop right, I just wouldn't have expected anything to have overwritten since we didn't use any new data, although I guess there aren't guarantees about that since it is definitely something to avoid regardless. Commented Dec 19, 2021 at 21:20
  • @user4815162342 that is super interesting, did not know that ever existed Commented Dec 19, 2021 at 21:21

Not the answer you're looking for? Browse other questions tagged or ask your own question.