]> git.lizzy.rs Git - rust.git/commitdiff
add basic lint testing for misuse of mem::zeroed and mem::uninitialized
authorRalf Jung <post@ralfj.de>
Tue, 6 Aug 2019 21:11:52 +0000 (23:11 +0200)
committerRalf Jung <post@ralfj.de>
Sun, 11 Aug 2019 10:04:49 +0000 (12:04 +0200)
src/librustc_lint/builtin.rs
src/librustc_lint/lib.rs
src/libsyntax_pos/symbol.rs
src/test/ui/lint/uninitialized-zeroed.rs [new file with mode: 0644]
src/test/ui/lint/uninitialized-zeroed.stderr [new file with mode: 0644]
src/test/ui/panic-uninitialized-zeroed.rs

index c9153f285fff71634fa62bed35d5326d2af7eede..b4155646c891f531477729ad2ae1e5ff805068fa 100644 (file)
@@ -1862,3 +1862,62 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
             });
     }
 }
+
+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"
+                                    }
+                                ),
+                            );
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
index 78bc164ba1a0f0fcc16c3ff93647f22a41ba69bf..3a540fdf4b91f80c292478e22f03ccfa80cfba6f 100644 (file)
@@ -177,6 +177,7 @@ macro_rules! late_lint_mod_passes {
             UnreachablePub: UnreachablePub,
 
             ExplicitOutlivesRequirements: ExplicitOutlivesRequirements,
+            InvalidValue: InvalidValue,
         ]);
     )
 }
index f7e1b983e54468cb0f1c423c82a3c7ccd6fec569..2d9556233d15fb85a557462bbb2ea13a7312e73a 100644 (file)
         match_beginning_vert,
         match_default_bindings,
         may_dangle,
+        mem,
         member_constraints,
         message,
         meta,
         underscore_imports,
         underscore_lifetimes,
         uniform_paths,
+        uninitialized,
         universal_impl_trait,
         unmarked_api,
         unreachable_code,
         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 (file)
index 0000000..40b1765
--- /dev/null
@@ -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 (file)
index 0000000..c6a4763
--- /dev/null
@@ -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
+
index 0c97babd51cd4ac3fd70097bee1eb3d029f0a73e..b0d6629561803077c5324c0c2d9d61e553657557 100644 (file)
@@ -4,7 +4,7 @@
 // in a runtime panic.
 
 #![feature(never_type)]
-#![allow(deprecated)]
+#![allow(deprecated, invalid_value)]
 
 use std::{mem, panic};