Skip to content

Commit 2667d85

Browse files
authored
Rollup merge of #114784 - Urgau:many-improve-invalid_reference_casting-lint, r=est31
Improve `invalid_reference_casting` lint This PR improves the `invalid_reference_casting` lint: - by considering an unlimited number of casts instead only const to mut ptr - by also considering ptr-to-integer and integer-to-ptr casts - by also taking into account [`ptr::cast`](https://doc.rust-lang.org/std/primitive.pointer.html#method.cast), [`ptr::cast`](https://doc.rust-lang.org/std/primitive.pointer.html#method.cast-1) and [`ptr::cast_const`](https://doc.rust-lang.org/std/primitive.pointer.html#method.cast_const) Most of this improvements comes from skimming Github Code Search result for [`&mut \*.*as \*const`](https://github.com/search?q=lang%3Arust+%2F%26mut+%5C*.*as+%5C*const%2F&type=code) r? ``@est31`` (maybe)
2 parents 52d6947 + 91b05f8 commit 2667d85

File tree

6 files changed

+137
-35
lines changed

6 files changed

+137
-35
lines changed

compiler/rustc_lint/src/reference_casting.rs

+31-18
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
100100

101101
fn from_casts<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
102102
// <expr> as *mut ...
103-
let e = if let ExprKind::Cast(e, t) = e.kind
103+
let mut e = if let ExprKind::Cast(e, t) = e.kind
104104
&& let ty::RawPtr(TypeAndMut { mutbl: Mutability::Mut, .. }) = cx.typeck_results().node_type(t.hir_id).kind() {
105105
e
106106
// <expr>.cast_mut()
@@ -112,23 +112,36 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
112112
return None;
113113
};
114114

115-
let e = e.peel_blocks();
116-
117-
// <expr> as *const ...
118-
let e = if let ExprKind::Cast(e, t) = e.kind
119-
&& let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) = cx.typeck_results().node_type(t.hir_id).kind() {
120-
e
121-
// ptr::from_ref(<expr>)
122-
} else if let ExprKind::Call(path, [arg]) = e.kind
123-
&& let ExprKind::Path(ref qpath) = path.kind
124-
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
125-
&& cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) {
126-
arg
127-
} else {
128-
return None;
129-
};
130-
131-
Some(e)
115+
let mut had_at_least_one_cast = false;
116+
loop {
117+
e = e.peel_blocks();
118+
// <expr> as *mut/const ... or <expr> as <uint>
119+
e = if let ExprKind::Cast(expr, t) = e.kind
120+
&& matches!(cx.typeck_results().node_type(t.hir_id).kind(), ty::RawPtr(_) | ty::Uint(_)) {
121+
had_at_least_one_cast = true;
122+
expr
123+
// <expr>.cast(), <expr>.cast_mut() or <expr>.cast_const()
124+
} else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
125+
&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
126+
&& matches!(
127+
cx.tcx.get_diagnostic_name(def_id),
128+
Some(sym::ptr_cast | sym::const_ptr_cast | sym::ptr_cast_mut | sym::ptr_cast_const)
129+
)
130+
{
131+
had_at_least_one_cast = true;
132+
expr
133+
// ptr::from_ref(<expr>)
134+
} else if let ExprKind::Call(path, [arg]) = e.kind
135+
&& let ExprKind::Path(ref qpath) = path.kind
136+
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
137+
&& cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) {
138+
return Some(arg);
139+
} else if had_at_least_one_cast {
140+
return Some(e);
141+
} else {
142+
return None;
143+
};
144+
}
132145
}
133146

