add basic lint testing for misuse of mem::zeroed and mem::uninitialized

This commit is contained in:
Ralf Jung 2019-08-06 23:11:52 +02:00
parent 6f70adcb18
commit da6fbb1895
6 changed files with 136 additions and 1 deletions

View File

@ -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"
}
),
);
}
}
}
}
}
}
}

View File

@ -177,6 +177,7 @@ macro_rules! late_lint_mod_passes {
UnreachablePub: UnreachablePub,
ExplicitOutlivesRequirements: ExplicitOutlivesRequirements,
InvalidValue: InvalidValue,
]);
)
}

View File

@ -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,
}
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -4,7 +4,7 @@
// in a runtime panic.
#![feature(never_type)]
#![allow(deprecated)]
#![allow(deprecated, invalid_value)]
use std::{mem, panic};