Skip to content

Miri and provenance using raw_ref macros #1695

Closed
@danielhenrymantilla

Description

@danielhenrymantilla

The following code:

#[derive(Debug)]
#[repr(C)] // for easier computation of the field offsets (no need to `offset_of!`)
struct S {
    a: u8,
    b: u8,
}
let s = S { a: 42, b: 27 };
let at_b = ::core::ptr::raw_const!(s.b);
unsafe {
    dbg!(
        &*at_b.cast::<u8>().offset(-1 /* offset of `.b` in `S` */).cast::<S>()
    );
}

triggers the following Miri error:

error: Undefined Behavior: trying to reborrow for SharedReadOnly at alloc1414+0x1, but parent tag <untagged> does not have an appropriate item in the borrow stack
  --> src/main.rs:15:13
   |
15 |             &*at_b.cast::<u8>().offset(-1 /* offset of `.b` in `S` */).cast::<S>()
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to reborrow for SharedReadOnly at alloc1414+0x1, but parent tag <untagged> does not have an appropriate item in the borrow stack
   |
   = help: this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental
   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
   = note: inside `main` at src/main.rs:15:13
   = note: inside `<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
   = note: inside `std::sys_common::backtrace::__rust_begin_short_backtrace::<fn(), ()>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys_common/backtrace.rs:125:18
   = note: inside closure at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:66:18
   = note: inside `std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:259:13
   = note: inside `std::panicking::r#try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:379:40
   = note: inside `std::panicking::r#try::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:343:19
   = note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:396:14
   = note: inside `std::rt::lang_start_internal` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:51:25
   = note: inside `std::rt::lang_start::<()>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:65:5

Which means that ptr::raw_const!(s.b) is interpreted as having provenance over s.b only; i.e., that it is not equivalent to doing ptr::raw_const!(s).cast::<u8>().offset( offset_of!(S, b) )

More surprisingly, if at_b is computed as:

let at_b = ::core::ptr::raw_const!((*&s).b);

then Miri does not complain.


This looks like a bug (Miri false positive), although I can imagine that having the raw_ref! macros "guess" the right provenance might be hard to implement, if not impossible; but these differences in semantics with changes that subtle look error-prone and footgunny to me; at the very least the docs of the raw_ref macros ought to mention that extra care should be taken when dealing with provenance (that is, to recommend using the explicit at_s.cast::<u8>.offset(offset_of!(S, field)).cast::<FieldTy>(), with offset_of! being the one that uses raw_ref! macros under the hood).

This might be an important thing to clarify / to fix now that the macros are about to be stabilized (rust-lang/rust#80886)

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-aliasingArea: This affects the aliasing model (Stacked/Tree Borrows)C-supportCategory: Not necessarily a bug, but someone asking for support

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions