about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2020-05-16 16:17:07 +0200
committerRalf Jung <post@ralfj.de>2020-05-25 10:56:47 +0200
commitb27e649537770ba6631f3347974e3ae7e082adfe (patch)
treeec0a3951fda5ac5407c65e326513f8d8b61d1cf8
parent2679c38fc33b5f69ce3c502c81315aa889035191 (diff)
downloadrust-b27e649537770ba6631f3347974e3ae7e082adfe.tar.gz
rust-b27e649537770ba6631f3347974e3ae7e082adfe.zip
add a lint against references to packed fields
-rw-r--r--src/librustc_mir/transform/check_packed_ref.rs66
-rw-r--r--src/librustc_mir/transform/mod.rs6
-rw-r--r--src/librustc_session/lint/builtin.rs9
-rw-r--r--src/test/ui/lint/packed_reference.rs25
-rw-r--r--src/test/ui/lint/packed_reference.stderr23
5 files changed, 127 insertions, 2 deletions
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 00000000000..9e07a4599f6
--- /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::PACKED_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(
+                    PACKED_REFERENCES,
+                    lint_root,
+                    source_info.span,
+                    |lint| {
+                        lint.build(&format!("reference to packed field is not allowed",))
+                            .note(
+                                "fields of packed structs might be misaligned, 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 0551ed5a15d..7d2f8903624 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<Body<'_>> {
     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<Body<'_>> {
         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 3d03e46683e..0753e9e4b73 100644
--- a/src/librustc_session/lint/builtin.rs
+++ b/src/librustc_session/lint/builtin.rs
@@ -217,9 +217,15 @@ declare_lint! {
 }
 
 declare_lint! {
+    pub PACKED_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 <https://github.com/rust-lang/rust/issues/46043>",
         edition: None,
@@ -545,6 +551,7 @@ declare_lint_pass! {
         INVALID_TYPE_PARAM_DEFAULT,
         CONST_ERR,
         RENAMED_AND_REMOVED_LINTS,
+        PACKED_REFERENCES,
         SAFE_PACKED_BORROWS,
         PATTERNS_IN_FNS_WITHOUT_BODY,
         MISSING_FRAGMENT_SPECIFIER,
diff --git a/src/test/ui/lint/packed_reference.rs b/src/test/ui/lint/packed_reference.rs
new file mode 100644
index 00000000000..d588ffd2120
--- /dev/null
+++ b/src/test/ui/lint/packed_reference.rs
@@ -0,0 +1,25 @@
+#![deny(packed_references)]
+
+#[repr(packed)]
+pub struct Good {
+    data: &'static u32,
+    data2: [&'static u32; 2],
+    aligned: [u8; 32],
+}
+
+#[repr(packed)]
+pub struct JustArray {
+    array: [u32],
+}
+
+fn main() {
+    unsafe {
+        let good = Good { data: &0, data2: [&0, &0], aligned: [0; 32] };
+
+        let _ = &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/packed_reference.stderr b/src/test/ui/lint/packed_reference.stderr
new file mode 100644
index 00000000000..094fb4f34d3
--- /dev/null
+++ b/src/test/ui/lint/packed_reference.stderr
@@ -0,0 +1,23 @@
+error: reference to packed field is not allowed
+  --> $DIR/packed_reference.rs:19:17
+   |
+LL |         let _ = &good.data;
+   |                 ^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/packed_reference.rs:1:9
+   |
+LL | #![deny(packed_references)]
+   |         ^^^^^^^^^^^^^^^^^
+   = note: fields of packed structs might be misaligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
+
+error: reference to packed field is not allowed
+  --> $DIR/packed_reference.rs:20:17
+   |
+LL |         let _ = &good.data2[0];
+   |                 ^^^^^^^^^^^^^^
+   |
+   = note: fields of packed structs might be misaligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
+
+error: aborting due to 2 previous errors
+