Allow UnsafeCell content access without get in invalid_reference_casting lint - #159960
Conversation
|
r? @mejrs rustbot has assigned @mejrs. Use Why was this reviewer chosen?The reviewer was selected based on:
|
| // make sure we don't ICE on it when trying to | ||
| // determine if we should lint on it or not. | ||
| *((&std::cell::UnsafeCell::new(0)) as *const _ as *mut i32) = 5; | ||
|
|
||
| let cell = &std::cell::UnsafeCell::new(0); | ||
| let _num = &mut *(cell.get() as *mut i32); | ||
| let _num = &mut *(cell as *const _ as *mut i32); | ||
|
|
||
| fn safe_as_mut<T>(x: &std::cell::UnsafeCell<T>) -> &mut T { | ||
| unsafe fn get_mut_unchecked<T>(x: &std::cell::UnsafeCell<T>) -> &mut T { | ||
| unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) } | ||
| } | ||
|
|
||
| fn cell_as_mut(x: &std::cell::Cell<i32>) -> &mut i32 { | ||
| unsafe fn get_mut_unchecked2<T>(ptr: &std::cell::UnsafeCell<T>) -> &mut T { | ||
| let t = ptr as *const std::cell::UnsafeCell<T> as *mut T; | ||
| unsafe { &mut *t } | ||
| } | ||
|
|
||
| unsafe fn cell_as_mut(x: &std::cell::Cell<i32>) -> &mut i32 { | ||
| unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) } | ||
| } | ||
|
|
||
| unsafe fn cell_as_mut2(x: &std::cell::Cell<i32>) -> &mut i32 { | ||
| unsafe { &mut *(x as *const std::cell::Cell<i32> as *mut i32) } | ||
| } | ||
|
|
||
| #[repr(transparent)] | ||
| struct DoesContainUnsafeCell(std::cell::UnsafeCell<i32>); | ||
| fn safe_as_mut2(x: &DoesContainUnsafeCell) -> &mut DoesContainUnsafeCell { | ||
|
|
||
| unsafe fn get_mut_unchecked3(x: &DoesContainUnsafeCell) -> &mut DoesContainUnsafeCell { | ||
| unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) } | ||
| } | ||
|
|
||
| unsafe fn get_mut_unchecked4(x: &DoesContainUnsafeCell) -> &mut DoesContainUnsafeCell { | ||
| unsafe { &mut *(x as *const DoesContainUnsafeCell as *mut _) } | ||
| } |
There was a problem hiding this comment.
@RalfJung could you double-check and confirm that all of those casts are now fine.
There was a problem hiding this comment.
Depends on what you mean by "fine". ;)
They are not immediate UB. But getting a reference into a Cell is still incredibly dangerous. Just as dangerous as Cell::as_ptr though.
There was a problem hiding this comment.
Yeah, by "fine" I mean no longer immediate UB, it can still be very UB. Thanks for checking.
|
Wow that was fast ❤️ |
714e85a to
3e3ab53
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
Are these casts valid? The lint, as implemented here, does not lint on them. use std::cell::UnsafeCell;
use std::marker::PhantomData;
#[repr(transparent)]
struct MyUnsafeCell<T> {
data: T,
_p: PhantomData<UnsafeCell<T>>,
}
unsafe fn cast<T>(ptr: &MyUnsafeCell<T>) -> &mut T {
let t = ptr as *const MyUnsafeCell<T> as *mut T;
unsafe { &mut *t }
}and unsafe fn cast<T>(ptr: &UnsafeCell<T>) -> &mut UnsafeCell<T> {
let t = ptr as *const _ as *mut _;
unsafe { &mut *t }
} |
|
Those casts are both dangerous but not immediate UB. |
@RalfJung are you sure ? the first one triggers miri instantaneously. use std::cell::UnsafeCell;
use std::marker::PhantomData;
#[repr(transparent)]
struct MyUnsafeCell<T> {
data: T,
_p: PhantomData<UnsafeCell<T>>,
}
#[allow(invalid_reference_casting)]
unsafe fn cast<T>(ptr: &MyUnsafeCell<T>) -> &mut T {
let t = ptr as *const MyUnsafeCell<T> as *mut T;
unsafe { &mut *t }
}
fn main() {
let mut c : MyUnsafeCell<u8> = MyUnsafeCell { data : 2, _p : PhantomData };
unsafe {cast(&mut c) };
} |
|
Oh wait I didn't read the example properly. I thought you were asking about what Please always give context for questions like this to make it more likely I look at the right parts of the code. Dumping an uncommented piece of code with weird corner cases without even mentioning the thought process that lead to the example isn't a good way to ask a question. Unfortunately I am often in a rush so I only dig deeper when there's some clear indication where to dig. Sorry for that. OTOH the lint is not expected to find all UB so not linting on these is not necessarily a problem. |
My apologies, I was also a bit in a rush myself 😅 Anyway, my mental model of interior mutability is that you always have to go through/pierce/peel/unwrap (what's the precise language for this?) But in the second example: unsafe fn cast<T>(ptr: &UnsafeCell<T>) -> &mut UnsafeCell<T> {
let t = ptr as *const _ as *mut _;
unsafe { &mut *t }
}...we cast the UnsafeCell itself, we don't peel it away Is that also OK?
That's true, it doesn't have to be perfect. However what I see occasionally is:
It would be nice to make them jump a little bit higher. |
There was a problem hiding this comment.
Regardless, the impl looks good to me so no need to block on anything.
@Urgau up to you if you want to make the lint smarter, either here or in a followup
r=me (after nits) if not
That's not quite right. It's more that UnsafeCell "punches a hole" in the immutability requirement of That's why the 2nd example is okay. |
3e3ab53 to
6b0ea30
Compare
|
@bors r=mejrs |
…t, r=mejrs Allow `UnsafeCell` content access without `get` in `invalid_reference_casting` lint rust-lang#159730 relaxed the rules so that it's no longer necessary to use `UnsafeCell::raw_get` or `UnsafeCell::get` to access the content of an unsafe cell. As a consequence this means that code this like is no longer invalid: ```rust use std::cell::UnsafeCell; unsafe fn get_mut_unchecked<T>(ptr: &UnsafeCell<T>) -> &mut T { let t = ptr as *const UnsafeCell<T> as *mut T; unsafe { &mut *t } } ``` Fixes rust-lang#159915 cc @RalfJung
Rollup of 9 pull requests Successful merges: - #153563 (Lint against iterator functions that panic when `N` is zero ) - #159960 (Allow `UnsafeCell` content access without `get` in `invalid_reference_casting` lint) - #158893 (Clarify preconditions of raw size/align methods) - #159220 (Don't optimize across storage markers in SimplifyComparisonIntegral) - #159309 (Move tests batch 18) - #159450 (Add codegen test for enum clone) - #160017 (Make BorrowSet methods public again) - #160022 (Refactor rustc_hir re-exports) - #160041 (Correct tracking issue for `casefold` feature)
Rollup merge of #159960 - Urgau:invalid_ref-safe-interior-mut, r=mejrs Allow `UnsafeCell` content access without `get` in `invalid_reference_casting` lint #159730 relaxed the rules so that it's no longer necessary to use `UnsafeCell::raw_get` or `UnsafeCell::get` to access the content of an unsafe cell. As a consequence this means that code this like is no longer invalid: ```rust use std::cell::UnsafeCell; unsafe fn get_mut_unchecked<T>(ptr: &UnsafeCell<T>) -> &mut T { let t = ptr as *const UnsafeCell<T> as *mut T; unsafe { &mut *t } } ``` Fixes #159915 cc @RalfJung
#159730 relaxed the rules so that it's no longer necessary to use
UnsafeCell::raw_getorUnsafeCell::getto access the content of an unsafe cell.As a consequence this means that code this like is no longer invalid:
Fixes #159915
cc @RalfJung