Skip to content

Commit b27e649

Browse files
committed
add a lint against references to packed fields
1 parent 2679c38 commit b27e649

File tree

5 files changed

+127
-2
lines changed

5 files changed

+127
-2
lines changed
Lines changed: 66 additions & 0 deletions
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::PACKED_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+
PACKED_REFERENCES,
51+
lint_root,
52+
source_info.span,
53+
|lint| {
54+
lint.build(&format!("reference to packed field is not allowed",))
55+
.note(
56+
"fields of packed structs might be misaligned, 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

Lines changed: 5 additions & 1 deletion
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;
@@ -211,10 +212,11 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs {
211212
validator.qualifs_in_return_place()
212213
}
213214

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

217-
// Unsafety check uses the raw mir, so make sure it is run
219+
// Unsafety check uses the raw mir, so make sure it is run.
218220
let _ = tcx.unsafety_check_result(def_id);
219221

220222
let mut body = tcx.mir_built(def_id).steal();
@@ -230,6 +232,8 @@ fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> Steal<Body<'_>> {
230232
None,
231233
MirPhase::Const,
232234
&[&[
235+
// MIR-level lints.
236+
&check_packed_ref::CheckPackedRef,
233237
// What we need to do constant evaluation.
234238
&simplify::SimplifyCfg::new("initial"),
235239
&rustc_peek::SanityCheck,

src/librustc_session/lint/builtin.rs

Lines changed: 8 additions & 1 deletion
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 PACKED_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+
PACKED_REFERENCES,
548555
SAFE_PACKED_BORROWS,
549556
PATTERNS_IN_FNS_WITHOUT_BODY,
550557
MISSING_FRAGMENT_SPECIFIER,

src/test/ui/lint/packed_reference.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![deny(packed_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+
#[repr(packed)]
11+
pub struct JustArray {
12+
array: [u32],
13+
}
14+
15+
fn main() {
16+
unsafe {
17+
let good = Good { data: &0, data2: [&0, &0], aligned: [0; 32] };
18+
19+
let _ = &good.data; //~ ERROR reference to packed field
20+
let _ = &good.data2[0]; //~ ERROR reference to packed field
21+
let _ = &*good.data; // ok, behind a pointer
22+
let _ = &good.aligned; // ok, has align 1
23+
let _ = &good.aligned[2]; // ok, has align 1
24+
}
25+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error: reference to packed field is not allowed
2+
--> $DIR/packed_reference.rs:19:17
3+
|
4+
LL | let _ = &good.data;
5+
| ^^^^^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/packed_reference.rs:1:9
9+
|
10+
LL | #![deny(packed_references)]
11+
| ^^^^^^^^^^^^^^^^^
12+
= note: fields of packed structs might be misaligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
13+
14+
error: reference to packed field is not allowed
15+
--> $DIR/packed_reference.rs:20:17
16+
|
17+
LL | let _ = &good.data2[0];
18+
| ^^^^^^^^^^^^^^
19+
|
20+
= note: fields of packed structs might be misaligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
21+
22+
error: aborting due to 2 previous errors
23+

0 commit comments

Comments
 (0)