-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Niches of Cell
and others still not hidden
#87341
Comments
Cc @rust-lang/lang @eddyb |
Note: |
I guess this logic introduced by #68491 isn't useful unless rust/compiler/rustc_middle/src/ty/layout.rs Lines 956 to 960 in 18840b0
The issue is that What needs to happen for rust/compiler/rustc_middle/src/ty/layout.rs Lines 836 to 848 in 18840b0
However, that might impact e.g. miri negatively, as it may make it seem like |
Miri should be fine -- it both checks the "validity range" in |
Can we get some clarification on what the question being asked of @rust-lang/lang is here? Is the desire here to fix the semantics (making all of the cell types not have niches)? Or is there a question about the correct semantics? I see in the internals thread several questions about what transmutes are valid (e.g. between Could we get a specific ask to go with this nomination? |
It sounds like I should have explicitly included testing of However, these experimental results do concern me a little bit. Do they imply that under the current implementation, any wrapper type around Anyway, I'm pretty sure we are justified to make changes to hide any niche under |
Any wrapper that has a non-zero-sized field before the |
Basically, the question is: should I think the intention was that yes the niche should be hidden for all |
I was pretty confused by this response, because I took it to mean that the niche is only reexposed when there is another field before the UnsafeCell. After reading the linked playground, I think that interpretation was backwards. I.e., I think the intended statement of the current behavior is that: Any struct holding an UnsafeCell that has a non-zero-sized field before the UnsafeCell will not re-expose any niche within the UnsafeCell. (Which sounds pretty weird, in terms of why that ends up being the case where the rule is enforced...) Anyway, I'm personally in agreement with @RalfJung that the intention was to hide the niche for all occurrences of |
You're right, I wrote it backwards. Your final interpretation is the right one. |
My assumption (in #87341 (comment)) was that the initial implementation "just" has a bug, where it's hiding niches but not invalid values (which give rise to niches in the first place).
Did we guarantee anything around |
The lang team discussed this last week and came to the conclusion that:
It was unclear during the meeting whether there is actually a problem with the Mutex/RwLock types, which we believe at least on Linux are currently boxed, so the niche is not arising from the cell (but rather the box), which should be fine. But this doesn't change the fact that we should fix the no_niche repr to do the right thing. |
I wonder, do we really need a use core::mem::MaybeUninit;
#[repr(transparent)]
struct NoNiche<T>(MaybeUninit<T>);
impl<T> NoNiche<T> {
pub fn new(value: T) -> Self {
Self(MaybeUninit::new(value))
}
pub fn into_inner(self) -> T {
// Safety: we never store an uninitialized MaybeUninit
unsafe { self.0.assume_init() }
}
pub fn get(&self) -> &T {
// Safety: we never store an uninitialized MaybeUninit
unsafe { self.0.assume_init_ref() }
}
pub fn get_mut(&mut self) -> &mut T {
// Safety: we never store an uninitialized MaybeUninit
unsafe { self.0.assume_init_mut() }
}
} |
I mean, do we even need anything at all, other than the fact that Users might be fine with getting this effect, if they ever need it for some other reason, through either |
I am labeling this issue I-unsound because inserting niches into types that we've agreed are not supposed to have niches is definitely unsound territory. This bug is causing surprising soundness issues in the ecosystem:
Additionally, there is no good way for libraries to work around this bug. Using Example of an observable/consequential data race in what should be correct code: // [dependencies]
// crossbeam-utils = "=0.8.8"
use crossbeam_utils::atomic::AtomicCell;
use std::num::NonZeroU128;
use std::thread;
enum Enum {
NeverConstructed,
Cell(AtomicCell<NonZeroU128>),
}
static STATIC: Enum = Enum::Cell(AtomicCell::new(match NonZeroU128::new(1) {
Some(nonzero) => nonzero,
None => unreachable!(),
}));
fn main() {
thread::spawn(|| {
let cell = match &STATIC {
Enum::NeverConstructed => unreachable!(),
Enum::Cell(cell) => cell,
};
let x = NonZeroU128::new(0xFFFFFFFF_FFFFFFFF_00000000_00000000).unwrap();
let y = NonZeroU128::new(0x00000000_00000000_FFFFFFFF_FFFFFFFF).unwrap();
loop {
cell.store(x);
cell.store(y);
}
});
loop {
if let Enum::NeverConstructed = STATIC {
unreachable!(":(");
}
}
} |
UnsafeCell unfortunately lets the niches of the inner type "leak through" to the outer type, which can cause unsoundness. Fixes #29
Assigning priority as discussed in the Zulip thread of the Prioritization Working Group. @rustbot label -I-prioritize +P-high |
I described the correct fix in #87341 (comment) but didn't realize nobody picked it up last year, whoops. |
`UnsafeCell` blocks niches inside its nested type from being available outside fixes rust-lang#87341 This implements the plan by `@eddyb` in rust-lang#87341 (comment) Somewhat related PR (not strictly necessary, but that cleanup made this PR simpler): rust-lang#94527
`UnsafeCell` blocks niches inside its nested type from being available outside fixes rust-lang#87341 This implements the plan by ``@eddyb`` in rust-lang#87341 (comment) Somewhat related PR (not strictly necessary, but that cleanup made this PR simpler): rust-lang#94527
`UnsafeCell` blocks niches inside its nested type from being available outside fixes #87341 This implements the plan by `@eddyb` in rust-lang/rust#87341 (comment) Somewhat related PR (not strictly necessary, but that cleanup made this PR simpler): #94527
`UnsafeCell` blocks niches inside its nested type from being available outside fixes #87341 This implements the plan by `@eddyb` in rust-lang/rust#87341 (comment) Somewhat related PR (not strictly necessary, but that cleanup made this PR simpler): #94527
Original conversation can be found in this IRLO thread, including a request from @RalfJung to file a bug report.
PR 68491 was accepted to hide niches of types that are in an
UnsafeCell
. However, experiments show that niches are still exposed withinCell
,Mutex
, and others.Consider this code, adapted from a playground by @SkiFire13. It currently runs on the playground without panicking and optimizes down to a
ret
in the Godbolt explorer.I would have expected
Option<Cell<&()>>
to not exhibit the niche-exploiting size optimization. TheRwLock
andMutex
cases may or may not be problematic, depending on their platform-specific implementations. ButCell
in particular is a#[repr(transparent)]
wrapper aroundUnsafeCell
.Here is another playground by @SkiFire13 from which they observed:
This indicates that the behavior is not
Cell
-specific. And although there has been some discussion about revealing the niches forCell
in particular, the discussion in the PR and in this hackmd seem to indicate that the niches being exploited is not intentional, even forCell
. The behavior can be observed back until Rust 1.43 when the PR landed.Meta
Current playground (all versions) and Godbolt on all versions I tried from 1.43 forward.
The text was updated successfully, but these errors were encountered: