Skip to content

Commit 4e5b31c

Browse files
committed
Auto merge of #115166 - Urgau:invalid_ref_casting-invalid-unsafecell-usage, r=est31
Lint on invalid usage of `UnsafeCell::raw_get` in reference casting This PR proposes to take into account `UnsafeCell::raw_get` method call for non-Freeze types for the `invalid_reference_casting` lint. The goal of this is to catch those kind of invalid reference casting: ```rust fn as_mut<T>(x: &T) -> &mut T { unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) } //~^ ERROR casting `&T` to `&mut T` is undefined behavior } ``` r? `@est31`
2 parents b0d4553 + 89800a2 commit 4e5b31c

File tree

5 files changed

+117
-25
lines changed

5 files changed

+117
-25
lines changed

Diff for: compiler/rustc_lint/src/reference_casting.rs

+39-5
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,11 @@ fn is_operation_we_care_about<'tcx>(
128128
fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
129129
let e = e.peel_blocks();
130130

131-
fn from_casts<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
131+
fn from_casts<'tcx>(
132+
cx: &LateContext<'tcx>,
133+
e: &'tcx Expr<'tcx>,
134+
need_check_freeze: &mut bool,
135+
) -> Option<&'tcx Expr<'tcx>> {
132136
// <expr> as *mut ...
133137
let mut e = if let ExprKind::Cast(e, t) = e.kind
134138
&& let ty::RawPtr(TypeAndMut { mutbl: Mutability::Mut, .. }) = cx.typeck_results().node_type(t.hir_id).kind() {
@@ -138,6 +142,14 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
138142
&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
139143
&& cx.tcx.is_diagnostic_item(sym::ptr_cast_mut, def_id) {
140144
expr
145+
// UnsafeCell::raw_get(<expr>)
146+
} else if let ExprKind::Call(path, [arg]) = e.kind
147+
&& let ExprKind::Path(ref qpath) = path.kind
148+
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
149+
&& cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id)
150+
{
151+
*need_check_freeze = true;
152+
arg
141153
} else {
142154
return None;
143155
};
@@ -160,11 +172,18 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
160172
{
161173
had_at_least_one_cast = true;
162174
expr
163-
// ptr::from_ref(<expr>)
175+
// ptr::from_ref(<expr>) or UnsafeCell::raw_get(<expr>)
164176
} else if let ExprKind::Call(path, [arg]) = e.kind
165177
&& let ExprKind::Path(ref qpath) = path.kind
166178
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
167-
&& cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) {
179+
&& matches!(
180+
cx.tcx.get_diagnostic_name(def_id),
181+
Some(sym::ptr_from_ref | sym::unsafe_cell_raw_get)
182+
)
183+
{
184+
if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) {
185+
*need_check_freeze = true;
186+
}
168187
return Some(arg);
169188
} else if had_at_least_one_cast {
170189
return Some(e);
@@ -190,10 +209,25 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
190209
}
191210
}
192211

193-
let Some(e) = from_casts(cx, e).or_else(|| from_transmute(cx, e)) else {
212+
let mut need_check_freeze = false;
213+
let Some(e) = from_casts(cx, e, &mut need_check_freeze).or_else(|| from_transmute(cx, e))
214+
else {
194215
return false;
195216
};
196217

197218
let e = e.peel_blocks();
198-
matches!(cx.typeck_results().node_type(e.hir_id).kind(), ty::Ref(_, _, Mutability::Not))
219+
let node_type = cx.typeck_results().node_type(e.hir_id);
220+
if let ty::Ref(_, inner_ty, Mutability::Not) = node_type.kind() {
221+
// If an UnsafeCell method is involved we need to additionaly check the
222+
// inner type for the presence of the Freeze trait (ie does NOT contain
223+
// an UnsafeCell), since in that case we would incorrectly lint on valid casts.
224+
//
225+
// We also consider non concrete skeleton types (ie generics)
226+
// to be an issue since there is no way to make it safe for abitrary types.
227+
!need_check_freeze
228+
|| inner_ty.is_freeze(cx.tcx, cx.param_env)
229+
|| !inner_ty.has_concrete_skeleton()
230+
} else {
231+
false
232+
}
199233
}

Diff for: compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1639,6 +1639,7 @@ symbols! {
16391639
unsafe_block_in_unsafe_fn,
16401640
unsafe_cell,
16411641
unsafe_cell_from_mut,
1642+
unsafe_cell_raw_get,
16421643
unsafe_no_drop_flag,
16431644
unsafe_pin_internals,
16441645
unsize,

Diff for: library/core/src/cell.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2165,6 +2165,7 @@ impl<T: ?Sized> UnsafeCell<T> {
21652165
#[inline(always)]
21662166
#[stable(feature = "unsafe_cell_raw_get", since = "1.56.0")]
21672167
#[rustc_const_stable(feature = "unsafe_cell_raw_get", since = "1.56.0")]
2168+
#[rustc_diagnostic_item = "unsafe_cell_raw_get"]
21682169
pub const fn raw_get(this: *const Self) -> *mut T {
21692170
// We can just cast the pointer from `UnsafeCell<T>` to `T` because of
21702171
// #[repr(transparent)]. This exploits std's special status, there is

Diff for: tests/ui/lint/reference_casting.rs

+28
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ unsafe fn ref_to_mut() {
3636
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
3737
let _num = &mut *std::mem::transmute::<_, *mut i32>(num);
3838
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
39+
let _num = &mut *std::cell::UnsafeCell::raw_get(
40+
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
41+
num as *const i32 as *const std::cell::UnsafeCell<i32>
42+
);
3943

4044
let deferred = num as *const i32 as *mut i32;
4145
let _num = &mut *deferred;
@@ -50,6 +54,16 @@ unsafe fn ref_to_mut() {
5054
&mut *((this as *const _) as *mut _)
5155
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
5256
}
57+
58+
fn as_mut<T>(x: &T) -> &mut T {
59+
unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
60+
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
61+
}
62+
63+
fn as_mut_i32(x: &i32) -> &mut i32 {
64+
unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
65+
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
66+
}
5367
}
5468

5569
unsafe fn assign_to_ref() {
@@ -111,6 +125,20 @@ unsafe fn no_warn() {
111125
let mut value = 3;
112126
let value: *const i32 = &mut value;
113127
*(value as *const i16 as *mut i16) = 42;
128+
129+
fn safe_as_mut<T>(x: &std::cell::UnsafeCell<T>) -> &mut T {
130+
unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
131+
}
132+
133+
fn cell_as_mut(x: &std::cell::Cell<i32>) -> &mut i32 {
134+
unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
135+
}
136+
137+
#[repr(transparent)]
138+
struct DoesContainUnsafeCell(std::cell::UnsafeCell<i32>);
139+
fn safe_as_mut2(x: &DoesContainUnsafeCell) -> &mut DoesContainUnsafeCell {
140+
unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
141+
}
114142
}
115143

116144
fn main() {}

Diff for: tests/ui/lint/reference_casting.stderr

+48-20
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,19 @@ LL | let _num = &mut *std::mem::transmute::<_, *mut i32>(num);
8080
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
8181

8282
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
83-
--> $DIR/reference_casting.rs:41:16
83+
--> $DIR/reference_casting.rs:39:16
84+
|
85+
LL | let _num = &mut *std::cell::UnsafeCell::raw_get(
86+
| ________________^
87+
LL | |
88+
LL | | num as *const i32 as *const std::cell::UnsafeCell<i32>
89+
LL | | );
90+
| |_____^
91+
|
92+
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
93+
94+
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
95+
--> $DIR/reference_casting.rs:45:16
8496
|
8597
LL | let deferred = num as *const i32 as *mut i32;
8698
| ----------------------------- casting happend here
@@ -90,7 +102,7 @@ LL | let _num = &mut *deferred;
90102
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
91103

92104
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
93-
--> $DIR/reference_casting.rs:44:16
105+
--> $DIR/reference_casting.rs:48:16
94106
|
95107
LL | let deferred = (std::ptr::from_ref(num) as *const i32 as *const i32).cast_mut() as *mut i32;
96108
| ---------------------------------------------------------------------------- casting happend here
@@ -100,79 +112,95 @@ LL | let _num = &mut *deferred;
100112
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
101113

102114
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
103-
--> $DIR/reference_casting.rs:46:16
115+
--> $DIR/reference_casting.rs:50:16
104116
|
105117
LL | let _num = &mut *(num as *const _ as usize as *mut i32);
106118
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
107119
|
108120
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
109121

110122
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
111-
--> $DIR/reference_casting.rs:50:9
123+
--> $DIR/reference_casting.rs:54:9
112124
|
113125
LL | &mut *((this as *const _) as *mut _)
114126
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
115127
|
116128
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
117129

130+
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
131+
--> $DIR/reference_casting.rs:59:18
132+
|
133+
LL | unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
134+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
135+
|
136+
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
137+
138+
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
139+
--> $DIR/reference_casting.rs:64:18
140+
|
141+
LL | unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) }
142+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
143+
|
144+
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
145+
118146
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
119-
--> $DIR/reference_casting.rs:60:5
147+
--> $DIR/reference_casting.rs:74:5
120148
|
121149
LL | *(a as *const _ as *mut _) = String::from("Replaced");
122150
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
123151
|
124152
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
125153

126154
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
127-
--> $DIR/reference_casting.rs:62:5
155+
--> $DIR/reference_casting.rs:76:5
128156
|
129157
LL | *(a as *const _ as *mut String) += " world";
130158
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
131159
|
132160
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
133161

134162
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
135-
--> $DIR/reference_casting.rs:64:5
163+
--> $DIR/reference_casting.rs:78:5
136164
|
137165
LL | *std::ptr::from_ref(num).cast_mut() += 1;
138166
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
139167
|
140168
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
141169

142170
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
143-
--> $DIR/reference_casting.rs:66:5
171+
--> $DIR/reference_casting.rs:80:5
144172
|
145173
LL | *std::ptr::from_ref({ num }).cast_mut() += 1;
146174
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
147175
|
148176
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
149177

150178
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
151-
--> $DIR/reference_casting.rs:68:5
179+
--> $DIR/reference_casting.rs:82:5
152180
|
153181
LL | *{ std::ptr::from_ref(num) }.cast_mut() += 1;
154182
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
155183
|
156184
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
157185

158186
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
159-
--> $DIR/reference_casting.rs:70:5
187+
--> $DIR/reference_casting.rs:84:5
160188
|
161189
LL | *(std::ptr::from_ref({ num }) as *mut i32) += 1;
162190
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
163191
|
164192
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
165193

166194
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
167-
--> $DIR/reference_casting.rs:72:5
195+
--> $DIR/reference_casting.rs:86:5
168196
|
169197
LL | *std::mem::transmute::<_, *mut i32>(num) += 1;
170198
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
171199
|
172200
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
173201

174202
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
175-
--> $DIR/reference_casting.rs:74:5
203+
--> $DIR/reference_casting.rs:88:5
176204
|
177205
LL | / std::ptr::write(
178206
LL | |
@@ -184,7 +212,7 @@ LL | | );
184212
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
185213

186214
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
187-
--> $DIR/reference_casting.rs:81:5
215+
--> $DIR/reference_casting.rs:95:5
188216
|
189217
LL | let value = num as *const i32 as *mut i32;
190218
| ----------------------------- casting happend here
@@ -194,23 +222,23 @@ LL | *value = 1;
194222
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
195223

196224
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
197-
--> $DIR/reference_casting.rs:83:5
225+
--> $DIR/reference_casting.rs:97:5
198226
|
199227
LL | *(num as *const i32).cast::<i32>().cast_mut() = 2;
200228
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
201229
|
202230
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
203231

204232
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
205-
--> $DIR/reference_casting.rs:85:5
233+
--> $DIR/reference_casting.rs:99:5
206234
|
207235
LL | *(num as *const _ as usize as *mut i32) = 2;
208236
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
209237
|
210238
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
211239

212240
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
213-
--> $DIR/reference_casting.rs:87:5
241+
--> $DIR/reference_casting.rs:101:5
214242
|
215243
LL | let value = num as *const i32 as *mut i32;
216244
| ----------------------------- casting happend here
@@ -221,7 +249,7 @@ LL | std::ptr::write(value, 2);
221249
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
222250

223251
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
224-
--> $DIR/reference_casting.rs:89:5
252+
--> $DIR/reference_casting.rs:103:5
225253
|
226254
LL | let value = num as *const i32 as *mut i32;
227255
| ----------------------------- casting happend here
@@ -232,7 +260,7 @@ LL | std::ptr::write_unaligned(value, 2);
232260
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
233261

234262
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
235-
--> $DIR/reference_casting.rs:91:5
263+
--> $DIR/reference_casting.rs:105:5
236264
|
237265
LL | let value = num as *const i32 as *mut i32;
238266
| ----------------------------- casting happend here
@@ -243,12 +271,12 @@ LL | std::ptr::write_volatile(value, 2);
243271
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
244272

245273
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
246-
--> $DIR/reference_casting.rs:95:9
274+
--> $DIR/reference_casting.rs:109:9
247275
|
248276
LL | *(this as *const _ as *mut _) = a;
249277
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
250278
|
251279
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
252280

253-
error: aborting due to 29 previous errors
281+
error: aborting due to 32 previous errors
254282

0 commit comments

Comments
 (0)