Skip to content

offset_from "same allocation" check is confused by int-to-ptr casts #3767

Closed
rust-lang/rust
#128277
@RalfJung

Description

This code fails with -Zmiri-disable-stacked-borrows, but interestingly passes with SB:

use std::{mem::ManuallyDrop, slice};

fn extend_fail() {
    let data = Data::allocate(b"hello");
    assert_eq!(data.read(), b"hello");
    let mut buffer = Vec::<u8>::with_capacity(1024);
    buffer.extend(data.read());
    assert_eq!(buffer.len(), 5);
}

fn main() {
    extend_fail();
}

pub struct Data {
    len: usize,
    ptr: usize,
}

impl Data {
    pub fn allocate(data: &[u8]) -> Self {
        let mut data = ManuallyDrop::new(data.to_owned().into_boxed_slice());
        Self {
            len: data.len(),
            ptr: data.as_mut_ptr() as usize,
        }
    }
    pub fn read(&self) -> &[u8] {
        unsafe {
            // SAFETY: fine since we r/w it directly!
            slice::from_raw_parts(self.ptr as *mut u8, self.len)
        }
    }
}

impl Drop for Data {
    fn drop(&mut self) {
        unsafe {
            Vec::from_raw_parts(self.ptr as *mut u8, self.len, self.len);
        }
    }
}

gives

error: Undefined Behavior: `ptr_offset_from_unsigned` called on pointers into different allocations
   --> /home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/core/src/slice/iter.rs:136:1
    |
136 | / iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, as_ref, {
137 | |     fn is_sorted_by<F>(self, mut compare: F) -> bool
138 | |     where
139 | |         Self: Sized,
...   |
143 | |     }
144 | | }}
    | |__^ `ptr_offset_from_unsigned` called on pointers into different allocations
    |
    = 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: BACKTRACE:
    = note: inside `std::slice::Iter::<'_, u8>::make_slice` at /home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/core/src/slice/iter/macros.rs:57:26: 57:48
    = note: inside `std::slice::Iter::<'_, u8>::as_slice` at /home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/core/src/slice/iter.rs:132:9: 132:26
    = note: inside `<std::vec::Vec<u8> as std::vec::spec_extend::SpecExtend<&u8, std::slice::Iter<'_, u8>>>::spec_extend` at /home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/alloc/src/vec/spec_extend.rs:54:21: 54:40
    = note: inside `<std::vec::Vec<u8> as std::iter::Extend<&u8>>::extend::<&[u8]>` at /home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:3251:9: 3251:43
note: inside `extend_fail`
   --> x.rs:7:5
    |
7   |     buffer.extend(data.read());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^

I think the "same allocation" check in the offset_from intrinsic doesn't work properly when there are wildcard pointers involved.

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions