You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Mutex and RwLock are unsound in presence of discriminant elision #68206
Neither Mutex type nor RwLock type inhibit layout optimization and so either of
them might contain a niche. When a niche is used to perform a discriminant
elision extracting the discriminant of the outer type will inadvertently load
the content of Mutex / RwLock without proper synchronization.
Reacted by chpio, JQ Zhang, Stepan Koltsov, Soveu and Celthi
Can you show an example where this is happening? I can't come up with an example where the niche inside the Mutex is ever used where there even is an initialized Mutex at all.
Note that there's another niche in Mutex, the Box<sys::Mutex> field, which also enables e.g. Option<Mutex<i32>> to have the same size as Mutex<i32>. But in this case, indeed the data: UnsafeCell<T> field is used for storing the discriminant (presumably because it has a bigger niche?). This can be confirmed by looking at the assembly code generated for this playground and noticing that the same offset is used for locating the data in the mutex as for locating the discriminant.
A simple fix would be to change the UnsafeCell<T> field to UnsafeCell<MaybeUninit<T>>. In reality this field is always initialized, but MaybeUninit<T> has no niche regardless of T, so it would stop the layout optimizations.
I'd say the problem is rather with UnsafeCell itself. match shouldn't look for the tag through the boundary of an UnsafeCell at all, as accessing its value is inherently unsafe and can only be safe if you know the invariants of the safe abstraction on top, which match in general can not know. So UnsafeCell, like MaybeUninit shouldn't allow outside tags to use niches of its T.
Reacted by Hanna Kruppe, Steven Fackler, Felix S Klock II, Aaron Hill, Mikail Bagishov, Taylor Cramer, oli, Kornel, Paul Dicker, srrrse and 5 more
I'd say the problem is rather with UnsafeCell itself. match shouldn't look for the tag through the boundary of an UnsafeCell at all, as accessing its value is inherently unsafe and can only be safe if you know the invariants of the safe abstraction on top, which match in general can not know.
I tend to agree that the UnsafeCell<T> exported from std should not allow uses of niches within T
However, I also tend to think that we should have Cell<T> continue to allow use of the niches in itsT
This then raises the immediate question of whether we will need to make a new variant of UnsafeCell<T>, e.g. UnsafeCellWithExposedNiches<T> (deliberately ugly name there), that Cell can use...
Reacted by Paul Dicker, Kornel, Peter Atashian, Stepan Koltsov and bluss
Cell<T> having the same niche as T should be sound and sensible, but I don't expect it'll actually matter much whether it's done or not. In my experience, type combinations that would benefit from it are pretty rare: generally cells are not (directly) placed in enums, as they are rarely passed around by value in general. If a field with interior mutability is optional, then Cell<Option<T>> is easier to work with and strictly more general. Types containing cells being wrapped in enums is slightly more likely (e.g. with a fallible constructor (fn new(...) -> Result<Self, E>), but I still don't recall ever seeing it in practice.
On its own that isn't a reason to leave the optimization opporturnity on the table, it would still be nice to have if only for "peace of mind". But I don't think hacks such as adding a second variant of UnsafeCell are worthwhile. I'd rather wait and hope that Rust gains a more general and first-class (than rustc_layout_scalar_valid_range_{start,end}) way of declaring niches that allows Cell<T> to inherit the niche of T without such hacks.
But I don't think hacks such as adding a second variant of UnsafeCell are worthwhile. I'd rather wait and hope that Rust gains a more general and first-class (than rustc_layout_scalar_valid_range_{start,end}) way of declaring niches that allows Cell to inherit the niche of T without such hacks.
I think it would be useful to have a perma-unstable UnsafeCellWithExposedNiches type that's only used within libstd. If we can avoid penalizing user code (even if it's rare), I think we should do so. UnsafeCellWithExposedNiches could then be removed whenever a more general rustc_layout_scalar_valid_range_ is developed.
Ah, good catch! This is very closely related to rust-lang/unsafe-code-guidelines#204, I think. Basically, Rust's notion of "disjointness" says that the discriminant is disjoint from the actual enum data, but with niche optimizations that is not actually the case.
Notably, T and Cell<T> are still the same -- just when wrapped in some other type, they might differ (and specifically not when wrapped in a product type). So I do not see how there can be a problem here. But I might be missing something.
Activity
CryZe commentedon Jan 14, 2020
Can you show an example where this is happening? I can't come up with an example where the niche inside the Mutex is ever used where there even is an initialized Mutex at all.
tmiasko commentedon Jan 14, 2020
For example following code contains a data race on
x86_64-unknown-linux-gnu, between store inthread1and load inthread2.The
Option<Mutex<T>>combination is not uncommon. It happens when a mutex is placed inside a lazy static for example.hanna-kruppe commentedon Jan 14, 2020
Note that there's another niche in
Mutex, theBox<sys::Mutex>field, which also enables e.g.Option<Mutex<i32>>to have the same size asMutex<i32>. But in this case, indeed thedata: UnsafeCell<T>field is used for storing the discriminant (presumably because it has a bigger niche?). This can be confirmed by looking at the assembly code generated for this playground and noticing that the same offset is used for locating the data in the mutex as for locating the discriminant.A simple fix would be to change the
UnsafeCell<T>field toUnsafeCell<MaybeUninit<T>>. In reality this field is always initialized, butMaybeUninit<T>has no niche regardless ofT, so it would stop the layout optimizations.CryZe commentedon Jan 14, 2020
I'd say the problem is rather with UnsafeCell itself. match shouldn't look for the tag through the boundary of an UnsafeCell at all, as accessing its value is inherently unsafe and can only be safe if you know the invariants of the safe abstraction on top, which match in general can not know. So UnsafeCell, like MaybeUninit shouldn't allow outside tags to use niches of its T.
pnkfelix commentedon Jan 14, 2020
I tend to agree that the
UnsafeCell<T>exported fromstdshould not allow uses of niches withinTHowever, I also tend to think that we should have
Cell<T>continue to allow use of the niches in itsTThis then raises the immediate question of whether we will need to make a new variant of
UnsafeCell<T>, e.g.UnsafeCellWithExposedNiches<T>(deliberately ugly name there), thatCellcan use...hanna-kruppe commentedon Jan 14, 2020
Cell<T>having the same niche asTshould be sound and sensible, but I don't expect it'll actually matter much whether it's done or not. In my experience, type combinations that would benefit from it are pretty rare: generally cells are not (directly) placed in enums, as they are rarely passed around by value in general. If a field with interior mutability is optional, thenCell<Option<T>>is easier to work with and strictly more general. Types containing cells being wrapped in enums is slightly more likely (e.g. with a fallible constructor (fn new(...) -> Result<Self, E>), but I still don't recall ever seeing it in practice.On its own that isn't a reason to leave the optimization opporturnity on the table, it would still be nice to have if only for "peace of mind". But I don't think hacks such as adding a second variant of
UnsafeCellare worthwhile. I'd rather wait and hope that Rust gains a more general and first-class (thanrustc_layout_scalar_valid_range_{start,end}) way of declaring niches that allowsCell<T>to inherit the niche ofTwithout such hacks.Aaron1011 commentedon Jan 14, 2020
I think it would be useful to have a perma-unstable
UnsafeCellWithExposedNichestype that's only used withinlibstd. If we can avoid penalizing user code (even if it's rare), I think we should do so.UnsafeCellWithExposedNichescould then be removed whenever a more generalrustc_layout_scalar_valid_range_is developed.MikailBag commentedon Jan 15, 2020
It seems to me that this bug also happens in
parking_lotand atomic types, because they don't put MaybeUninit in the UnsafeCell. Is it correct?tmiasko commentedon Jan 15, 2020
Atomics have no niches, but
Synctypes that useUnsafeCellwith user provided type are likely to be affected, includingparking_lot.In terms of naming one might signify
!Syncaspect and name variant that participates in layout optimization asUnsafeUnsyncCell.RalfJung commentedon Jan 15, 2020
Ah, good catch! This is very closely related to rust-lang/unsafe-code-guidelines#204, I think. Basically, Rust's notion of "disjointness" says that the discriminant is disjoint from the actual enum data, but with niche optimizations that is not actually the case.
28 remaining items
eddyb commentedon Jan 30, 2020
@rpjohnst I think it's fine because it doesn't let you make any assumptions, the
Cellprojections only go through product types.Also, creating a
Cellwhere there was none requires a mutable reference, which I believe is wrong enough to account for all possible effects.Rollup merge of rust-lang#68491 - pnkfelix:hide-niches-under-unsafe-c…
RalfJung commentedon Jan 30, 2020
Notably,
TandCell<T>are still the same -- just when wrapped in some other type, they might differ (and specifically not when wrapped in a product type). So I do not see how there can be a problem here. But I might be missing something.rfcbot commentedon Feb 1, 2020
The final comment period, with a disposition to merge, as per the review above, is now complete.
As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.
The RFC will be merged soon.
nikomatsakis commentedon Feb 3, 2020
@rpjohnst a good catch for sure, definitely something to think about.
pnkfelix commentedon Feb 3, 2020
(assigning self, since I authored PR #68491)
Bumped patch release,updated layout for Cell and UnsafeCell due to ru…
Unsafecell fix (#32)