-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Cell::swap assumes Cells never overlap #80778
Comments
Cc @rust-lang/libs |
This makes the |
I don't think so. This makes the |
I share this hunch, although the code, out of nowhere, may look very contrived. Consider: use ::core::cell::Cell;
#[derive(Clone, Copy)]
#[repr(C)]
struct T(bool, u8, bool);
let ref mut s: [u8; 5] = [1, 2, 0, 1, 0];
let p = s.as_mut_ptr(); // SB: shared read-write over those 5 bytes (`&[Cell<u8>; 5]`)
let a: &Cell< T > = unsafe { &*p.cast() };
let b: &Cell< T > = unsafe { &*p.add(2).cast() };
// `a.2` overlaps with `b.0`
Cell::swap(a, b);
// Assuming the non-overlapping parts swap, the original 5-byte layout now is:
// `[1, 0, 0, 1, 2]`
b.get().2 // invalid `bool` So it seems like |
That is an example of the "strange" behavior not being the right thing for this code, but it is not an unsoundness of Current
IMO it seems rather strange to only panic for non- Btw, |
At the very least, an argument that Plus also, You could still construct an example using only As I understand it, there's three main cases here.
The third case is obviously the most restricting one, and Consider a type I could probably create an even worse example using underaligned types and relying on a specific endianness, perhaps even accidentally breaking validity invariants (think The only two resolutions I'm confident in are making |
I don't think |
In particular, they certainly can only overlap in restricted ways -- if they overlap such that one's
Oh I see... to only swap the non-overlapping parts we have to make an argument that these parts have the same type (safety+validity invariant). Which... seems utterly bogus now that I think about it. Worse, the invariant could correlate the overlapping with the non-overlapping part, as in your (Removed a whole bunch of nonsense where I try to find some special cases that work.) Yeah we should make it panic.^^
It is unsound to create an
I am confused, is there a negation missing somewhere in this sentence? |
Fair. I'm fairly confident that it'd be possible to come up with some safety invariant that a partial swap would break, though, with more data-dependent qualities.
The parenthetical is an optional relaxation of the potential requirement (but at the cost of a lot of complexity). |
But shouldn't it be forbidding overlapping |
Yeah, poor verbage. I meant roughly "forbidding overlapping |
notriddle brings up that partially overlapping fn unwrap_left<L, R>(either: &Cell<Either<L, R>>) -> &Cell<L> { ... }
let cell = Cell::new(Either::Left(...));
let left = unwrap_left(&cell);
cell.set(Either::Right(...));
left.get() |
Enums isn't really the same thing. That enum projections are unsound is already known, but as also discussed in that article, struct projections are typically ok, and the array projection discussed here is essentially a struct projection, not an enum projection. |
I would like to bring up my crate example usage: use std::cell::Cell;
use cell_project::cell_project as cp; // renamed for ergonomics
struct Point {
x: f32,
y: f32,
}
fn get_x_cell(point: &Cell<Point>) -> &Cell<f32> {
cp!(Point, point.x)
} Note: I'm not sure how i.e. This isn't possible, so it's not possible to project through to a field that's the same type as the parent. Alongside the fact that all fields are disjoint means that cell-project should only produce overlapping strurct Foo { inner: Foo } |
I agree with @Darksonn here. Sum types and product types are different, and |
You can use |
While you could use |
@SkiFire13 Oh I see, you're right. False alarm. |
make Cell::swap panic if the Cells partially overlap The following function ought to be sound: ```rust fn as_cell_of_array<T, const N: usize>(c: &[Cell<T>; N]) -> &Cell<[T; N]> { unsafe { transmute(c) } } ``` However, due to `Cell::swap`, it currently is not -- safe code can [cause a use-after-free](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=c9415799722d985ff7d2c2c997b724ca). This PR fixes that. Fixes rust-lang/rust#80778
make Cell::swap panic if the Cells partially overlap The following function ought to be sound: ```rust fn as_cell_of_array<T, const N: usize>(c: &[Cell<T>; N]) -> &Cell<[T; N]> { unsafe { transmute(c) } } ``` However, due to `Cell::swap`, it currently is not -- safe code can [cause a use-after-free](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=c9415799722d985ff7d2c2c997b724ca). This PR fixes that. Fixes rust-lang/rust#80778
make Cell::swap panic if the Cells partially overlap The following function ought to be sound: ```rust fn as_cell_of_array<T, const N: usize>(c: &[Cell<T>; N]) -> &Cell<[T; N]> { unsafe { transmute(c) } } ``` However, due to `Cell::swap`, it currently is not -- safe code can [cause a use-after-free](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=c9415799722d985ff7d2c2c997b724ca). This PR fixes that. Fixes rust-lang/rust#80778
In #80682 (comment), it was uncovered that
Cell::swap
is making some rather strong assumptions: twoCell
s with different address but the same type must not overlap. Not only is this a scarily non-local safety invariant, it is also fundamentally incomaptible with some APIs that ought to be correct, as demonstrated by this snippet (thanks to @xfix and @SkiFire13 for help with working out the example):Run it in Miri to see the issue:
ptr::swap
will duplicate parts of the memory range when there is overlap, which leads to double-drop (other parts of the memory range are just lost, leading to memory leaks, but that is not the main issue here).This is not itself a soundness issue as it requires unsafe code to trigger UB. But this likely reflects an unintended consequence of
Cell::swap
.Cell::swap
historyswap
is not mentioned in either of them.swap
was added in Addswap
method forCell
#39716.Possible solutions
as_cell_of_array
is unsound and document "non-overlap" as part of theCell
safety invariant. This seems very fragile.Cell::swap
not misbehave on overlap, either by panicking or by only swapping the non-overlapping parts.Cc @rust-lang/lang
The text was updated successfully, but these errors were encountered: