From df4e12d88947db6ff832bb7caae44927af687eb7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 2 Nov 2019 11:56:06 +0100 Subject: [PATCH 1/3] uninit/zeroed lint: warn against NULL vtables --- src/librustc_lint/builtin.rs | 2 ++ src/librustc_lint/lib.rs | 1 + src/test/ui/lint/uninitialized-zeroed.rs | 3 ++ src/test/ui/lint/uninitialized-zeroed.stderr | 36 ++++++++++++++++---- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index e3c3966c2f5e0..e1c57d8afffb8 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1949,6 +1949,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { Adt(..) if ty.is_box() => Some((format!("`Box` must be non-null"), None)), FnPtr(..) => Some((format!("Function pointers must be non-null"), None)), Never => Some((format!("The never type (`!`) has no valid value"), None)), + RawPtr(tm) if matches!(tm.ty.kind, Dynamic(..)) => // raw ptr to dyn Trait + Some((format!("The vtable of a wide raw pointer must be non-null"), None)), // Primitive types with other constraints. Bool if init == InitKind::Uninit => Some((format!("Booleans must be `true` or `false`"), None)), diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index b1beef04c5929..a47980c5ead30 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -15,6 +15,7 @@ #![feature(box_patterns)] #![feature(box_syntax)] #![feature(nll)] +#![feature(matches_macro)] #![recursion_limit="256"] diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs index 5cf62b8691239..ccc4e77bc97d6 100644 --- a/src/test/ui/lint/uninitialized-zeroed.rs +++ b/src/test/ui/lint/uninitialized-zeroed.rs @@ -67,6 +67,9 @@ fn main() { let _val: NonNull = mem::zeroed(); //~ ERROR: does not permit zero-initialization let _val: NonNull = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + let _val: *const dyn Send = mem::zeroed(); //~ ERROR: does not permit zero-initialization + let _val: *const dyn Send = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized + // Things that can be zero, but not uninit. let _val: bool = mem::zeroed(); let _val: bool = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr index a36a32a39a11b..85b1e0aaff00e 100644 --- a/src/test/ui/lint/uninitialized-zeroed.stderr +++ b/src/test/ui/lint/uninitialized-zeroed.stderr @@ -307,8 +307,30 @@ LL | let _val: NonNull = mem::uninitialized(); | = note: std::ptr::NonNull must be non-null +error: the type `*const dyn std::marker::Send` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:70:37 + | +LL | let _val: *const dyn Send = mem::zeroed(); + | ^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | + = note: The vtable of a wide raw pointer must be non-null + +error: the type `*const dyn std::marker::Send` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:71:37 + | +LL | let _val: *const dyn Send = mem::uninitialized(); + | ^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | + = note: The vtable of a wide raw pointer must be non-null + error: the type `bool` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:72:26 + --> $DIR/uninitialized-zeroed.rs:75:26 | LL | let _val: bool = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -319,7 +341,7 @@ LL | let _val: bool = mem::uninitialized(); = note: Booleans must be `true` or `false` error: the type `Wrap` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:75:32 + --> $DIR/uninitialized-zeroed.rs:78:32 | LL | let _val: Wrap = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -334,7 +356,7 @@ LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ error: the type `NonBig` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:78:28 + --> $DIR/uninitialized-zeroed.rs:81:28 | LL | let _val: NonBig = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -345,7 +367,7 @@ LL | let _val: NonBig = mem::uninitialized(); = note: NonBig must be initialized inside its custom valid range error: the type `&'static i32` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:81:34 + --> $DIR/uninitialized-zeroed.rs:84:34 | LL | let _val: &'static i32 = mem::transmute(0usize); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -356,7 +378,7 @@ LL | let _val: &'static i32 = mem::transmute(0usize); = note: References must be non-null error: the type `&'static [i32]` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:82:36 + --> $DIR/uninitialized-zeroed.rs:85:36 | LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -367,7 +389,7 @@ LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize)); = note: References must be non-null error: the type `std::num::NonZeroU32` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:83:32 + --> $DIR/uninitialized-zeroed.rs:86:32 | LL | let _val: NonZeroU32 = mem::transmute(0); | ^^^^^^^^^^^^^^^^^ @@ -377,5 +399,5 @@ LL | let _val: NonZeroU32 = mem::transmute(0); | = note: std::num::NonZeroU32 must be non-null -error: aborting due to 30 previous errors +error: aborting due to 32 previous errors From 7ff57edb93625857b1ac289160550859e78ef6fb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 2 Nov 2019 16:12:33 +0100 Subject: [PATCH 2/3] also identiy MaybeUninit::uninit().assume_init() as dangerous --- src/librustc/lint/context.rs | 3 ++ src/librustc_lint/builtin.rs | 23 +++++++++++-- src/libsyntax_pos/symbol.rs | 4 +++ src/test/ui/lint/uninitialized-zeroed.rs | 6 ++++ src/test/ui/lint/uninitialized-zeroed.stderr | 35 +++++++++++++++++++- 5 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 1cb53d754dcd3..99a45c4e76104 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -699,6 +699,9 @@ impl<'a, 'tcx> LateContext<'a, 'tcx> { /// Check if a `DefId`'s path matches the given absolute type path usage. /// + /// Anonymous scopes such as `extern` imports are matched with `kw::Invalid`; + /// inherent `impl` blocks are matched with the name of the type. + /// /// # Examples /// /// ```rust,ignore (no context or def id available) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index e1c57d8afffb8..7f7db46466f38 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1911,8 +1911,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { // `Invalid` represents the empty string and matches that. const TRANSMUTE_PATH: &[Symbol] = &[sym::core, sym::intrinsics, kw::Invalid, sym::transmute]; + const MU_ZEROED_PATH: &[Symbol] = + &[sym::core, sym::mem, sym::maybe_uninit, sym::MaybeUninit, sym::zeroed]; + const MU_UNINIT_PATH: &[Symbol] = + &[sym::core, sym::mem, sym::maybe_uninit, sym::MaybeUninit, sym::uninit]; if let hir::ExprKind::Call(ref path_expr, ref args) = expr.kind { + // Find calls to `mem::{uninitialized,zeroed}` methods. if let hir::ExprKind::Path(ref qpath) = path_expr.kind { let def_id = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; @@ -1927,8 +1932,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { return Some(InitKind::Zeroed); } } - // FIXME: Also detect `MaybeUninit::zeroed().assume_init()` and - // `MaybeUninit::uninit().assume_init()`. + } + } else if let hir::ExprKind::MethodCall(ref path, _, ref args) = expr.kind { + // Find problematic calls to `MaybeUninit::assume_init`. + if path.ident.name == sym::assume_init { + // This is a call to *some* method named `assume_init`. + // See if the `self` parameter is one of the dangerous constructors. + if let hir::ExprKind::Call(ref path_expr, _) = args[0].kind { + if let hir::ExprKind::Path(ref qpath) = path_expr.kind { + let def_id = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; + if cx.match_def_path(def_id, MU_ZEROED_PATH) { + return Some(InitKind::Zeroed); + } else if cx.match_def_path(def_id, MU_UNINIT_PATH) { + return Some(InitKind::Uninit); + } + } + } } } diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 57131ffe18cb3..e3165a457b42b 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -148,6 +148,7 @@ symbols! { associated_type_bounds, associated_type_defaults, associated_types, + assume_init, async_await, async_closure, attr, @@ -417,6 +418,8 @@ symbols! { match_beginning_vert, match_default_bindings, may_dangle, + maybe_uninit, + MaybeUninit, mem, member_constraints, message, @@ -709,6 +712,7 @@ symbols! { underscore_imports, underscore_lifetimes, uniform_paths, + uninit, uninitialized, universal_impl_trait, unmarked_api, diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs index ccc4e77bc97d6..473be434a7524 100644 --- a/src/test/ui/lint/uninitialized-zeroed.rs +++ b/src/test/ui/lint/uninitialized-zeroed.rs @@ -85,10 +85,16 @@ fn main() { 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 + // `MaybeUninit` cases + let _val: NonNull = MaybeUninit::zeroed().assume_init(); //~ ERROR: does not permit zero-initialization + let _val: NonNull = MaybeUninit::uninit().assume_init(); //~ ERROR: does not permit being left uninitialized + let _val: bool = MaybeUninit::uninit().assume_init(); //~ ERROR: does not permit being left uninitialized + // Some more types that should work just fine. let _val: Option<&'static i32> = mem::zeroed(); let _val: Option = mem::zeroed(); let _val: MaybeUninit<&'static i32> = mem::zeroed(); let _val: i32 = mem::zeroed(); + let _val: bool = MaybeUninit::zeroed().assume_init(); } } diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr index 85b1e0aaff00e..e12b1897ade1b 100644 --- a/src/test/ui/lint/uninitialized-zeroed.stderr +++ b/src/test/ui/lint/uninitialized-zeroed.stderr @@ -399,5 +399,38 @@ LL | let _val: NonZeroU32 = mem::transmute(0); | = note: std::num::NonZeroU32 must be non-null -error: aborting due to 32 previous errors +error: the type `std::ptr::NonNull` does not permit zero-initialization + --> $DIR/uninitialized-zeroed.rs:89:34 + | +LL | let _val: NonNull = MaybeUninit::zeroed().assume_init(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | + = note: std::ptr::NonNull must be non-null + +error: the type `std::ptr::NonNull` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:90:34 + | +LL | let _val: NonNull = MaybeUninit::uninit().assume_init(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | + = note: std::ptr::NonNull must be non-null + +error: the type `bool` does not permit being left uninitialized + --> $DIR/uninitialized-zeroed.rs:91:26 + | +LL | let _val: bool = MaybeUninit::uninit().assume_init(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit` instead + | + = note: Booleans must be `true` or `false` + +error: aborting due to 35 previous errors From bb37d0078750b760f013bfa706fe19d4d823b8df Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 4 Nov 2019 10:11:58 +0100 Subject: [PATCH 3/3] more robust method checking through DefId and diagnostic_item --- src/libcore/mem/maybe_uninit.rs | 1 + src/librustc_lint/builtin.rs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libcore/mem/maybe_uninit.rs b/src/libcore/mem/maybe_uninit.rs index 792ce9dfad419..339a94c218005 100644 --- a/src/libcore/mem/maybe_uninit.rs +++ b/src/libcore/mem/maybe_uninit.rs @@ -440,6 +440,7 @@ impl MaybeUninit { /// ``` #[stable(feature = "maybe_uninit", since = "1.36.0")] #[inline(always)] + #[cfg_attr(all(not(bootstrap)), rustc_diagnostic_item = "assume_init")] pub unsafe fn assume_init(self) -> T { intrinsics::panic_if_uninhabited::(); ManuallyDrop::into_inner(self.value) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 7f7db46466f38..e38ecd882830d 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1933,9 +1933,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { } } } - } else if let hir::ExprKind::MethodCall(ref path, _, ref args) = expr.kind { + } else if let hir::ExprKind::MethodCall(_, _, ref args) = expr.kind { // Find problematic calls to `MaybeUninit::assume_init`. - if path.ident.name == sym::assume_init { + let def_id = cx.tables.type_dependent_def_id(expr.hir_id)?; + if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) { // This is a call to *some* method named `assume_init`. // See if the `self` parameter is one of the dangerous constructors. if let hir::ExprKind::Call(ref path_expr, _) = args[0].kind {