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

Potential soundness issue (stacked borrows): Is alloc_fast_path persisting borrows too long? #62

Closed
Manishearth opened this issue Jul 19, 2023 · 11 comments

Comments

@Manishearth
Copy link

Manishearth commented Jul 19, 2023

rust-typed-arena/src/lib.rs

Lines 193 to 205 in f1f26c3

fn alloc_fast_path(&self, value: T) -> Result<&mut T, T> {
let mut chunks = self.chunks.borrow_mut();
let len = chunks.current.len();
if len < chunks.current.capacity() {
chunks.current.push(value);
// Avoid going through `Vec::deref_mut`, which overlaps
// other references we have already handed out!
debug_assert!(len < chunks.current.len()); // bounds check
Ok(unsafe { &mut *chunks.current.as_mut_ptr().add(len) })
} else {
Err(value)
}
}

It's unclear to me if this is sound under stacked/tree borrows.

Essentially, the lifetime of the *chunks mutable place is less than that of the function, and when we call chunks.current.as_mut_ptr() we materialize an &mut Vec<T> for the receiver, but then we return a reference within there that lives longer.

My understanding of how these things interact with cells is that from the purposes of these rules cell types behind references can do things that would not be allowed from the general rules of working with a reference, but that doesn't apply to references obtained from within a cell, which is what's happening here.

As far as I can see there's no actual safe way to do this currently (but it is absolutely something that ought to be safe): we need a way to get the ptr behind a vector without materializing a reference to it, and there is currently no self: *mut Self Vec API for this. (We essentially need the equivalent of addr_of!() but for vector fields). So personally this does not worry me too much since "this is unsafe according to currently-in-flux rules but there is no safe way to handle this use case" to me means that we should raise the use case with the folks making the rules and wait for the right APIs.

I also understand that UCG has been trying to figure out the balance on recursive validity and stuff, and Miri does not currently complain on this.

So this might be fine in the end, but I do think we should talk about it and raise this use case with UCG so we can get a ruling and folks can ensure the stdlib gets the appropriate APIs as needed.

@Manishearth Manishearth changed the title Potential soundness issue: Is alloc_fast_path persisting borrows too long? Potential soundness issue (stacked borrows): Is alloc_fast_path persisting borrows too long? Jul 19, 2023
@Manishearth
Copy link
Author

Manishearth commented Jul 19, 2023

Well I guess one safe way to do this is to only store raw pointers, but that does kinda suck

@Manishearth
Copy link
Author

Manishearth commented Jul 19, 2023

Also opened a zulip thread to get feedback from people who understand these rules better than I do.

@thomcc
Copy link
Owner

thomcc commented Jul 19, 2023

I think vec.as_mut_ptr() deliberately doesn't materialize a slice for this reason, but perhaps I'm misunderstanding the issue.

@Manishearth
Copy link
Author

@thomcc it does because as_mut_ptr() has an &mut Self receiver.

(this kind of thing is exactly why addr_of!() exists)

@Manishearth
Copy link
Author

However, AIUI UCG does not want types like Vec to be special here, so maybe this is still fine and the stacked borrow does not poke through the vec.

@thomcc
Copy link
Owner

thomcc commented Jul 19, 2023

the &mut Self only applies to the Vec, not to the pointer it holds (which is inside the RawVec).

@Manishearth
Copy link
Author

Yeah, I'm convinced. We're fine as long as Vec isn't special and so far I have mostly heard noises that Vec is not special.

@Manishearth
Copy link
Author

Also relevant, from Ralf:

Stacked Borrows and Tree Borrows are strictly 1-layer, they don't go recurisvely looking for nested references

@RalfJung
Copy link
Contributor

Yeah, I'm convinced. We're fine as long as Vec isn't special and so far I have mostly heard noises that Vec is not special.

Cc rust-lang/unsafe-code-guidelines#326

@Manishearth
Copy link
Author

Ah, thanks, I was going to go digging for that in a bit 😄

@Manishearth
Copy link
Author

Opened rust-lang/rust#113859 upstream to document the common-sense-but-not-guaranteed invariant that this code relies on.

bors added a commit to rust-lang-ci/rust that referenced this issue Aug 29, 2023
…orrow, r=dtolnay

Add note that Vec::as_mut_ptr() does not materialize a reference to the internal buffer

See discussion on thomcc/rust-typed-arena#62 and [t-opsem](https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/is.20this.20typed_arena.20code.20sound.20under.20stacked.2Ftree.20borrows.3F)

This method already does the correct thing here, but it is worth guaranteeing that it does so it can be used more freely in unsafe code without having to worry about potential Stacked/Tree Borrows violations. This moves one more unsafe usage pattern from the "very likely sound but technically not fully defined" box into "definitely sound", and currently our surface area of the latter is woefully small.

I'm not sure how best to word this, opening this PR as a way to start discussion.
github-actions bot pushed a commit to rust-lang/miri that referenced this issue Aug 31, 2023
…dtolnay

Add note that Vec::as_mut_ptr() does not materialize a reference to the internal buffer

See discussion on thomcc/rust-typed-arena#62 and [t-opsem](https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/is.20this.20typed_arena.20code.20sound.20under.20stacked.2Ftree.20borrows.3F)

This method already does the correct thing here, but it is worth guaranteeing that it does so it can be used more freely in unsafe code without having to worry about potential Stacked/Tree Borrows violations. This moves one more unsafe usage pattern from the "very likely sound but technically not fully defined" box into "definitely sound", and currently our surface area of the latter is woefully small.

I'm not sure how best to word this, opening this PR as a way to start discussion.
lnicola pushed a commit to lnicola/rust-analyzer that referenced this issue Apr 7, 2024
…dtolnay

Add note that Vec::as_mut_ptr() does not materialize a reference to the internal buffer

See discussion on thomcc/rust-typed-arena#62 and [t-opsem](https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/is.20this.20typed_arena.20code.20sound.20under.20stacked.2Ftree.20borrows.3F)

This method already does the correct thing here, but it is worth guaranteeing that it does so it can be used more freely in unsafe code without having to worry about potential Stacked/Tree Borrows violations. This moves one more unsafe usage pattern from the "very likely sound but technically not fully defined" box into "definitely sound", and currently our surface area of the latter is woefully small.

I'm not sure how best to word this, opening this PR as a way to start discussion.
RalfJung pushed a commit to RalfJung/rust-analyzer that referenced this issue Apr 27, 2024
…dtolnay

Add note that Vec::as_mut_ptr() does not materialize a reference to the internal buffer

See discussion on thomcc/rust-typed-arena#62 and [t-opsem](https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/is.20this.20typed_arena.20code.20sound.20under.20stacked.2Ftree.20borrows.3F)

This method already does the correct thing here, but it is worth guaranteeing that it does so it can be used more freely in unsafe code without having to worry about potential Stacked/Tree Borrows violations. This moves one more unsafe usage pattern from the "very likely sound but technically not fully defined" box into "definitely sound", and currently our surface area of the latter is woefully small.

I'm not sure how best to word this, opening this PR as a way to start discussion.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants