Skip to content

Commit ad832a1

Browse files
committed
add basic lint testing for misuse of mem::zeroed and mem::uninitialized
1 parent 188ab5c commit ad832a1

File tree

6 files changed

+136
-1
lines changed

6 files changed

+136
-1
lines changed

src/librustc_lint/builtin.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1862,3 +1862,62 @@ impl EarlyLintPass for IncompleteFeatures {
18621862
});
18631863
}
18641864
}
1865+
1866+
declare_lint! {
1867+
pub INVALID_VALUE,
1868+
Warn,
1869+
"an invalid value is being created (such as a NULL reference)"
1870+
}
1871+
1872+
declare_lint_pass!(InvalidValue => [INVALID_VALUE]);
1873+
1874+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
1875+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &hir::Expr) {
1876+
1877+
const ZEROED_PATH: &[Symbol] = &[sym::core, sym::mem, sym::zeroed];
1878+
const UININIT_PATH: &[Symbol] = &[sym::core, sym::mem, sym::uninitialized];
1879+
1880+
/// Return `false` only if we are sure this type does *not*
1881+
/// allow zero initialization.
1882+
fn ty_maybe_allows_zero_init(ty: Ty<'_>) -> bool {
1883+
use rustc::ty::TyKind::*;
1884+
match ty.sty {
1885+
// Primitive types that don't like 0 as a value.
1886+
Ref(..) | FnPtr(..) | Never => false,
1887+
// Conservative fallback.
1888+
_ => true,
1889+
}
1890+
}
1891+
1892+
if let hir::ExprKind::Call(ref path_expr, ref _args) = expr.node {
1893+
if let hir::ExprKind::Path(ref qpath) = path_expr.node {
1894+
if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
1895+
if cx.match_def_path(def_id, &ZEROED_PATH) ||
1896+
cx.match_def_path(def_id, &UININIT_PATH)
1897+
{
1898+
// This conjures an instance of a type out of nothing,
1899+
// using zeroed or uninitialized memory.
1900+
// We are extremely conservative with what we warn about.
1901+
let conjured_ty = cx.tables.expr_ty(expr);
1902+
1903+
if !ty_maybe_allows_zero_init(conjured_ty) {
1904+
cx.span_lint(
1905+
INVALID_VALUE,
1906+
expr.span,
1907+
&format!(
1908+
"the type `{}` does not permit {}",
1909+
conjured_ty,
1910+
if cx.match_def_path(def_id, &ZEROED_PATH) {
1911+
"zero-initialization"
1912+
} else {
1913+
"being left uninitialized"
1914+
}
1915+
),
1916+
);
1917+
}
1918+
}
1919+
}
1920+
}
1921+
}
1922+
}
1923+
}

src/librustc_lint/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ macro_rules! late_lint_mod_passes {
177177
UnreachablePub: UnreachablePub,
178178

179179
ExplicitOutlivesRequirements: ExplicitOutlivesRequirements,
180+
InvalidValue: InvalidValue,
180181
]);
181182
)
182183
}

src/libsyntax_pos/symbol.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@ symbols! {
412412
match_beginning_vert,
413413
match_default_bindings,
414414
may_dangle,
415+
mem,
415416
member_constraints,
416417
message,
417418
meta,
@@ -695,6 +696,7 @@ symbols! {
695696
underscore_imports,
696697
underscore_lifetimes,
697698
uniform_paths,
699+
uninitialized,
698700
universal_impl_trait,
699701
unmarked_api,
700702
unreachable_code,
@@ -726,6 +728,7 @@ symbols! {
726728
windows,
727729
windows_subsystem,
728730
Yield,
731+
zeroed,
729732
}
730733
}
731734

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// ignore-tidy-linelength
2+
// This test checks that calling `mem::{uninitialized,zeroed}` with certain types results
3+
// in a lint.
4+
5+
#![feature(never_type)]
6+
#![allow(deprecated)]
7+
#![deny(invalid_value)]
8+
9+
use std::mem;
10+
11+
fn main() {
12+
unsafe {
13+
let _val: ! = mem::zeroed(); //~ ERROR: does not permit zero-initialization
14+
let _val: ! = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
15+
16+
let _val: &'static i32 = mem::zeroed(); //~ ERROR: does not permit zero-initialization
17+
let _val: &'static i32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
18+
19+
let _val: fn() = mem::zeroed(); //~ ERROR: does not permit zero-initialization
20+
let _val: fn() = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
21+
22+
// Some types that should work just fine.
23+
let _val: Option<&'static i32> = mem::zeroed();
24+
let _val: Option<fn()> = mem::zeroed();
25+
let _val: bool = mem::zeroed();
26+
let _val: i32 = mem::zeroed();
27+
}
28+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
error: the type `!` does not permit zero-initialization
2+
--> $DIR/uninitialized-zeroed.rs:15:23
3+
|
4+
LL | let _val: ! = mem::zeroed();
5+
| ^^^^^^^^^^^^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/uninitialized-zeroed.rs:7:9
9+
|
10+
LL | #![deny(invalid_value)]
11+
| ^^^^^^^^^^^^^
12+
13+
error: the type `!` does not permit being left uninitialized
14+
--> $DIR/uninitialized-zeroed.rs:16:23
15+
|
16+
LL | let _val: ! = mem::uninitialized();
17+
| ^^^^^^^^^^^^^^^^^^^^
18+
19+
error: the type `&'static i32` does not permit zero-initialization
20+
--> $DIR/uninitialized-zeroed.rs:21:34
21+
|
22+
LL | let _val: &'static i32 = mem::zeroed();
23+
| ^^^^^^^^^^^^^
24+
25+
error: the type `&'static i32` does not permit being left uninitialized
26+
--> $DIR/uninitialized-zeroed.rs:22:34
27+
|
28+
LL | let _val: &'static i32 = mem::uninitialized();
29+
| ^^^^^^^^^^^^^^^^^^^^
30+
31+
error: the type `fn()` does not permit zero-initialization
32+
--> $DIR/uninitialized-zeroed.rs:24:26
33+
|
34+
LL | let _val: fn() = mem::zeroed();
35+
| ^^^^^^^^^^^^^
36+
37+
error: the type `fn()` does not permit being left uninitialized
38+
--> $DIR/uninitialized-zeroed.rs:25:26
39+
|
40+
LL | let _val: fn() = mem::uninitialized();
41+
| ^^^^^^^^^^^^^^^^^^^^
42+
43+
error: aborting due to 6 previous errors
44+

src/test/ui/panic-uninitialized-zeroed.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// in a runtime panic.
55

66
#![feature(never_type)]
7-
#![allow(deprecated)]
7+
#![allow(deprecated, invalid_value)]
88

99
use std::{mem, panic};
1010

0 commit comments

Comments
 (0)