about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2019-08-06 23:11:52 +0200
committerRalf Jung <post@ralfj.de>2019-08-11 12:04:49 +0200
commitda6fbb18951547da08c2e353838e6b1d47d9b3be (patch)
tree89f01635f686ed4009a7ed2769ec59520d311cf6
parent6f70adcb18e5dc8df0672898a8404fd05a9c32cb (diff)
downloadrust-da6fbb18951547da08c2e353838e6b1d47d9b3be.tar.gz
rust-da6fbb18951547da08c2e353838e6b1d47d9b3be.zip
add basic lint testing for misuse of mem::zeroed and mem::uninitialized
-rw-r--r--src/librustc_lint/builtin.rs59
-rw-r--r--src/librustc_lint/lib.rs1
-rw-r--r--src/libsyntax_pos/symbol.rs3
-rw-r--r--src/test/ui/lint/uninitialized-zeroed.rs28
-rw-r--r--src/test/ui/lint/uninitialized-zeroed.stderr44
-rw-r--r--src/test/ui/panic-uninitialized-zeroed.rs2
6 files changed, 136 insertions, 1 deletions
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index c9153f285ff..b4155646c89 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -1862,3 +1862,62 @@ impl EarlyLintPass for IncompleteFeatures {
             });
     }
 }
+
+declare_lint! {
+    pub INVALID_VALUE,
+    Warn,
+    "an invalid value is being created (such as a NULL reference)"
+}
+
+declare_lint_pass!(InvalidValue => [INVALID_VALUE]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
+    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &hir::Expr) {
+
+        const ZEROED_PATH: &[Symbol] = &[sym::core, sym::mem, sym::zeroed];
+        const UININIT_PATH: &[Symbol] = &[sym::core, sym::mem, sym::uninitialized];
+
+        /// Return `false` only if we are sure this type does *not*
+        /// allow zero initialization.
+        fn ty_maybe_allows_zero_init(ty: Ty<'_>) -> bool {
+            use rustc::ty::TyKind::*;
+            match ty.sty {
+                // Primitive types that don't like 0 as a value.
+                Ref(..) | FnPtr(..) | Never => false,
+                // Conservative fallback.
+                _ => true,
+            }
+        }
+
+        if let hir::ExprKind::Call(ref path_expr, ref _args) = expr.node {
+            if let hir::ExprKind::Path(ref qpath) = path_expr.node {
+                if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
+                    if cx.match_def_path(def_id, &ZEROED_PATH) ||
+                        cx.match_def_path(def_id, &UININIT_PATH)
+                    {
+                        // This conjures an instance of a type out of nothing,
+                        // using zeroed or uninitialized memory.
+                        // We are extremely conservative with what we warn about.
+                        let conjured_ty = cx.tables.expr_ty(expr);
+
+                        if !ty_maybe_allows_zero_init(conjured_ty) {
+                            cx.span_lint(
+                                INVALID_VALUE,
+                                expr.span,
+                                &format!(
+                                    "the type `{}` does not permit {}",
+                                    conjured_ty,
+                                    if cx.match_def_path(def_id, &ZEROED_PATH) {
+                                        "zero-initialization"
+                                    } else {
+                                        "being left uninitialized"
+                                    }
+                                ),
+                            );
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index 78bc164ba1a..3a540fdf4b9 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -177,6 +177,7 @@ macro_rules! late_lint_mod_passes {
             UnreachablePub: UnreachablePub,
 
             ExplicitOutlivesRequirements: ExplicitOutlivesRequirements,
+            InvalidValue: InvalidValue,
         ]);
     )
 }
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index f7e1b983e54..2d9556233d1 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -412,6 +412,7 @@ symbols! {
         match_beginning_vert,
         match_default_bindings,
         may_dangle,
+        mem,
         member_constraints,
         message,
         meta,
@@ -695,6 +696,7 @@ symbols! {
         underscore_imports,
         underscore_lifetimes,
         uniform_paths,
+        uninitialized,
         universal_impl_trait,
         unmarked_api,
         unreachable_code,
@@ -726,6 +728,7 @@ symbols! {
         windows,
         windows_subsystem,
         Yield,
+        zeroed,
     }
 }
 
diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs
new file mode 100644
index 00000000000..40b17651e47
--- /dev/null
+++ b/src/test/ui/lint/uninitialized-zeroed.rs
@@ -0,0 +1,28 @@
+// ignore-tidy-linelength
+// This test checks that calling `mem::{uninitialized,zeroed}` with certain types results
+// in a lint.
+
+#![feature(never_type)]
+#![allow(deprecated)]
+#![deny(invalid_value)]
+
+use std::mem;
+
+fn main() {
+    unsafe {
+        let _val: ! = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: ! = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        let _val: &'static i32 = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: &'static i32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        let _val: fn() = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: fn() = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        // Some types that should work just fine.
+        let _val: Option<&'static i32> = mem::zeroed();
+        let _val: Option<fn()> = mem::zeroed();
+        let _val: bool = mem::zeroed();
+        let _val: i32 = mem::zeroed();
+    }
+}
diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr
new file mode 100644
index 00000000000..c6a47638d38
--- /dev/null
+++ b/src/test/ui/lint/uninitialized-zeroed.stderr
@@ -0,0 +1,44 @@
+error: the type `!` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:15:23
+   |
+LL |         let _val: ! = mem::zeroed();
+   |                       ^^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/uninitialized-zeroed.rs:7:9
+   |
+LL | #![deny(invalid_value)]
+   |         ^^^^^^^^^^^^^
+
+error: the type `!` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:16:23
+   |
+LL |         let _val: ! = mem::uninitialized();
+   |                       ^^^^^^^^^^^^^^^^^^^^
+
+error: the type `&'static i32` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:21:34
+   |
+LL |         let _val: &'static i32 = mem::zeroed();
+   |                                  ^^^^^^^^^^^^^
+
+error: the type `&'static i32` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:22:34
+   |
+LL |         let _val: &'static i32 = mem::uninitialized();
+   |                                  ^^^^^^^^^^^^^^^^^^^^
+
+error: the type `fn()` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:24:26
+   |
+LL |         let _val: fn() = mem::zeroed();
+   |                          ^^^^^^^^^^^^^
+
+error: the type `fn()` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:25:26
+   |
+LL |         let _val: fn() = mem::uninitialized();
+   |                          ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/test/ui/panic-uninitialized-zeroed.rs b/src/test/ui/panic-uninitialized-zeroed.rs
index 0c97babd51c..b0d66295618 100644
--- a/src/test/ui/panic-uninitialized-zeroed.rs
+++ b/src/test/ui/panic-uninitialized-zeroed.rs
@@ -4,7 +4,7 @@
 // in a runtime panic.
 
 #![feature(never_type)]
-#![allow(deprecated)]
+#![allow(deprecated, invalid_value)]
 
 use std::{mem, panic};