Skip to content

Commit 6e6bd63

Browse files
authored
Rollup merge of rust-lang#72270 - RalfJung:lint-ref-to-packed, r=oli-obk
add a lint against references to packed fields Creating a reference to an insufficiently aligned packed field is UB and should be disallowed, both inside and outside of `unsafe` blocks. However, currently there is no stable alternative (rust-lang#64490) so all we do right now is have a future incompatibility warning when doing this outside `unsafe` (rust-lang#46043). This adds an allow-by-default lint. @retep998 suggested this can help early adopters avoid issues. It also means we can then do a crater run where this is deny-by-default as suggested by @joshtriplett. I guess the main thing to bikeshed is the lint name. I am not particularly happy with "packed_references" as it sounds like the packed field has reference type. I chose this because it is similar to "safe_packed_borrows". What about "reference_to_packed" or "unaligned_reference" or so?
2 parents aeca4d6 + d959a8f commit 6e6bd63

File tree

8 files changed

+144
-20
lines changed

8 files changed

+144
-20
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use rustc_middle::mir::visit::{PlaceContext, Visitor};
2+
use rustc_middle::mir::*;
3+
use rustc_middle::ty::{self, TyCtxt};
4+
use rustc_session::lint::builtin::UNALIGNED_REFERENCES;
5+
6+
use crate::transform::{MirPass, MirSource};
7+
use crate::util;
8+
9+
pub struct CheckPackedRef;
10+
11+
impl<'tcx> MirPass<'tcx> for CheckPackedRef {
12+
fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
13+
let param_env = tcx.param_env(src.instance.def_id());
14+
let source_info = SourceInfo::outermost(body.span);
15+
let mut checker = PackedRefChecker { body, tcx, param_env, source_info };
16+
checker.visit_body(&body);
17+
}
18+
}
19+
20+
struct PackedRefChecker<'a, 'tcx> {
21+
body: &'a Body<'tcx>,
22+
tcx: TyCtxt<'tcx>,
23+
param_env: ty::ParamEnv<'tcx>,
24+
source_info: SourceInfo,
25+
}
26+
27+
impl<'a, 'tcx> Visitor<'tcx> for PackedRefChecker<'a, 'tcx> {
28+
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
29+
// Make sure we know where in the MIR we are.
30+
self.source_info = terminator.source_info;
31+
self.super_terminator(terminator, location);
32+
}
33+
34+
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
35+
// Make sure we know where in the MIR we are.
36+
self.source_info = statement.source_info;
37+
self.super_statement(statement, location);
38+
}
39+
40+
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
41+
if context.is_borrow() {
42+
if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
43+
let source_info = self.source_info;
44+
let lint_root = self.body.source_scopes[source_info.scope]
45+
.local_data
46+
.as_ref()
47+
.assert_crate_local()
48+
.lint_root;
49+
self.tcx.struct_span_lint_hir(
50+
UNALIGNED_REFERENCES,
51+
lint_root,
52+
source_info.span,
53+
|lint| {
54+
lint.build(&format!("reference to packed field is unaligned",))
55+
.note(
56+
"fields of packed structs are not properly aligned, and creating \
57+
a misaligned reference is undefined behavior (even if that \
58+
reference is never dereferenced)",
59+
)
60+
.emit()
61+
},
62+
);
63+
}
64+
}
65+
}
66+
}

src/librustc_mir/transform/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod add_call_guards;
1717
pub mod add_moves_for_packed_drops;
1818
pub mod add_retag;
1919
pub mod check_consts;
20+
pub mod check_packed_ref;
2021
pub mod check_unsafety;
2122
pub mod cleanup_post_borrowck;
2223
pub mod const_prop;
@@ -228,10 +229,11 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs {
228229
validator.qualifs_in_return_place()
229230
}
230231

232+
/// Make MIR ready for const evaluation. This is run on all MIR, not just on consts!
231233
fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> Steal<Body<'_>> {
232234
let def_id = def_id.expect_local();
233235

234-
// Unsafety check uses the raw mir, so make sure it is run
236+
// Unsafety check uses the raw mir, so make sure it is run.
235237
let _ = tcx.unsafety_check_result(def_id);
236238