134147
fn from_transmute<'tcx>(

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ symbols! {
543543
const_panic_fmt,
544544
const_param_ty,
545545
const_precise_live_drops,
546+
const_ptr_cast,
546547
const_raw_ptr_deref,
547548
const_raw_ptr_to_usize_cast,
548549
const_refs_to_cell,
@@ -1160,6 +1161,7 @@ symbols! {
11601161
profiler_runtime,
11611162
ptr,
11621163
ptr_cast,
1164+
ptr_cast_const,
11631165
ptr_cast_mut,
11641166
ptr_const_is_null,
11651167
ptr_from_mut,

library/core/src/ptr/const_ptr.rs

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ impl<T: ?Sized> *const T {
5555
/// Casts to a pointer of another type.
5656
#[stable(feature = "ptr_cast", since = "1.38.0")]
5757
#[rustc_const_stable(feature = "const_ptr_cast", since = "1.38.0")]
58+
#[rustc_diagnostic_item = "const_ptr_cast"]
5859
#[inline(always)]
5960
pub const fn cast<U>(self) -> *const U {
6061
self as _

library/core/src/ptr/mut_ptr.rs

+1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ impl<T: ?Sized> *mut T {
112112
/// [`cast_mut`]: #method.cast_mut
113113
#[stable(feature = "ptr_const_cast", since = "1.65.0")]
114114
#[rustc_const_stable(feature = "ptr_const_cast", since = "1.65.0")]
115+
#[rustc_diagnostic_item = "ptr_cast_const"]
115116
#[inline(always)]
116117
pub const fn cast_const(self) -> *const T {
117118
self as _

tests/ui/lint/reference_casting.rs

+29
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ extern "C" {
99
fn int_ffi(c: *mut i32);
1010
}
1111

12+
fn static_u8() -> &'static u8 {
13+
&8
14+
}
15+
1216
unsafe fn ref_to_mut() {
1317
let num = &3i32;
1418

@@ -24,12 +28,28 @@ unsafe fn ref_to_mut() {
2428
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
2529
let _num = &mut *(std::ptr::from_ref({ num }) as *mut i32);
2630
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
31+
let _num = &mut *(num as *const i32).cast::<i32>().cast_mut();
32+
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
33+
let _num = &mut *(num as *const i32).cast::<i32>().cast_mut().cast_const().cast_mut();
34+
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
35+
let _num = &mut *(std::ptr::from_ref(static_u8()) as *mut i32);
36+
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
2737
let _num = &mut *std::mem::transmute::<_, *mut i32>(num);
2838
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
2939

3040
let deferred = num as *const i32 as *mut i32;
3141
let _num = &mut *deferred;
3242
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
43+
let deferred = (std::ptr::from_ref(num) as *const i32 as *const i32).cast_mut() as *mut i32;
44+
let _num = &mut *deferred;
45+
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
46+
let _num = &mut *(num as *const _ as usize as *mut i32);
47+
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
48+
49+
unsafe fn generic_ref_cast_mut<T>(this: &T) -> &mut T {
50+
&mut *((this as *const _) as *mut _)
51+
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
52+
}
3353
}
3454

3555
unsafe fn assign_to_ref() {
@@ -55,6 +75,15 @@ unsafe fn assign_to_ref() {
5575
let value = num as *const i32 as *mut i32;
5676
*value = 1;
5777
//~^ ERROR assigning to `&T` is undefined behavior
78+
*(num as *const i32).cast::<i32>().cast_mut() = 2;
79+
//~^ ERROR assigning to `&T` is undefined behavior
80+
*(num as *const _ as usize as *mut i32) = 2;
81+
//~^ ERROR assigning to `&T` is undefined behavior
82+
83+
unsafe fn generic_assign_to_ref<T>(this: &T, a: T) {
84+
*(this as *const _ as *mut _) = a;
85+
//~^ ERROR assigning to `&T` is undefined behavior
86+
}
5887
}
5988

6089
unsafe fn no_warn() {
+73-17
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,160 @@
11
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
2-
--> $DIR/reference_casting.rs:15:16
2+
--> $DIR/reference_casting.rs:19:16
33
|
44
LL | let _num = &mut *(num as *const i32 as *mut i32);
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= note: `#[deny(invalid_reference_casting)]` on by default
88

99
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
10-
--> $DIR/reference_casting.rs:17:16
10+
--> $DIR/reference_casting.rs:21:16
1111
|
1212
LL | let _num = &mut *(num as *const i32).cast_mut();
1313
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1414

1515
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
16-
--> $DIR/reference_casting.rs:19:16
16+
--> $DIR/reference_casting.rs:23:16
1717
|
1818
LL | let _num = &mut *std::ptr::from_ref(num).cast_mut();
1919
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2020

2121
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
22-
--> $DIR/reference_casting.rs:21:16
22+
--> $DIR/reference_casting.rs:25:16
2323
|
2424
LL | let _num = &mut *std::ptr::from_ref({ num }).cast_mut();
2525
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2626

2727
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
28-
--> $DIR/reference_casting.rs:23:16
28+
--> $DIR/reference_casting.rs:27:16
2929
|
3030
LL | let _num = &mut *{ std::ptr::from_ref(num) }.cast_mut();
3131
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3232

3333
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
34-
--> $DIR/reference_casting.rs:25:16
34+
--> $DIR/reference_casting.rs:29:16
3535
|
3636
LL | let _num = &mut *(std::ptr::from_ref({ num }) as *mut i32);
3737
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3838

3939
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
40-
--> $DIR/reference_casting.rs:27:16
40+
--> $DIR/reference_casting.rs:31:16
41+
|
42+
LL | let _num = &mut *(num as *const i32).cast::<i32>().cast_mut();
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44+
45+
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
46+
--> $DIR/reference_casting.rs:33:16
47+
|
48+
LL | let _num = &mut *(num as *const i32).cast::<i32>().cast_mut().cast_const().cast_mut();
49+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
50+
51+
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
52+
--> $DIR/reference_casting.rs:35:16
53+
|
54+
LL | let _num = &mut *(std::ptr::from_ref(static_u8()) as *mut i32);
55+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56+
57+
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
58+
--> $DIR/reference_casting.rs:37:16
4159
|
4260
LL | let _num = &mut *std::mem::transmute::<_, *mut i32>(num);
4361
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4462

4563
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
46-
--> $DIR/reference_casting.rs:31:16
64+
--> $DIR/reference_casting.rs:41:16
4765
|
4866
LL | let deferred = num as *const i32 as *mut i32;
4967
| ----------------------------- casting happend here
5068
LL | let _num = &mut *deferred;
5169
| ^^^^^^^^^^^^^^
5270

71+
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
72+
--> $DIR/reference_casting.rs:44:16
73+
|
74+
LL | let deferred = (std::ptr::from_ref(num) as *const i32 as *const i32).cast_mut() as *mut i32;
75+
| ---------------------------------------------------------------------------- casting happend here
76+
LL | let _num = &mut *deferred;
77+
| ^^^^^^^^^^^^^^
78+
79+
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
80+
--> $DIR/reference_casting.rs:46:16
81+
|
82+
LL | let _num = &mut *(num as *const _ as usize as *mut i32);
83+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
84+
85+
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
86+
--> $DIR/reference_casting.rs:50:9
87+
|
88+
LL | &mut *((this as *const _) as *mut _)
89+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
90+
5391
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
54-
--> $DIR/reference_casting.rs:40:5
92+
--> $DIR/reference_casting.rs:60:5
5593
|
5694
LL | *(a as *const _ as *mut _) = String::from("Replaced");
5795
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5896

5997
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
60-
--> $DIR/reference_casting.rs:42:5
98+
--> $DIR/reference_casting.rs:62:5
6199
|
62100
LL | *(a as *const _ as *mut String) += " world";
63101
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
64102

65103
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
66-
--> $DIR/reference_casting.rs:44:5
104+
--> $DIR/reference_casting.rs:64:5
67105
|
68106
LL | *std::ptr::from_ref(num).cast_mut() += 1;
69107
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
70108

71109
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
72-
--> $DIR/reference_casting.rs:46:5
110+
--> $DIR/reference_casting.rs:66:5
73111
|
74112
LL | *std::ptr::from_ref({ num }).cast_mut() += 1;
75113
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
76114

77115
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
78-
--> $DIR/reference_casting.rs:48:5
116+
--> $DIR/reference_casting.rs:68:5
79117
|
80118
LL | *{ std::ptr::from_ref(num) }.cast_mut() += 1;
81119
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
82120

83121
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
84-
--> $DIR/reference_casting.rs:50:5
122+
--> $DIR/reference_casting.rs:70:5
85123
|
86124
LL | *(std::ptr::from_ref({ num }) as *mut i32) += 1;
87125
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
88126

89127
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
90-
--> $DIR/reference_casting.rs:52:5
128+
--> $DIR/reference_casting.rs:72:5
91129
|
92130
LL | *std::mem::transmute::<_, *mut i32>(num) += 1;
93131
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
94132

95133
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
96-
--> $DIR/reference_casting.rs:56:5
134+
--> $DIR/reference_casting.rs:76:5
97135
|
98136
LL | let value = num as *const i32 as *mut i32;
99137
| ----------------------------- casting happend here
100138
LL | *value = 1;
101139
| ^^^^^^^^^^
102140

103-
error: aborting due to 16 previous errors
141+
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
142+
--> $DIR/reference_casting.rs:78:5
143+
|
144+
LL | *(num as *const i32).cast::<i32>().cast_mut() = 2;
145+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
146+
147+
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
148+
--> $DIR/reference_casting.rs:80:5
149+
|
150+
LL | *(num as *const _ as usize as *mut i32) = 2;
151+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
152+
153+
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
154+
--> $DIR/reference_casting.rs:84:9
155+
|
156+
LL | *(this as *const _ as *mut _) = a;
157+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
158+
159+
error: aborting due to 25 previous errors
104160

0 commit comments

Comments
 (0)