Skip to content

Commit a133e53

Browse files
authored
Rollup merge of rust-lang#63346 - RalfJung:zeroed-lint, r=eddyb
Lint on some incorrect uses of mem::zeroed / mem::uninitialized Cc rust-lang#62825 and https://internals.rust-lang.org/t/make-mem-uninitialized-and-mem-zeroed-panic-for-some-types-where-0-is-a-niche/10605 This does not yet handle `NonNull`/`NonZero*`, but it is a start. I also improved some doc issues I hit on the way, and added a useful helper to `TyS`.
2 parents 3b99865 + 6c7d84d commit a133e53

File tree

14 files changed

+353
-16
lines changed

14 files changed

+353
-16
lines changed

src/libcore/mem/maybe_uninit.rs

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::mem::ManuallyDrop;
1313
/// ever gets used to access memory:
1414
///
1515
/// ```rust,no_run
16+
/// # #![allow(invalid_value)]
1617
/// use std::mem::{self, MaybeUninit};
1718
///
1819
/// let x: &i32 = unsafe { mem::zeroed() }; // undefined behavior!
@@ -27,6 +28,7 @@ use crate::mem::ManuallyDrop;
2728
/// always be `true` or `false`. Hence, creating an uninitialized `bool` is undefined behavior:
2829
///
2930
/// ```rust,no_run
31+
/// # #![allow(invalid_value)]
3032
/// use std::mem::{self, MaybeUninit};
3133
///
3234
/// let b: bool = unsafe { mem::uninitialized() }; // undefined behavior!
@@ -40,6 +42,7 @@ use crate::mem::ManuallyDrop;
4042
/// which otherwise can hold any *fixed* bit pattern:
4143
///
4244
/// ```rust,no_run
45+
/// # #![allow(invalid_value)]
4346
/// use std::mem::{self, MaybeUninit};
4447
///
4548
/// let x: i32 = unsafe { mem::uninitialized() }; // undefined behavior!

