rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.2k stars 12.7k forks source link

Fat pointers with same data part, but different vtables #48795

Closed rom1v closed 4 years ago

rom1v commented 6 years ago

I did not manage to reproduce in a minimal sample, but in my application, I ended up with:

let x = …;
let y = …;
println!("{:p} == {:p} ? {}", x, y, ptr::eq(x, y));

printing:

0x7f87a8c3a618 == 0x7f87a8c3a618 ? false

After investigations (and discussion on freenode/##rust), it appeared that x and y were fat pointers having the same data part (the 64 first bits), but a different vtable (the last 64 bits).

I applied this patch to my application, which solves the reported issue: https://github.com/Genymobile/gnirehtet/commit/c36fa4d1a1086aa03e56aabae12669f8b1a1a1c4

But I can't understand how two fat pointers for the same objects may have different vtables. Is it expected?

sfackler commented 6 years ago

We don't guarantee that vtables are unique. For example, a separate copy could be generated in multiple codegen units, or multiple crates.

rom1v commented 6 years ago

OK.

In that case, shouldn't ptr::eq() ignore the vtable part?

rom1v commented 6 years ago

This also affects slices:

use std::ptr;
fn main() {
    let array = [1, 2, 3, 4];
    let slice1 = &array[0..1];
    let slice2 = &array[0..3];
    println!("{:p} == {:p} ? {}", slice1, slice2, ptr::eq(slice1, slice2));
}
0x7fff0a6595b8 == 0x7fff0a6595b8 ? false
hetmankp commented 6 years ago

Here is the smallest sample I managed to find to demonstrate this on my system using rustups's 1.27.0-nightly. It appears this is only reproducible with incremental builds, which is why it won't show up when trying it with Rust Playground.

use std::path::Path;

pub trait Block { }

struct Inner {
    data: i32,
}

impl Block for Inner { }

impl Inner {
    fn new_box(data: i32) -> Box<Inner> {
        Box::new(Inner {
            data: data,
        })
    }
}

pub struct Outer {
    inner: Box<Inner>,
    block: *mut Block,
}

impl Outer {
    pub fn new_box<P: AsRef<Path>>() -> Box<Outer> {
        let mut inner = Inner::new_box(123);
        let block = &mut *inner as *mut _;

        Box::new(Outer {
            inner: inner,
            block: block,
        })
    }

    pub fn get_inner(&mut self) -> &mut Block {
        &mut *self.inner
    }
}

fn main() {
    let mut outer = Outer::new_box::<&str>();
    let b = outer.block;
    let a = outer.get_inner() as *mut Block;
    println!("{:p} == {:p}: {}", a, b, a == b);
}

Result:

$ rustc main.rs -C incremental=./inc
$ ./main
0x7f0672a23008 == 0x7f0672a23008: false
njaard commented 5 years ago

I'm being bit by this problem also. It's causing ptr::eq to fail which is breaking my program. It seems as though you could compare the vtable itself byte-for-byte to fix the problem, as their contents are the same.

RalfJung commented 4 years ago

Closing as duplicate of https://github.com/rust-lang/rust/issues/46139.