Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stacked Borrows: raw pointer usable only for `T` too strict? #134

Open
RalfJung opened this issue May 28, 2019 · 6 comments
Open

Stacked Borrows: raw pointer usable only for `T` too strict? #134

RalfJung opened this issue May 28, 2019 · 6 comments

Comments

@RalfJung
Copy link
Member

@RalfJung RalfJung commented May 28, 2019

Currently, the following is illegal according to Stacked Borrows:

let val = [1u8, 2];
let ptr = &val[0] as *const u8;
let _val = unsafe { *ptr.add(1) };

The problem is that the cast to *const u8 creates a raw pointer that may only be used for the u8 it points to, not anything else. The most common case is to do &slice[0] as *const _ instead of slice.as_ptr().

This has lead to problems:

Maybe this is too restrictive and raw pointers should be allowed to access their "surroundings"? I am not sure what exactly that would look like though.

I'll use this issue to collect such cases.

@RalfJung

This comment has been minimized.

Copy link
Member Author

@RalfJung RalfJung commented May 28, 2019

This is also a problem in Rc::into_raw/Rc::from_raw:

    pub fn into_raw(this: Self) -> *const T {
        let ptr: *const T = &*this;
        mem::forget(this);
        ptr
    }

ptr may only be used to access the T part of the RcBox<T>, but if later used with from_raw it is used for the entire Rc. Fixing this is not even possible without rust-lang/rfcs#2582.

bors added a commit to rust-lang/hashbrown that referenced this issue May 28, 2019
cast the entire slice to a raw pointer, not just the first element

A strict reading of pointer provenance implies that when a `&T` gets cast to `*const T`, you may only use the raw pointer to access that `T`, not its neighbors.  That's what Miri currently implements, though it is less strict around statics (which is why this one does not currently cause a Miri failure -- I'd like to make Miri more strict though).

Cc rust-lang/unsafe-code-guidelines#134
@RalfJung

This comment has been minimized.

Copy link
Member Author

@RalfJung RalfJung commented Aug 16, 2019

This also came up in Gilnaa/memoffset#21, where @Amanieu proposed an container_of! macro that computes the address of a struct given the address of one of its fields.

@RalfJung

This comment has been minimized.

Copy link
Member Author

@RalfJung RalfJung commented Aug 17, 2019

@Amanieu the problem with code like this

struct Pair { f1: u16, f2: u16 };

let p = Pair { f1: 2, f2: 3 };
let c = container_of!(&p.f1, Pair, f1);
let _val = c.f2;

arises when you imagine splitting it across several functions:

struct Pair { f1: u16, f2: u16 };

let p = Pair { f1: 2, f2: 3 };
foo(&p.f1);
p.f2 = 4;

We want the compiler to be able to move the assignment to f2 up across the call to foo. But if foo is allowed to use container_of! and then read f2, that is no longer possible.

So, I think there is a real conflict here between being able to bound the effects of a call like foo(&p.f1), and allowing container_of!.

@Lokathor

This comment has been minimized.

Copy link
Contributor

@Lokathor Lokathor commented Aug 19, 2019

How does this work with [T]::as_ptr? Does that pointer let you use "the whole slice" when offsetting?

@RalfJung

This comment has been minimized.

Copy link
Member Author

@RalfJung RalfJung commented Aug 20, 2019

@Lokathor yes. Those methods do the right thing. They cast the wide reference to a wide raw pointer, and only then go to thin -- so the ref-to-raw cast has the right "span" in memory.

@ecstatic-morse

This comment has been minimized.

Copy link

@ecstatic-morse ecstatic-morse commented Oct 5, 2019

rust-lang/rust#64980 gives a minor reason to maintain the status quo here. It includes a test-case for a dataflow analysis that computes whether a given Local is mutable through a reference. This test would show the analysis to be unsound if it were legal to offset a reference to one field into a pointer to another, disjoint field.

It is possible to relax the analysis so that it remains sound if this behavior became defined (see rust-lang/rust#65030).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.