237239
let mut body = tcx.mir_built(def_id).steal();
@@ -247,6 +249,8 @@ fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> Steal<Body<'_>> {
247249
None,
248250
MirPhase::Const,
249251
&[&[
252+
// MIR-level lints.
253+
&check_packed_ref::CheckPackedRef,
250254
// What we need to do constant evaluation.
251255
&simplify::SimplifyCfg::new("initial"),
252256
&rustc_peek::SanityCheck,

src/librustc_session/lint/builtin.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,16 @@ declare_lint! {
216216
"lints that have been renamed or removed"
217217
}
218218

219+
declare_lint! {
220+
pub UNALIGNED_REFERENCES,
221+
Allow,
222+
"detects unaligned references to fields of packed structs",
223+
}
224+
219225
declare_lint! {
220226
pub SAFE_PACKED_BORROWS,
221227
Warn,
222-
"safe borrows of fields of packed structs were was erroneously allowed",
228+
"safe borrows of fields of packed structs were erroneously allowed",
223229
@future_incompatible = FutureIncompatibleInfo {
224230
reference: "issue #46043 <https://github.com/rust-lang/rust/issues/46043>",
225231
edition: None,
@@ -545,6 +551,7 @@ declare_lint_pass! {
545551
INVALID_TYPE_PARAM_DEFAULT,
546552
CONST_ERR,
547553
RENAMED_AND_REMOVED_LINTS,
554+
UNALIGNED_REFERENCES,
548555
SAFE_PACKED_BORROWS,
549556
PATTERNS_IN_FNS_WITHOUT_BODY,
550557
MISSING_FRAGMENT_SPECIFIER,

src/test/ui/issues/issue-27060-rpass.rs

+1-10
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,10 @@ pub struct Good {
77
aligned: [u8; 32],
88
}
99

10-
#[repr(packed)]
11-
pub struct JustArray {
12-
array: [u32]
13-
}
14-
1510
// kill this test when that turns to a hard error
1611
#[allow(safe_packed_borrows)]
1712
fn main() {
18-
let good = Good {
19-
data: &0,
20-
data2: [&0, &0],
21-
aligned: [0; 32]
22-
};
13+
let good = Good { data: &0, data2: [&0, &0], aligned: [0; 32] };
2314

2415
unsafe {
2516
let _ = &good.data; // ok

src/test/ui/issues/issue-27060.rs

-5
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@ pub struct Good {
55
aligned: [u8; 32],
66
}
77

8-
#[repr(packed)]
9-
pub struct JustArray {
10-
array: [u32]
11-
}
12-
138
#[deny(safe_packed_borrows)]
149
fn main() {
1510
let good = Good {

src/test/ui/issues/issue-27060.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
error: borrow of packed field is unsafe and requires unsafe function or block (error E0133)
2-
--> $DIR/issue-27060.rs:26:13
2+
--> $DIR/issue-27060.rs:21:13
33
|
44
LL | let _ = &good.data;
55
| ^^^^^^^^^^
66
|
77
note: the lint level is defined here
8-
--> $DIR/issue-27060.rs:13:8
8+
--> $DIR/issue-27060.rs:8:8
99
|
1010
LL | #[deny(safe_packed_borrows)]
1111
| ^^^^^^^^^^^^^^^^^^^
@@ -14,7 +14,7 @@ LL | #[deny(safe_packed_borrows)]
1414
= note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior
1515

1616
error: borrow of packed field is unsafe and requires unsafe function or block (error E0133)
17-
--> $DIR/issue-27060.rs:28:13
17+
--> $DIR/issue-27060.rs:23:13
1818
|
1919
LL | let _ = &good.data2[0];
2020
| ^^^^^^^^^^^^^^
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#![deny(unaligned_references)]
2+
3+
#[repr(packed)]
4+
pub struct Good {
5+
data: &'static u32,
6+
data2: [&'static u32; 2],
7+
aligned: [u8; 32],
8+
}
9+
10+
fn main() {
11+
unsafe {
12+
let good = Good { data: &0, data2: [&0, &0], aligned: [0; 32] };
13+
14+
let _ = &good.data; //~ ERROR reference to packed field
15+
let _ = &good.data as *const _; //~ ERROR reference to packed field
16+
let _: *const _ = &good.data; //~ ERROR reference to packed field
17+
let _ = &good.data2[0]; //~ ERROR reference to packed field
18+
let _ = &*good.data; // ok, behind a pointer
19+
let _ = &good.aligned; // ok, has align 1
20+
let _ = &good.aligned[2]; // ok, has align 1
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error: reference to packed field is unaligned
2+
--> $DIR/unaligned_references.rs:14:17
3+
|
4+
LL | let _ = &good.data;
5+
| ^^^^^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/unaligned_references.rs:1:9
9+
|
10+
LL | #![deny(unaligned_references)]
11+
| ^^^^^^^^^^^^^^^^^^^^
12+
= note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
13+
14+
error: reference to packed field is unaligned
15+
--> $DIR/unaligned_references.rs:15:17
16+
|
17+
LL | let _ = &good.data as *const _;
18+
| ^^^^^^^^^^
19+
|
20+
= note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
21+
22+
error: reference to packed field is unaligned
23+
--> $DIR/unaligned_references.rs:16:27
24+
|
25+
LL | let _: *const _ = &good.data;
26+
| ^^^^^^^^^^
27+
|
28+
= note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
29+
30+
error: reference to packed field is unaligned
31+
--> $DIR/unaligned_references.rs:17:17
32+
|
33+
LL | let _ = &good.data2[0];
34+
| ^^^^^^^^^^^^^^
35+
|
36+
= note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
37+
38+
error: aborting due to 4 previous errors
39+

0 commit comments

Comments
 (0)