]> git.lizzy.rs Git - rust.git/commitdiff
invalid_value: also detect transmute-from-0 (seen in the wild)
authorRalf Jung <post@ralfj.de>
Sat, 17 Aug 2019 09:48:30 +0000 (11:48 +0200)
committerRalf Jung <post@ralfj.de>
Sat, 17 Aug 2019 09:57:01 +0000 (11:57 +0200)
src/librustc_lint/builtin.rs
src/test/ui/lint/uninitialized-zeroed.rs
src/test/ui/lint/uninitialized-zeroed.stderr

index 74cff1bab0f814c53786b5af146619cb03c3d97e..877dc43fb7b9be10dcd8073d0bb6dbc8bc43d617 100644 (file)
@@ -1879,12 +1879,38 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &hir::Expr) {
         #[derive(Debug, Copy, Clone, PartialEq)]
         enum InitKind { Zeroed, Uninit };
 
+        /// Information about why a type cannot be initialized this way.
+        /// Contains an error message and optionally a span to point at.
+        type InitError = (String, Option<Span>);
+
+        /// Test if this constant is all-0.
+        fn is_zero(expr: &hir::Expr) -> bool {
+            use hir::ExprKind::*;
+            use syntax::ast::LitKind::*;
+            match &expr.node {
+                Lit(lit) =>
+                    if let Int(i, _) = lit.node {
+                        i == 0
+                    } else {
+                        false
+                    },
+                Tup(tup) =>
+                    tup.iter().all(is_zero),
+                _ =>
+                    false
+            }
+        }
+
         /// Determine if this expression is a "dangerous initialization".
         fn is_dangerous_init(cx: &LateContext<'_, '_>, expr: &hir::Expr) -> Option<InitKind> {
             const ZEROED_PATH: &[Symbol] = &[sym::core, sym::mem, sym::zeroed];
             const UININIT_PATH: &[Symbol] = &[sym::core, sym::mem, sym::uninitialized];
+            // `transmute` is inside an anonymous module (the `extern` block?);
+            // `Invalid` represents the empty string and matches that.
+            const TRANSMUTE_PATH: &[Symbol] =
+                &[sym::core, sym::intrinsics, kw::Invalid, sym::transmute];
 
-            if let hir::ExprKind::Call(ref path_expr, ref _args) = expr.node {
+            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()
@@ -1895,9 +1921,13 @@ fn is_dangerous_init(cx: &LateContext<'_, '_>, expr: &hir::Expr) -> Option<InitK
                         if cx.match_def_path(def_id, &UININIT_PATH) {
                             return Some(InitKind::Uninit);
                         }
+                        if cx.match_def_path(def_id, &TRANSMUTE_PATH) {
+                            if is_zero(&args[0]) {
+                                return Some(InitKind::Zeroed);
+                            }
+                        }
                         // FIXME: Also detect `MaybeUninit::zeroed().assume_init()` and
                         // `MaybeUninit::uninit().assume_init()`.
-                        // FIXME: Also detect `transmute` from 0.
                     }
                 }
             }
@@ -1905,10 +1935,6 @@ fn is_dangerous_init(cx: &LateContext<'_, '_>, expr: &hir::Expr) -> Option<InitK
             None
         }
 
-        /// Information about why a type cannot be initialized this way.
-        /// Contains an error message and optionally a span to point at.
-        type InitError = (String, Option<Span>);
-
         /// Return `Some` only if we are sure this type does *not*
         /// allow zero initialization.
         fn ty_find_init_error<'tcx>(
index 237f2c0141dcea647941059aa0e9e7aecfd34548..54e1210768cd2b55a5950e628ac1f4b1fb46025e 100644 (file)
@@ -7,6 +7,7 @@
 #![deny(invalid_value)]
 
 use std::mem::{self, MaybeUninit};
+use std::num::NonZeroU32;
 
 enum Void {}
 