src/libcore/mem/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,8 @@ pub const fn needs_drop<T>() -> bool {
445445
///
446446
/// *Incorrect* usage of this function: initializing a reference with zero.
447447
///
448-
/// ```no_run
448+
/// ```rust,no_run
449+
/// # #![allow(invalid_value)]
449450
/// use std::mem;
450451
///
451452
/// let _x: &i32 = unsafe { mem::zeroed() }; // Undefined behavior!

src/librustc/ty/mod.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1842,7 +1842,8 @@ pub struct VariantDef {
18421842
pub ctor_kind: CtorKind,
18431843
/// Flags of the variant (e.g. is field list non-exhaustive)?
18441844
flags: VariantFlags,
1845-
/// Recovered?
1845+
/// Variant is obtained as part of recovering from a syntactic error.
1846+
/// May be incomplete or bogus.
18461847
pub recovered: bool,
18471848
}
18481849

@@ -1949,7 +1950,7 @@ pub struct FieldDef {
19491950
pub struct AdtDef {
19501951
/// `DefId` of the struct, enum or union item.
19511952
pub did: DefId,
1952-
/// Variants of the ADT. If this is a struct or enum, then there will be a single variant.
1953+
/// Variants of the ADT. If this is a struct or union, then there will be a single variant.
19531954
pub variants: IndexVec<self::layout::VariantIdx, VariantDef>,
19541955
/// Flags of the ADT (e.g. is this a struct? is this non-exhaustive?)
19551956
flags: AdtFlags,
@@ -2565,6 +2566,8 @@ impl<'tcx> AdtDef {
25652566
}
25662567

25672568
impl<'tcx> FieldDef {
2569+
/// Returns the type of this field. The `subst` is typically obtained
2570+
/// via the second field of `TyKind::AdtDef`.
25682571
pub fn ty(&self, tcx: TyCtxt<'tcx>, subst: SubstsRef<'tcx>) -> Ty<'tcx> {
25692572
tcx.type_of(self.did).subst(tcx, subst)
25702573
}

src/librustc/ty/sty.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ pub enum TyKind<'tcx> {
171171
Never,
172172

173173
/// A tuple type. For example, `(i32, bool)`.
174+
/// Use `TyS::tuple_fields` to iterate over the field types.
174175
Tuple(SubstsRef<'tcx>),
175176

176177
/// The projection of an associated type. For example,
@@ -1723,8 +1724,8 @@ impl<'tcx> TyS<'tcx> {
17231724
})
17241725
})
17251726
}
1726-
ty::Tuple(tys) => tys.iter().any(|ty| {
1727-
ty.expect_ty().conservative_is_privately_uninhabited(tcx)
1727+
ty::Tuple(..) => self.tuple_fields().any(|ty| {
1728+
ty.conservative_is_privately_uninhabited(tcx)
17281729
}),
17291730
ty::Array(ty, len) => {
17301731
match len.try_eval_usize(tcx, ParamEnv::empty()) {
@@ -2103,6 +2104,15 @@ impl<'tcx> TyS<'tcx> {
21032104
}
21042105
}
21052106

2107+
/// Iterates over tuple fields.
2108+
/// Panics when called on anything but a tuple.
2109+
pub fn tuple_fields(&self) -> impl DoubleEndedIterator<Item=Ty<'tcx>> {
2110+
match self.sty {
2111+
Tuple(substs) => substs.iter().map(|field| field.expect_ty()),
2112+
_ => bug!("tuple_fields called on non-tuple"),
2113+
}
2114+
}
2115+
21062116
/// If the type contains variants, returns the valid range of variant indices.
21072117
/// FIXME This requires the optimized MIR in the case of generators.
21082118
#[inline]

src/librustc/ty/util.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -845,15 +845,15 @@ impl<'tcx> ty::TyS<'tcx> {
845845
ty: Ty<'tcx>,
846846
) -> Representability {
847847
match ty.sty {
848-
Tuple(ref ts) => {
848+
Tuple(..) => {
849849
// Find non representable
850-
fold_repr(ts.iter().map(|ty| {
850+
fold_repr(ty.tuple_fields().map(|ty| {
851851
is_type_structurally_recursive(
852852
tcx,
853853
sp,
854854
seen,
855855
representable_cache,
856-
ty.expect_ty(),
856+
ty,
857857
)
858858
}))
859859
}
@@ -1095,7 +1095,7 @@ fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>
10951095
// state transformation pass
10961096
ty::Generator(..) => true,
10971097

1098-
ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).any(needs_drop),
1098+
ty::Tuple(..) => ty.tuple_fields().any(needs_drop),
10991099

11001100
// unions don't have destructors because of the child types,
11011101
// only if they manually implement `Drop` (handled above).

src/librustc/ty/walk.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) {
119119
ty::GeneratorWitness(ts) => {
120120
stack.extend(ts.skip_binder().iter().cloned().rev());
121121
}
122-
ty::Tuple(ts) => {
123-
stack.extend(ts.iter().map(|k| k.expect_ty()).rev());
122+
ty::Tuple(..) => {
123+
stack.extend(parent_ty.tuple_fields().rev());
124124
}
125125
ty::FnDef(_, substs) => {
126126
stack.extend(substs.types().rev());

src/librustc_lint/builtin.rs

+90-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
2424
use rustc::hir::def::{Res, DefKind};
2525
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
26-
use rustc::ty::{self, Ty, TyCtxt};
26+
use rustc::ty::{self, Ty, TyCtxt, layout::VariantIdx};
2727
use rustc::{lint, util};
2828
use hir::Node;
2929
use util::nodemap::HirIdSet;
@@ -1862,3 +1862,92 @@ 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<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> 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+
Adt(..) if ty.is_box() => false,
1888+
// Recurse for some compound types.
1889+
Adt(adt_def, substs) if !adt_def.is_union() => {
1890+
match adt_def.variants.len() {
1891+
0 => false, // Uninhabited enum!
1892+
1 => {
1893+
// Struct, or enum with exactly one variant.
1894+
// Proceed recursively, check all fields.
1895+
let variant = &adt_def.variants[VariantIdx::from_u32(0)];
1896+
variant.fields.iter().all(|field| {
1897+
ty_maybe_allows_zero_init(
1898+
tcx,
1899+
field.ty(tcx, substs),
1900+
)
1901+
})
1902+
}
1903+
_ => true, // Conservative fallback for multi-variant enum.
1904+
}
1905+
}
1906+
Tuple(..) => {
1907+
// Proceed recursively, check all fields.
1908+
ty.tuple_fields().all(|field| ty_maybe_allows_zero_init(tcx, field))
1909+
}
1910+
// FIXME: Would be nice to also warn for `NonNull`/`NonZero*`.
1911+
// FIXME: *Only for `mem::uninitialized`*, we could also warn for `bool`,
1912+
// `char`, and any multivariant enum.
1913+
// Conservative fallback.
1914+
_ => true,
1915+
}
1916+
}
1917+
1918+
if let hir::ExprKind::Call(ref path_expr, ref _args) = expr.node {
1919+
if let hir::ExprKind::Path(ref qpath) = path_expr.node {
1920+
if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
1921+
if cx.match_def_path(def_id, &ZEROED_PATH) ||
1922+
cx.match_def_path(def_id, &UININIT_PATH)
1923+
{
1924+
// This conjures an instance of a type out of nothing,
1925+
// using zeroed or uninitialized memory.
1926+
// We are extremely conservative with what we warn about.
1927+
let conjured_ty = cx.tables.expr_ty(expr);
1928+
1929+
if !ty_maybe_allows_zero_init(cx.tcx, conjured_ty) {
1930+
cx.struct_span_lint(
1931+
INVALID_VALUE,
1932+
expr.span,
1933+
&format!(
1934+
"the type `{}` does not permit {}",
1935+
conjured_ty,
1936+
if cx.match_def_path(def_id, &ZEROED_PATH) {
1937+
"zero-initialization"
1938+
} else {
1939+
"being left uninitialized"
1940+
}
1941+
),
1942+
)
1943+
.note("this means that this code causes undefined behavior \
1944+
when executed")
1945+
.help("use `MaybeUninit` instead")
1946+
.emit();
1947+
}
1948+
}
1949+
}
1950+
}
1951+
}
1952+
}
1953+
}

src/librustc_lint/lib.rs

+1
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/librustc_mir/shim.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -
324324
substs.upvar_tys(def_id, tcx)
325325
)
326326
}
327-
ty::Tuple(tys) => builder.tuple_like_shim(dest, src, tys.iter().map(|k| k.expect_ty())),
327+
ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()),
328328
_ => {
329329
bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty)
330330
}

src/librustc_mir/util/elaborate_drops.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -804,8 +804,8 @@ where
804804
let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect();
805805
self.open_drop_for_tuple(&tys)
806806
}
807-
ty::Tuple(tys) => {
808-
let tys: Vec<_> = tys.iter().map(|k| k.expect_ty()).collect();
807+
ty::Tuple(..) => {
808+
let tys: Vec<_> = ty.tuple_fields().collect();
809809
self.open_drop_for_tuple(&tys)
810810
}
811811
ty::Adt(def, substs) => {

src/libsyntax_pos/symbol.rs

+3
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

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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::{self, MaybeUninit};
10+
11+
enum Void {}
12+
13+
struct Ref(&'static i32);
14+
15+
struct Wrap<T> { wrapped: T }
16+
17+
#[allow(unused)]
18+
fn generic<T: 'static>() {
19+
unsafe {
20+
let _val: &'static T = mem::zeroed(); //~ ERROR: does not permit zero-initialization
21+
let _val: &'static T = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
22+
23+
let _val: Wrap<&'static T> = mem::zeroed(); //~ ERROR: does not permit zero-initialization
24+
let _val: Wrap<&'static T> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
25+
}
26+
}
27+
28+
fn main() {
29+
unsafe {
30+
let _val: ! = mem::zeroed(); //~ ERROR: does not permit zero-initialization
31+
let _val: ! = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
32+
33+
let _val: (i32, !) = mem::zeroed(); //~ ERROR: does not permit zero-initialization
34+
let _val: (i32, !) = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
35+
36+
let _val: Void = mem::zeroed(); //~ ERROR: does not permit zero-initialization
37+
let _val: Void = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
38+
39+
let _val: &'static i32 = mem::zeroed(); //~ ERROR: does not permit zero-initialization
40+
let _val: &'static i32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
41+
42+
let _val: Ref = mem::zeroed(); //~ ERROR: does not permit zero-initialization
43+
let _val: Ref = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
44+
45+
let _val: fn() = mem::zeroed(); //~ ERROR: does not permit zero-initialization
46+
let _val: fn() = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
47+
48+
let _val: Wrap<fn()> = mem::zeroed(); //~ ERROR: does not permit zero-initialization
49+
let _val: Wrap<fn()> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
50+
51+
// Some types that should work just fine.
52+
let _val: Option<&'static i32> = mem::zeroed();
53+
let _val: Option<fn()> = mem::zeroed();
54+
let _val: MaybeUninit<&'static i32> = mem::zeroed();
55+
let _val: bool = mem::zeroed();
56+
let _val: i32 = mem::zeroed();
57+
}
58+
}

0 commit comments

Comments
 (0)