Skip to content

Commit 09013de

Browse files
committed
Lint on reference casting to bigger layout
1 parent 4d1bd0d commit 09013de

File tree

6 files changed

+250
-14
lines changed

6 files changed

+250
-14
lines changed

compiler/rustc_lint/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,10 @@ lint_invalid_reference_casting_assign_to_ref = assigning to `&T` is undefined be
319319
lint_invalid_reference_casting_borrow_as_mut = casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
320320
.label = casting happend here
321321
322+
lint_invalid_reference_casting_bigger_layout = casting references to a bigger memory layout is undefined behavior, even if the reference is unused
323+
.label = casting happend here
324+
.layout = casting from `{$from_ty}` (size: {$from_size} bytes, align: {$from_align} bytes) to `{$to_ty}` (size: {$to_size} bytes, align: {$to_align} bytes)
325+
322326
lint_invalid_reference_casting_note_book = for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
323327
324328
lint_invalid_reference_casting_note_ty_has_interior_mutability = even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`

compiler/rustc_lint/src/lints.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,7 @@ pub enum InvalidFromUtf8Diag {
752752

753753
// reference_casting.rs
754754
#[derive(LintDiagnostic)]
755-
pub enum InvalidReferenceCastingDiag {
755+
pub enum InvalidReferenceCastingDiag<'tcx> {
756756
#[diag(lint_invalid_reference_casting_borrow_as_mut)]
757757
#[note(lint_invalid_reference_casting_note_book)]
758758
BorrowAsMut {
@@ -769,6 +769,18 @@ pub enum InvalidReferenceCastingDiag {
769769
#[note(lint_invalid_reference_casting_note_ty_has_interior_mutability)]
770770
ty_has_interior_mutability: Option<()>,
771771
},
772+
#[diag(lint_invalid_reference_casting_bigger_layout)]
773+
#[note(lint_layout)]
774+
BiggerLayout {
775+
#[label]
776+
orig_cast: Option<Span>,
777+
from_ty: Ty<'tcx>,
778+
from_size: u64,
779+
from_align: u64,
780+
to_ty: Ty<'tcx>,
781+
to_size: u64,
782+
to_align: u64,
783+
},
772784
}
773785

774786
// hidden_unicode_codepoints.rs

compiler/rustc_lint/src/reference_casting.rs

+56-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use rustc_ast::Mutability;
22
use rustc_hir::{Expr, ExprKind, UnOp};
3-
use rustc_middle::ty::{self, TypeAndMut};
3+
use rustc_middle::ty::layout::LayoutOf as _;
4+
use rustc_middle::ty::{self, layout::TyAndLayout, TypeAndMut};
45
use rustc_span::sym;
56

67
use crate::{lints::InvalidReferenceCastingDiag, LateContext, LateLintPass, LintContext};
@@ -38,13 +39,12 @@ declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]);
3839
impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
3940
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
4041
if let Some((e, pat)) = borrow_or_assign(cx, expr) {
41-
if matches!(pat, PatternKind::Borrow { mutbl: Mutability::Mut } | PatternKind::Assign) {
42-
let init = cx.expr_or_init(e);
42+
let init = cx.expr_or_init(e);
43+
let orig_cast = if init.span != e.span { Some(init.span) } else { None };
4344

44-
let Some(ty_has_interior_mutability) = is_cast_from_ref_to_mut_ptr(cx, init) else {
45-
return;
46-
};
47-
let orig_cast = if init.span != e.span { Some(init.span) } else { None };
45+
if matches!(pat, PatternKind::Borrow { mutbl: Mutability::Mut } | PatternKind::Assign)
46+
&& let Some(ty_has_interior_mutability) = is_cast_from_ref_to_mut_ptr(cx, init)
47+
{
4848
let ty_has_interior_mutability = ty_has_interior_mutability.then_some(());
4949

5050
cx.emit_spanned_lint(
@@ -63,6 +63,23 @@ impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
6363
},
6464
);
6565
}
66+
67+
if let Some((from_ty_layout, to_ty_layout)) = is_cast_to_bigger_memory_layout(cx, init)
68+
{
69+
cx.emit_spanned_lint(
70+
INVALID_REFERENCE_CASTING,
71+
expr.span,
72+
InvalidReferenceCastingDiag::BiggerLayout {
73+
orig_cast,
74+
from_ty: from_ty_layout.ty,
75+
from_size: from_ty_layout.layout.size().bytes(),
76+
from_align: from_ty_layout.layout.align().abi.bytes(),
77+
to_ty: to_ty_layout.ty,
78+
to_size: to_ty_layout.layout.size().bytes(),
79+
to_align: to_ty_layout.layout.align().abi.bytes(),
80+
},
81+
);
82+
}
6683
}
6784
}
6885
}
@@ -151,6 +168,38 @@ fn is_cast_from_ref_to_mut_ptr<'tcx>(
151168
}
152169
}
153170

171+
fn is_cast_to_bigger_memory_layout<'tcx>(
172+
cx: &LateContext<'tcx>,
173+
orig_expr: &'tcx Expr<'tcx>,
174+
) -> Option<(TyAndLayout<'tcx>, TyAndLayout<'tcx>)> {
175+
let end_ty = cx.typeck_results().node_type(orig_expr.hir_id);
176+
177+
let ty::RawPtr(TypeAndMut { ty: inner_end_ty, mutbl: _ }) = end_ty.kind() else {
178+
return None;
179+
};
180+
181+
let (e, _) = peel_casts(cx, orig_expr);
182+
let start_ty = cx.typeck_results().node_type(e.hir_id);
183+
184+
let ty::Ref(_, inner_start_ty, _) = start_ty.kind() else {
185+
return None;
186+
};
187+
188+
let (Ok(from_layout), Ok(to_layout)) =
189+
(cx.layout_of(*inner_start_ty), cx.layout_of(*inner_end_ty))
190+
else {
191+
return None;
192+
};
193+
194+
if to_layout.layout.size() > from_layout.layout.size()
195+
|| to_layout.layout.align().abi > from_layout.layout.align().abi
196+
{
197+
Some((from_layout, to_layout))
198+
} else {
199+
None
200+
}
201+
}
202+
154203
fn peel_casts<'tcx>(cx: &LateContext<'tcx>, mut e: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, bool) {
155204
let mut gone_trough_unsafe_cell_raw_get = false;
156205

tests/ui/cast/cast-rfc0401.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ fn main()
8282

8383
// ptr-ptr-cast (Length vtables)
8484
let mut l : [u8; 2] = [0,1];
85-
let w: *mut [u16; 2] = &mut l as *mut [u8; 2] as *mut _;
86-
let w: *mut [u16] = unsafe {&mut *w};
85+
let w: *mut [i8; 2] = &mut l as *mut [u8; 2] as *mut _;
86+
let w: *mut [i8] = unsafe {&mut *w};
8787
let w_u8 : *const [u8] = w as *const [u8];
8888
assert_eq!(unsafe{&*w_u8}, &l);
8989

tests/ui/lint/reference_casting.rs

+74-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ unsafe fn ref_to_mut() {
3232
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
3333
let _num = &mut *(num as *const i32).cast::<i32>().cast_mut().cast_const().cast_mut();
3434
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
35-
let _num = &mut *(std::ptr::from_ref(static_u8()) as *mut i32);
35+
let _num = &mut *(std::ptr::from_ref(static_u8()) as *mut i8);
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
@@ -143,6 +143,79 @@ unsafe fn assign_to_ref() {
143143
}
144144
}
145145

146+
#[repr(align(16))]
147+
struct I64(i64);
148+
149+
#[repr(C)]
150+
struct Mat3<T> {
151+
a: Vec3<T>,
152+
b: Vec3<T>,
153+
c: Vec3<T>,
154+
}
155+
156+
#[repr(C)]
157+
struct Vec3<T>(T, T, T);
158+
159+
unsafe fn bigger_layout() {
160+
{
161+
let num = &mut 3i32;
162+
163+
let _num = &*(num as *const i32 as *const i64);
164+
//~^ ERROR casting references to a bigger memory layout is undefined behavior
165+
let _num = &mut *(num as *mut i32 as *mut i64);
166+
//~^ ERROR casting references to a bigger memory layout is undefined behavior
167+
let _num = &mut *(num as *mut i32 as *mut I64);
168+
//~^ ERROR casting references to a bigger memory layout is undefined behavior
169+
std::ptr::write(num as *mut i32 as *mut i64, 2);
170+
//~^ ERROR casting references to a bigger memory layout is undefined behavior
171+
172+
let _num = &mut *(num as *mut i32);
173+
}
174+
175+
{
176+
let num = &mut [0i32; 3];
177+
178+
let _num = &mut *(num as *mut _ as *mut [i64; 2]);
179+
//~^ ERROR casting references to a bigger memory layout is undefined behavior
180+
std::ptr::write_unaligned(num as *mut _ as *mut [i32; 4], [0, 0, 1, 1]);
181+
//~^ ERROR casting references to a bigger memory layout is undefined behavior
182+
183+
let _num = &mut *(num as *mut _ as *mut [u32; 3]);
184+
let _num = &mut *(num as *mut _ as *mut [u32; 2]);
185+
}
186+
187+
{
188+
let num = &mut [0i32; 3] as &mut [i32];
189+
190+
let _num = &mut *(num as *mut _ as *mut [i64]);
191+
//~^ ERROR casting references to a bigger memory layout is undefined behavior
192+
let _num = &mut *(num as *mut _ as *mut [i64; 4]);
193+
//~^ ERROR casting references to a bigger memory layout is undefined behavior
194+
195+
let _num = &mut *(num as *mut _ as *mut [u32]);
196+
let _num = &mut *(num as *mut _ as *mut [i16]);
197+
}
198+
199+
{
200+
let mat3 = Mat3 { a: Vec3(0i32, 0, 0), b: Vec3(0, 0, 0), c: Vec3(0, 0, 0) };
201+
202+
let _num = &mut *(&mat3 as *const _ as *mut [[i64; 3]; 3]);
203+
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
204+
//~^^ ERROR casting references to a bigger memory layout is undefined behavior
205+
let _num = &*(&mat3 as *const _ as *mut [[i64; 3]; 3]);
206+
//~^ ERROR casting references to a bigger memory layout is undefined behavior
207+
208+
let _num = &*(&mat3 as *const _ as *mut [[i32; 3]; 3]);
209+
}
210+
211+
{
212+
let mut l : [u8; 2] = [0,1];
213+
let w: *mut [u16; 2] = &mut l as *mut [u8; 2] as *mut _;
214+
let w: *mut [u16] = unsafe {&mut *w};
215+
//~^ ERROR casting references to a bigger memory layout is undefined behavior
216+
}
217+
}
218+
146219
const RAW_PTR: *mut u8 = 1 as *mut u8;
147220
unsafe fn no_warn() {
148221
let num = &3i32;

tests/ui/lint/reference_casting.stderr

+101-3
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ LL | let _num = &mut *(num as *const i32).cast::<i32>().cast_mut().cast_cons
6666
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
6767
--> $DIR/reference_casting.rs:35:16
6868
|
69-
LL | let _num = &mut *(std::ptr::from_ref(static_u8()) as *mut i32);
70-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
69+
LL | let _num = &mut *(std::ptr::from_ref(static_u8()) as *mut i8);
70+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7171
|
7272
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
7373

@@ -373,5 +373,103 @@ LL | *(this as *const _ as *mut _) = a;
373373
|
374374
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
375375

376-
error: aborting due to 42 previous errors
376+
error: casting references to a bigger memory layout is undefined behavior, even if the reference is unused
377+
--> $DIR/reference_casting.rs:163:20
378+
|
379+
LL | let _num = &*(num as *const i32 as *const i64);
380+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
381+
|
382+
= note: casting from `i32` (size: 4 bytes, align: 4 bytes) to `i64` (size: 8 bytes, align: 8 bytes)
383+
384+
error: casting references to a bigger memory layout is undefined behavior, even if the reference is unused
385+
--> $DIR/reference_casting.rs:165:20
386+
|
387+
LL | let _num = &mut *(num as *mut i32 as *mut i64);
388+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
389+
|
390+
= note: casting from `i32` (size: 4 bytes, align: 4 bytes) to `i64` (size: 8 bytes, align: 8 bytes)
391+
392+
error: casting references to a bigger memory layout is undefined behavior, even if the reference is unused
393+
--> $DIR/reference_casting.rs:167:20
394+
|
395+
LL | let _num = &mut *(num as *mut i32 as *mut I64);
396+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
397+
|
398+
= note: casting from `i32` (size: 4 bytes, align: 4 bytes) to `I64` (size: 16 bytes, align: 16 bytes)
399+
400+
error: casting references to a bigger memory layout is undefined behavior, even if the reference is unused
401+
--> $DIR/reference_casting.rs:169:9
402+
|
403+
LL | std::ptr::write(num as *mut i32 as *mut i64, 2);
404+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
405+
|
406+
= note: casting from `i32` (size: 4 bytes, align: 4 bytes) to `i64` (size: 8 bytes, align: 8 bytes)
407+
408+
error: casting references to a bigger memory layout is undefined behavior, even if the reference is unused
409+
--> $DIR/reference_casting.rs:178:20
410+
|
411+
LL | let _num = &mut *(num as *mut _ as *mut [i64; 2]);
412+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
413+
|
414+
= note: casting from `[i32; 3]` (size: 12 bytes, align: 4 bytes) to `[i64; 2]` (size: 16 bytes, align: 8 bytes)
415+
416+
error: casting references to a bigger memory layout is undefined behavior, even if the reference is unused
417+
--> $DIR/reference_casting.rs:180:9
418+
|
419+
LL | std::ptr::write_unaligned(num as *mut _ as *mut [i32; 4], [0, 0, 1, 1]);
420+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
421+
|
422+
= note: casting from `[i32; 3]` (size: 12 bytes, align: 4 bytes) to `[i32; 4]` (size: 16 bytes, align: 4 bytes)
423+
424+
error: casting references to a bigger memory layout is undefined behavior, even if the reference is unused
425+
--> $DIR/reference_casting.rs:190:20
426+
|
427+
LL | let _num = &mut *(num as *mut _ as *mut [i64]);
428+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
429+
|
430+
= note: casting from `[i32; 3]` (size: 12 bytes, align: 4 bytes) to `[i64]` (size: 0 bytes, align: 8 bytes)
431+
432+
error: casting references to a bigger memory layout is undefined behavior, even if the reference is unused
433+
--> $DIR/reference_casting.rs:192:20
434+
|
435+
LL | let _num = &mut *(num as *mut _ as *mut [i64; 4]);
436+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
437+
|
438+
= note: casting from `[i32; 3]` (size: 12 bytes, align: 4 bytes) to `[i64; 4]` (size: 32 bytes, align: 8 bytes)
439+
440+
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
441+
--> $DIR/reference_casting.rs:202:20
442+
|
443+
LL | let _num = &mut *(&mat3 as *const _ as *mut [[i64; 3]; 3]);
444+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
445+
|
446+
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
447+
448+
error: casting references to a bigger memory layout is undefined behavior, even if the reference is unused
449+
--> $DIR/reference_casting.rs:202:20
450+
|
451+
LL | let _num = &mut *(&mat3 as *const _ as *mut [[i64; 3]; 3]);
452+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
453+
|
454+
= note: casting from `Mat3<i32>` (size: 36 bytes, align: 4 bytes) to `[[i64; 3]; 3]` (size: 72 bytes, align: 8 bytes)
455+
456+
error: casting references to a bigger memory layout is undefined behavior, even if the reference is unused
457+
--> $DIR/reference_casting.rs:205:20
458+
|
459+
LL | let _num = &*(&mat3 as *const _ as *mut [[i64; 3]; 3]);
460+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
461+
|
462+
= note: casting from `Mat3<i32>` (size: 36 bytes, align: 4 bytes) to `[[i64; 3]; 3]` (size: 72 bytes, align: 8 bytes)
463+
464+
error: casting references to a bigger memory layout is undefined behavior, even if the reference is unused
465+
--> $DIR/reference_casting.rs:214:37
466+
|
467+
LL | let w: *mut [u16; 2] = &mut l as *mut [u8; 2] as *mut _;
468+
| -------------------------------- casting happend here
469+
LL | let w: *mut [u16] = unsafe {&mut *w};
470+
| ^^^^^^^
471+
|
472+
= note: casting from `[u8; 2]` (size: 2 bytes, align: 1 bytes) to `[u16; 2]` (size: 4 bytes, align: 2 bytes)
473+
474+
error: aborting due to 54 previous errors
377475

0 commit comments

Comments
 (0)