@@ -75,6 +76,11 @@ fn main() {
         let _val: NonBig = mem::zeroed();
         let _val: NonBig = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
 
+        // Transmute-from-0
+        let _val: &'static i32 = mem::transmute(0usize); //~ ERROR: does not permit zero-initialization
+        let _val: &'static [i32] = mem::transmute((0usize, 0usize)); //~ ERROR: does not permit zero-initialization
+        let _val: NonZeroU32 = mem::transmute(0); //~ ERROR: does not permit zero-initialization
+
         // Some more types that should work just fine.
         let _val: Option<&'static i32> = mem::zeroed();
         let _val: Option<fn()> = mem::zeroed();
index c5006345e52f13cd22f3df9d4466d9e09d6d395e..90a51ba57e96fd6263f9eaac35d534cd0be2d932 100644 (file)
@@ -1,5 +1,5 @@
 error: the type `&'static T` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:27:32
+  --> $DIR/uninitialized-zeroed.rs:28:32
    |
 LL |         let _val: &'static T = mem::zeroed();
    |                                ^^^^^^^^^^^^^
@@ -15,7 +15,7 @@ LL | #![deny(invalid_value)]
    = note: References must be non-null
 
 error: the type `&'static T` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:28:32
+  --> $DIR/uninitialized-zeroed.rs:29:32
    |
 LL |         let _val: &'static T = mem::uninitialized();
    |                                ^^^^^^^^^^^^^^^^^^^^
@@ -26,7 +26,7 @@ LL |         let _val: &'static T = mem::uninitialized();
    = note: References must be non-null
 
 error: the type `Wrap<&'static T>` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:30:38
+  --> $DIR/uninitialized-zeroed.rs:31:38
    |
 LL |         let _val: Wrap<&'static T> = mem::zeroed();
    |                                      ^^^^^^^^^^^^^
@@ -35,13 +35,13 @@ LL |         let _val: Wrap<&'static T> = mem::zeroed();
    |                                      help: use `MaybeUninit<T>` instead
    |
 note: References must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:16:18
+  --> $DIR/uninitialized-zeroed.rs:17:18
    |
 LL | struct Wrap<T> { wrapped: T }
    |                  ^^^^^^^^^^
 
 error: the type `Wrap<&'static T>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:31:38
+  --> $DIR/uninitialized-zeroed.rs:32:38
    |
 LL |         let _val: Wrap<&'static T> = mem::uninitialized();
    |                                      ^^^^^^^^^^^^^^^^^^^^
@@ -50,13 +50,13 @@ LL |         let _val: Wrap<&'static T> = mem::uninitialized();
    |                                      help: use `MaybeUninit<T>` instead
    |
 note: References must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:16:18
+  --> $DIR/uninitialized-zeroed.rs:17:18
    |
 LL | struct Wrap<T> { wrapped: T }
    |                  ^^^^^^^^^^
 
 error: the type `!` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:38:23
+  --> $DIR/uninitialized-zeroed.rs:39:23
    |
 LL |         let _val: ! = mem::zeroed();
    |                       ^^^^^^^^^^^^^
@@ -67,7 +67,7 @@ LL |         let _val: ! = mem::zeroed();
    = note: The never type (`!`) has no valid value
 
 error: the type `!` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:39:23
+  --> $DIR/uninitialized-zeroed.rs:40:23
    |
 LL |         let _val: ! = mem::uninitialized();
    |                       ^^^^^^^^^^^^^^^^^^^^
@@ -78,7 +78,7 @@ LL |         let _val: ! = mem::uninitialized();
    = note: The never type (`!`) has no valid value
 
 error: the type `(i32, !)` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:41:30
+  --> $DIR/uninitialized-zeroed.rs:42:30
    |
 LL |         let _val: (i32, !) = mem::zeroed();
    |                              ^^^^^^^^^^^^^
@@ -89,7 +89,7 @@ LL |         let _val: (i32, !) = mem::zeroed();
    = note: The never type (`!`) has no valid value
 
 error: the type `(i32, !)` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:42:30
+  --> $DIR/uninitialized-zeroed.rs:43:30
    |
 LL |         let _val: (i32, !) = mem::uninitialized();
    |                              ^^^^^^^^^^^^^^^^^^^^
@@ -100,7 +100,7 @@ LL |         let _val: (i32, !) = mem::uninitialized();
    = note: The never type (`!`) has no valid value
 
 error: the type `Void` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:44:26
+  --> $DIR/uninitialized-zeroed.rs:45:26
    |
 LL |         let _val: Void = mem::zeroed();
    |                          ^^^^^^^^^^^^^
@@ -111,7 +111,7 @@ LL |         let _val: Void = mem::zeroed();
    = note: 0-variant enums have no valid value
 
 error: the type `Void` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:45:26
+  --> $DIR/uninitialized-zeroed.rs:46:26
    |
 LL |         let _val: Void = mem::uninitialized();
    |                          ^^^^^^^^^^^^^^^^^^^^
@@ -122,7 +122,7 @@ LL |         let _val: Void = mem::uninitialized();
    = note: 0-variant enums have no valid value
 
 error: the type `&'static i32` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:47:34
+  --> $DIR/uninitialized-zeroed.rs:48:34
    |
 LL |         let _val: &'static i32 = mem::zeroed();
    |                                  ^^^^^^^^^^^^^
@@ -133,7 +133,7 @@ LL |         let _val: &'static i32 = mem::zeroed();
    = note: References must be non-null
 
 error: the type `&'static i32` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:48:34
+  --> $DIR/uninitialized-zeroed.rs:49:34
    |
 LL |         let _val: &'static i32 = mem::uninitialized();
    |                                  ^^^^^^^^^^^^^^^^^^^^
@@ -144,7 +144,7 @@ LL |         let _val: &'static i32 = mem::uninitialized();
    = note: References must be non-null
 
 error: the type `Ref` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:50:25
+  --> $DIR/uninitialized-zeroed.rs:51:25
    |
 LL |         let _val: Ref = mem::zeroed();
    |                         ^^^^^^^^^^^^^
@@ -153,13 +153,13 @@ LL |         let _val: Ref = mem::zeroed();
    |                         help: use `MaybeUninit<T>` instead
    |
 note: References must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:13:12
+  --> $DIR/uninitialized-zeroed.rs:14:12
    |
 LL | struct Ref(&'static i32);
    |            ^^^^^^^^^^^^
 
 error: the type `Ref` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:51:25
+  --> $DIR/uninitialized-zeroed.rs:52:25
    |
 LL |         let _val: Ref = mem::uninitialized();
    |                         ^^^^^^^^^^^^^^^^^^^^
@@ -168,13 +168,13 @@ LL |         let _val: Ref = mem::uninitialized();
    |                         help: use `MaybeUninit<T>` instead
    |
 note: References must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:13:12
+  --> $DIR/uninitialized-zeroed.rs:14:12
    |
 LL | struct Ref(&'static i32);
    |            ^^^^^^^^^^^^
 
 error: the type `fn()` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:53:26
+  --> $DIR/uninitialized-zeroed.rs:54:26
    |
 LL |         let _val: fn() = mem::zeroed();
    |                          ^^^^^^^^^^^^^
@@ -185,7 +185,7 @@ LL |         let _val: fn() = mem::zeroed();
    = note: Function pointers must be non-null
 
 error: the type `fn()` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:54:26
+  --> $DIR/uninitialized-zeroed.rs:55:26
    |
 LL |         let _val: fn() = mem::uninitialized();
    |                          ^^^^^^^^^^^^^^^^^^^^
@@ -196,7 +196,7 @@ LL |         let _val: fn() = mem::uninitialized();
    = note: Function pointers must be non-null
 
 error: the type `Wrap<fn()>` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:56:32
+  --> $DIR/uninitialized-zeroed.rs:57:32
    |
 LL |         let _val: Wrap<fn()> = mem::zeroed();
    |                                ^^^^^^^^^^^^^
@@ -205,13 +205,13 @@ LL |         let _val: Wrap<fn()> = mem::zeroed();
    |                                help: use `MaybeUninit<T>` instead
    |
 note: Function pointers must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:16:18
+  --> $DIR/uninitialized-zeroed.rs:17:18
    |
 LL | struct Wrap<T> { wrapped: T }
    |                  ^^^^^^^^^^
 
 error: the type `Wrap<fn()>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:57:32
+  --> $DIR/uninitialized-zeroed.rs:58:32
    |
 LL |         let _val: Wrap<fn()> = mem::uninitialized();
    |                                ^^^^^^^^^^^^^^^^^^^^
@@ -220,13 +220,13 @@ LL |         let _val: Wrap<fn()> = mem::uninitialized();
    |                                help: use `MaybeUninit<T>` instead
    |
 note: Function pointers must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:16:18
+  --> $DIR/uninitialized-zeroed.rs:17:18
    |
 LL | struct Wrap<T> { wrapped: T }
    |                  ^^^^^^^^^^
 
 error: the type `WrapEnum<fn()>` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:59:36
+  --> $DIR/uninitialized-zeroed.rs:60:36
    |
 LL |         let _val: WrapEnum<fn()> = mem::zeroed();
    |                                    ^^^^^^^^^^^^^
@@ -235,13 +235,13 @@ LL |         let _val: WrapEnum<fn()> = mem::zeroed();
    |                                    help: use `MaybeUninit<T>` instead
    |
 note: Function pointers must be non-null (in this enum field)
-  --> $DIR/uninitialized-zeroed.rs:17:28
+  --> $DIR/uninitialized-zeroed.rs:18:28
    |
 LL | enum WrapEnum<T> { Wrapped(T) }
    |                            ^
 
 error: the type `WrapEnum<fn()>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:60:36
+  --> $DIR/uninitialized-zeroed.rs:61:36
    |
 LL |         let _val: WrapEnum<fn()> = mem::uninitialized();
    |                                    ^^^^^^^^^^^^^^^^^^^^
@@ -250,13 +250,13 @@ LL |         let _val: WrapEnum<fn()> = mem::uninitialized();
    |                                    help: use `MaybeUninit<T>` instead
    |
 note: Function pointers must be non-null (in this enum field)
-  --> $DIR/uninitialized-zeroed.rs:17:28
+  --> $DIR/uninitialized-zeroed.rs:18:28
    |
 LL | enum WrapEnum<T> { Wrapped(T) }
    |                            ^
 
 error: the type `Wrap<(RefPair, i32)>` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:62:42
+  --> $DIR/uninitialized-zeroed.rs:63:42
    |
 LL |         let _val: Wrap<(RefPair, i32)> = mem::zeroed();
    |                                          ^^^^^^^^^^^^^
@@ -265,13 +265,13 @@ LL |         let _val: Wrap<(RefPair, i32)> = mem::zeroed();
    |                                          help: use `MaybeUninit<T>` instead
    |
 note: References must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:14:16
+  --> $DIR/uninitialized-zeroed.rs:15:16
    |
 LL | struct RefPair((&'static i32, i32));
    |                ^^^^^^^^^^^^^^^^^^^
 
 error: the type `Wrap<(RefPair, i32)>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:63:42
+  --> $DIR/uninitialized-zeroed.rs:64:42
    |
 LL |         let _val: Wrap<(RefPair, i32)> = mem::uninitialized();
    |                                          ^^^^^^^^^^^^^^^^^^^^
@@ -280,13 +280,13 @@ LL |         let _val: Wrap<(RefPair, i32)> = mem::uninitialized();
    |                                          help: use `MaybeUninit<T>` instead
    |
 note: References must be non-null (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:14:16
+  --> $DIR/uninitialized-zeroed.rs:15:16
    |
 LL | struct RefPair((&'static i32, i32));
    |                ^^^^^^^^^^^^^^^^^^^
 
 error: the type `std::vec::Vec<i32>` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:65:30
+  --> $DIR/uninitialized-zeroed.rs:66:30
    |
 LL |         let _val: Vec<i32> = mem::zeroed();
    |                              ^^^^^^^^^^^^^
@@ -301,7 +301,7 @@ LL |     ptr: Unique<T>,
    |     ^^^^^^^^^^^^^^
 
 error: the type `std::vec::Vec<i32>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:66:30
+  --> $DIR/uninitialized-zeroed.rs:67:30
    |
 LL |         let _val: Vec<i32> = mem::uninitialized();
    |                              ^^^^^^^^^^^^^^^^^^^^
@@ -316,7 +316,7 @@ LL |     ptr: Unique<T>,
    |     ^^^^^^^^^^^^^^
 
 error: the type `bool` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:70:26
+  --> $DIR/uninitialized-zeroed.rs:71:26
    |
 LL |         let _val: bool = mem::uninitialized();
    |                          ^^^^^^^^^^^^^^^^^^^^
@@ -327,7 +327,7 @@ LL |         let _val: bool = mem::uninitialized();
    = note: Booleans must be `true` or `false`
 
 error: the type `Wrap<char>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:73:32
+  --> $DIR/uninitialized-zeroed.rs:74:32
    |
 LL |         let _val: Wrap<char> = mem::uninitialized();
    |                                ^^^^^^^^^^^^^^^^^^^^
@@ -336,13 +336,13 @@ LL |         let _val: Wrap<char> = mem::uninitialized();
    |                                help: use `MaybeUninit<T>` instead
    |
 note: Characters must be a valid unicode codepoint (in this struct field)
-  --> $DIR/uninitialized-zeroed.rs:16:18
+  --> $DIR/uninitialized-zeroed.rs:17:18
    |
 LL | struct Wrap<T> { wrapped: T }
    |                  ^^^^^^^^^^
 
 error: the type `NonBig` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:76:28
+  --> $DIR/uninitialized-zeroed.rs:77:28
    |
 LL |         let _val: NonBig = mem::uninitialized();
    |                            ^^^^^^^^^^^^^^^^^^^^
@@ -352,5 +352,38 @@ LL |         let _val: NonBig = mem::uninitialized();
    |
    = note: NonBig must be initialized inside its custom valid range
 
-error: aborting due to 27 previous errors
+error: the type `&'static i32` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:80:34
+   |
+LL |         let _val: &'static i32 = mem::transmute(0usize);
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^
+   |                                  |
+   |                                  this code causes undefined behavior when executed
+   |                                  help: use `MaybeUninit<T>` instead
+   |
+   = note: References must be non-null
+
+error: the type `&'static [i32]` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:81:36
+   |
+LL |         let _val: &'static [i32] = mem::transmute((0usize, 0usize));
+   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                                    |
+   |                                    this code causes undefined behavior when executed
+   |                                    help: use `MaybeUninit<T>` instead
+   |
+   = note: References must be non-null
+
+error: the type `std::num::NonZeroU32` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:82:32
+   |
+LL |         let _val: NonZeroU32 = mem::transmute(0);
+   |                                ^^^^^^^^^^^^^^^^^
+   |                                |
+   |                                this code causes undefined behavior when executed
+   |                                help: use `MaybeUninit<T>` instead
+   |
+   = note: std::num::NonZeroU32 must be non-null
+
+error: aborting due to 30 previous errors