diff --git a/src/librustc_mir/transform/check_packed_ref.rs b/src/librustc_mir/transform/check_packed_ref.rs new file mode 100644 index 0000000000000..faad1a72327f4 --- /dev/null +++ b/src/librustc_mir/transform/check_packed_ref.rs @@ -0,0 +1,66 @@ +use rustc_middle::mir::visit::{PlaceContext, Visitor}; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::lint::builtin::UNALIGNED_REFERENCES; + +use crate::transform::{MirPass, MirSource}; +use crate::util; + +pub struct CheckPackedRef; + +impl<'tcx> MirPass<'tcx> for CheckPackedRef { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { + let param_env = tcx.param_env(src.instance.def_id()); + let source_info = SourceInfo::outermost(body.span); + let mut checker = PackedRefChecker { body, tcx, param_env, source_info }; + checker.visit_body(&body); + } +} + +struct PackedRefChecker<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + source_info: SourceInfo, +} + +impl<'a, 'tcx> Visitor<'tcx> for PackedRefChecker<'a, 'tcx> { + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + // Make sure we know where in the MIR we are. + self.source_info = terminator.source_info; + self.super_terminator(terminator, location); + } + + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + // Make sure we know where in the MIR we are. + self.source_info = statement.source_info; + self.super_statement(statement, location); + } + + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) { + if context.is_borrow() { + if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { + let source_info = self.source_info; + let lint_root = self.body.source_scopes[source_info.scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; + self.tcx.struct_span_lint_hir( + UNALIGNED_REFERENCES, + lint_root, + source_info.span, + |lint| { + lint.build(&format!("reference to packed field is unaligned",)) + .note( + "fields of packed structs are not properly aligned, and creating \ + a misaligned reference is undefined behavior (even if that \ + reference is never dereferenced)", + ) + .emit() + }, + ); + } + } + } +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 0551ed5a15ddb..7d2f890362480 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -17,6 +17,7 @@ pub mod add_call_guards; pub mod add_moves_for_packed_drops; pub mod add_retag; pub mod check_consts; +pub mod check_packed_ref; pub mod check_unsafety; pub mod cleanup_post_borrowck; pub mod const_prop; @@ -211,10 +212,11 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs { validator.qualifs_in_return_place() } +/// Make MIR ready for const evaluation. This is run on all MIR, not just on consts! fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> Steal> { let def_id = def_id.expect_local(); - // Unsafety check uses the raw mir, so make sure it is run + // Unsafety check uses the raw mir, so make sure it is run. let _ = tcx.unsafety_check_result(def_id); let mut body = tcx.mir_built(def_id).steal(); @@ -230,6 +232,8 @@ fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> Steal> { None, MirPhase::Const, &[&[ + // MIR-level lints. + &check_packed_ref::CheckPackedRef, // What we need to do constant evaluation. &simplify::SimplifyCfg::new("initial"), &rustc_peek::SanityCheck, diff --git a/src/librustc_session/lint/builtin.rs b/src/librustc_session/lint/builtin.rs index 3d03e46683ed5..4035417204867 100644 --- a/src/librustc_session/lint/builtin.rs +++ b/src/librustc_session/lint/builtin.rs @@ -216,10 +216,16 @@ declare_lint! { "lints that have been renamed or removed" } +declare_lint! { + pub UNALIGNED_REFERENCES, + Allow, + "detects unaligned references to fields of packed structs", +} + declare_lint! { pub SAFE_PACKED_BORROWS, Warn, - "safe borrows of fields of packed structs were was erroneously allowed", + "safe borrows of fields of packed structs were erroneously allowed", @future_incompatible = FutureIncompatibleInfo { reference: "issue #46043 ", edition: None, @@ -545,6 +551,7 @@ declare_lint_pass! { INVALID_TYPE_PARAM_DEFAULT, CONST_ERR, RENAMED_AND_REMOVED_LINTS, + UNALIGNED_REFERENCES, SAFE_PACKED_BORROWS, PATTERNS_IN_FNS_WITHOUT_BODY, MISSING_FRAGMENT_SPECIFIER, diff --git a/src/test/ui/issues/issue-27060-rpass.rs b/src/test/ui/issues/issue-27060-rpass.rs index b6ffc3ecb5133..b20d614b3036b 100644 --- a/src/test/ui/issues/issue-27060-rpass.rs +++ b/src/test/ui/issues/issue-27060-rpass.rs @@ -7,19 +7,10 @@ pub struct Good { aligned: [u8; 32], } -#[repr(packed)] -pub struct JustArray { - array: [u32] -} - // kill this test when that turns to a hard error #[allow(safe_packed_borrows)] fn main() { - let good = Good { - data: &0, - data2: [&0, &0], - aligned: [0; 32] - }; + let good = Good { data: &0, data2: [&0, &0], aligned: [0; 32] }; unsafe { let _ = &good.data; // ok diff --git a/src/test/ui/issues/issue-27060.rs b/src/test/ui/issues/issue-27060.rs index 4caad03a36151..78f2022ed38df 100644 --- a/src/test/ui/issues/issue-27060.rs +++ b/src/test/ui/issues/issue-27060.rs @@ -5,11 +5,6 @@ pub struct Good { aligned: [u8; 32], } -#[repr(packed)] -pub struct JustArray { - array: [u32] -} - #[deny(safe_packed_borrows)] fn main() { let good = Good { diff --git a/src/test/ui/issues/issue-27060.stderr b/src/test/ui/issues/issue-27060.stderr index 6bf6348631a70..d14ae4d41d5c5 100644 --- a/src/test/ui/issues/issue-27060.stderr +++ b/src/test/ui/issues/issue-27060.stderr @@ -1,11 +1,11 @@ error: borrow of packed field is unsafe and requires unsafe function or block (error E0133) - --> $DIR/issue-27060.rs:26:13 + --> $DIR/issue-27060.rs:21:13 | LL | let _ = &good.data; | ^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/issue-27060.rs:13:8 + --> $DIR/issue-27060.rs:8:8 | LL | #[deny(safe_packed_borrows)] | ^^^^^^^^^^^^^^^^^^^ @@ -14,7 +14,7 @@ LL | #[deny(safe_packed_borrows)] = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior error: borrow of packed field is unsafe and requires unsafe function or block (error E0133) - --> $DIR/issue-27060.rs:28:13 + --> $DIR/issue-27060.rs:23:13 | LL | let _ = &good.data2[0]; | ^^^^^^^^^^^^^^ diff --git a/src/test/ui/lint/unaligned_references.rs b/src/test/ui/lint/unaligned_references.rs new file mode 100644 index 0000000000000..1d9f4c3db2eb5 --- /dev/null +++ b/src/test/ui/lint/unaligned_references.rs @@ -0,0 +1,22 @@ +#![deny(unaligned_references)] + +#[repr(packed)] +pub struct Good { + data: &'static u32, + data2: [&'static u32; 2], + aligned: [u8; 32], +} + +fn main() { + unsafe { + let good = Good { data: &0, data2: [&0, &0], aligned: [0; 32] }; + + let _ = &good.data; //~ ERROR reference to packed field + let _ = &good.data as *const _; //~ ERROR reference to packed field + let _: *const _ = &good.data; //~ ERROR reference to packed field + let _ = &good.data2[0]; //~ ERROR reference to packed field + let _ = &*good.data; // ok, behind a pointer + let _ = &good.aligned; // ok, has align 1 + let _ = &good.aligned[2]; // ok, has align 1 + } +} diff --git a/src/test/ui/lint/unaligned_references.stderr b/src/test/ui/lint/unaligned_references.stderr new file mode 100644 index 0000000000000..0c594cdb30a3c --- /dev/null +++ b/src/test/ui/lint/unaligned_references.stderr @@ -0,0 +1,39 @@ +error: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:14:17 + | +LL | let _ = &good.data; + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unaligned_references.rs:1:9 + | +LL | #![deny(unaligned_references)] + | ^^^^^^^^^^^^^^^^^^^^ + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +error: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:15:17 + | +LL | let _ = &good.data as *const _; + | ^^^^^^^^^^ + | + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +error: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:16:27 + | +LL | let _: *const _ = &good.data; + | ^^^^^^^^^^ + | + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +error: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:17:17 + | +LL | let _ = &good.data2[0]; + | ^^^^^^^^^^^^^^ + | + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +error: aborting due to 4 previous errors +