Skip to content

Commit 70cac59

Browse files
committed
Auto merge of #51361 - oli-obk:sanity_check_consts, r=nikomatsakis
Do a basic sanity check for all constant values ## Motivation and high level overview There has been some back and forth in this PR between @RalfJung and me in here about the motivation for this change and the stance it takes on unsafe coding guidelines. The initial implementation ran its checks on every value read (so `*x`, `y = x`, ...). In unsafe code that isn't reasonable, because we might be invalidating invariants for a short time in order to build up a proper value. The current implementation is a lint that runs its checks statics and constants. There is no need to check array lengths and enum variants, because it's a hard error to end up with anything but a number, and that one even has to have the required bits to be defined. ## What checks are done? * Some type related checks * `char` needs to be a correct unicode character as defined by `char::from_u32` * A reference to a ZST must have the correct alignment (and be nonzero) * A reference to anything is dereferenced and its value is checked * Layout checks use the information from `ty::Layout` to check * all fields of structs * all elements of arrays * enum discriminants * the fields of an enum variant (the variant is decided by the discriminant) * whether any union field succeeds in being checked (if none match the memory pattern, the check fails) * if the value is in the range described by the layout (e.g. for `NonZero*` types) Changing the layout of a type will thus automatically cause the checks to check for the new layout. fixes #51330 fixes #51471 cc @RalfJung r? @eddyb
2 parents 75af9df + 86e59cc commit 70cac59

36 files changed

+1013
-177
lines changed

src/Cargo.lock

+4-4
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ dependencies = [
355355
"cargo_metadata 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
356356
"clippy-mini-macro-test 0.2.0",
357357
"clippy_lints 0.0.212",
358-
"compiletest_rs 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
358+
"compiletest_rs 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
359359
"derive-new 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
360360
"lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
361361
"num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -461,7 +461,7 @@ dependencies = [
461461

462462
[[package]]
463463
name = "compiletest_rs"
464-
version = "0.3.11"
464+
version = "0.3.13"
465465
source = "registry+https://github.com/rust-lang/crates.io-index"
466466
dependencies = [
467467
"diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1319,7 +1319,7 @@ dependencies = [
13191319
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
13201320
"cargo_metadata 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
13211321
"colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
1322-
"compiletest_rs 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
1322+
"compiletest_rs 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
13231323
"env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
13241324
"lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
13251325
"log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3118,7 +3118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
31183118
"checksum colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0aa3473e85a3161b59845d6096b289bb577874cafeaf75ea1b1beaa6572c7fc"
31193119
"checksum commoncrypto 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d056a8586ba25a1e4d61cb090900e495952c7886786fc55f909ab2f819b69007"
31203120
"checksum commoncrypto-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fed34f46747aa73dfaa578069fd8279d2818ade2b55f38f22a9401c7f4083e2"
3121-
"checksum compiletest_rs 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "04cea0fe8b8aaca8143af607ad69076866c9f08b83c4b7faca0e993e5486831b"
3121+
"checksum compiletest_rs 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "d3064bc712922596dd5ab449fca9261d411893356581fe5297b96aa8f53bb1b8"
31223122
"checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980"
31233123
"checksum core-foundation 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cc3532ec724375c7cb7ff0a097b714fde180bb1f6ed2ab27cfcd99ffca873cd2"
31243124
"checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa"

src/librustc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
#![feature(in_band_lifetimes)]
7474
#![feature(macro_at_most_once_rep)]
7575
#![feature(inclusive_range_methods)]
76+
#![feature(crate_in_paths)]
7677

7778
#![recursion_limit="512"]
7879

src/librustc/mir/interpret/error.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
4747
tcx: TyCtxtAt<'a, 'gcx, 'tcx>,
4848
message: &str
4949
) {
50-
let err = self.struct_generic(tcx, message, None);
50+
let err = self.struct_error(tcx, message);
5151
if let Some(mut err) = err {
5252
err.emit();
5353
}

src/librustc/ty/layout.rs

+20-5
Original file line numberDiff line numberDiff line change
@@ -1599,7 +1599,7 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
15991599
// Potentially-fat pointers.
16001600
ty::TyRef(_, pointee, _) |
16011601
ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
1602-
assert!(i < 2);
1602+
assert!(i < this.fields.count());
16031603

16041604
// Reuse the fat *T type as its own thin pointer data field.
16051605
// This provides information about e.g. DST struct pointees
@@ -1621,10 +1621,25 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
16211621
match tcx.struct_tail(pointee).sty {
16221622
ty::TySlice(_) |
16231623
ty::TyStr => tcx.types.usize,
1624-
ty::TyDynamic(..) => {
1625-
// FIXME(eddyb) use an usize/fn() array with
1626-
// the correct number of vtables slots.
1627-
tcx.mk_imm_ref(tcx.types.re_static, tcx.mk_nil())
1624+
ty::TyDynamic(data, _) => {
1625+
let trait_def_id = data.principal().unwrap().def_id();
1626+
let num_fns: u64 = crate::traits::supertrait_def_ids(tcx, trait_def_id)
1627+
.map(|trait_def_id| {
1628+
tcx.associated_items(trait_def_id)
1629+
.filter(|item| item.kind == ty::AssociatedKind::Method)
1630+
.count() as u64
1631+
})
1632+
.sum();
1633+
tcx.mk_imm_ref(
1634+
tcx.types.re_static,
1635+
tcx.mk_array(tcx.types.usize, 3 + num_fns),
1636+
)
1637+
/* FIXME use actual fn pointers
1638+
tcx.mk_tup(&[
1639+
tcx.mk_array(tcx.types.usize, 3),
1640+
tcx.mk_array(Option<fn()>),
1641+
])
1642+
*/
16281643
}
16291644
_ => bug!("TyLayout::field_type({:?}): not applicable", this)
16301645
}

src/librustc_lint/builtin.rs

+68-7
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use lint::{LateContext, LintContext, LintArray};
4040
use lint::{LintPass, LateLintPass, EarlyLintPass, EarlyContext};
4141

4242
use std::collections::HashSet;
43+
use rustc::util::nodemap::FxHashSet;
4344

4445
use syntax::tokenstream::{TokenTree, TokenStream};
4546
use syntax::ast;
@@ -1576,20 +1577,77 @@ impl LintPass for UnusedBrokenConst {
15761577
}
15771578
}
15781579

1580+
fn validate_const<'a, 'tcx>(
1581+
tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
1582+
constant: &ty::Const<'tcx>,
1583+
param_env: ty::ParamEnv<'tcx>,
1584+
gid: ::rustc::mir::interpret::GlobalId<'tcx>,
1585+
what: &str,
1586+
) {
1587+
let mut ecx = ::rustc_mir::interpret::mk_eval_cx(tcx, gid.instance, param_env).unwrap();
1588+
let result = (|| {
1589+
let val = ecx.const_to_value(constant.val)?;
1590+
use rustc_target::abi::LayoutOf;
1591+
let layout = ecx.layout_of(constant.ty)?;
1592+
let place = ecx.allocate_place_for_value(val, layout, None)?;
1593+
let ptr = place.to_ptr()?;
1594+
let mut todo = vec![(ptr, layout.ty, String::new())];
1595+
let mut seen = FxHashSet();
1596+
seen.insert((ptr, layout.ty));
1597+
while let Some((ptr, ty, path)) = todo.pop() {
1598+
let layout = ecx.layout_of(ty)?;
1599+
ecx.validate_ptr_target(
1600+
ptr,
1601+
layout.align,
1602+
layout,
1603+
path,
1604+
&mut seen,
1605+
&mut todo,
1606+
)?;
1607+
}
1608+
Ok(())
1609+
})();
1610+
if let Err(err) = result {
1611+
let (trace, span) = ecx.generate_stacktrace(None);
1612+
let err = ::rustc::mir::interpret::ConstEvalErr {
1613+
error: err,
1614+
stacktrace: trace,
1615+
span,
1616+
};
1617+
let err = err.struct_error(
1618+
tcx.at(span),
1619+
&format!("this {} likely exhibits undefined behavior", what),
1620+
);
1621+
if let Some(mut err) = err {
1622+
err.note("The rules on what exactly is undefined behavior aren't clear, \
1623+
so this check might be overzealous. Please open an issue on the rust compiler \
1624+
repository if you believe it should not be considered undefined behavior",
1625+
);
1626+
err.emit();
1627+
}
1628+
}
1629+
}
1630+
15791631
fn check_const(cx: &LateContext, body_id: hir::BodyId, what: &str) {
15801632
let def_id = cx.tcx.hir.body_owner_def_id(body_id);
15811633
let param_env = cx.tcx.param_env(def_id);
15821634
let cid = ::rustc::mir::interpret::GlobalId {
15831635
instance: ty::Instance::mono(cx.tcx, def_id),
15841636
promoted: None
15851637
};
1586-
if let Err(err) = cx.tcx.const_eval(param_env.and(cid)) {
1587-
let span = cx.tcx.def_span(def_id);
1588-
err.report_as_lint(
1589-
cx.tcx.at(span),
1590-
&format!("this {} cannot be used", what),
1591-
cx.current_lint_root(),
1592-
);
1638+
match cx.tcx.const_eval(param_env.and(cid)) {
1639+
Ok(val) => validate_const(cx.tcx, val, param_env, cid, what),
1640+
Err(err) => {
1641+
// errors for statics are already reported directly in the query
1642+
if cx.tcx.is_static(def_id).is_none() {
1643+
let span = cx.tcx.def_span(def_id);
1644+
err.report_as_lint(
1645+
cx.tcx.at(span),
1646+
&format!("this {} cannot be used", what),
1647+
cx.current_lint_root(),
1648+
);
1649+
}
1650+
},
15931651
}
15941652
}
15951653

@@ -1610,6 +1668,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedBrokenConst {
16101668
hir::ItemKind::Const(_, body_id) => {
16111669
check_const(cx, body_id, "constant");
16121670
},
1671+
hir::ItemKind::Static(_, _, body_id) => {
1672+
check_const(cx, body_id, "static");
1673+
},
16131674
hir::ItemKind::Ty(ref ty, _) => hir::intravisit::walk_ty(
16141675
&mut UnusedBrokenConstVisitor(cx),
16151676
ty

src/librustc_mir/interpret/const_eval.rs

+12-24
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc::hir;
55
use rustc::mir::interpret::{ConstEvalErr};
66
use rustc::mir;
77
use rustc::ty::{self, TyCtxt, Ty, Instance};
8-
use rustc::ty::layout::{self, LayoutOf, Primitive};
8+
use rustc::ty::layout::{self, LayoutOf, Primitive, TyLayout};
99
use rustc::ty::subst::Subst;
1010

1111
use syntax::ast::Mutability;
@@ -16,7 +16,7 @@ use rustc::mir::interpret::{
1616
EvalResult, EvalError, EvalErrorKind, GlobalId,
1717
Value, Scalar, AllocId, Allocation, ConstValue,
1818
};
19-
use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra, Memory, MemoryKind};
19+
use super::{Place, EvalContext, StackPopCleanup, ValTy, Memory, MemoryKind};
2020

2121
pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
2222
tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -63,7 +63,7 @@ pub fn eval_promoted<'a, 'mir, 'tcx>(
6363
cid: GlobalId<'tcx>,
6464
mir: &'mir mir::Mir<'tcx>,
6565
param_env: ty::ParamEnv<'tcx>,
66-
) -> EvalResult<'tcx, (Value, Scalar, Ty<'tcx>)> {
66+
) -> EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)> {
6767
ecx.with_fresh_body(|ecx| {
6868
eval_body_using_ecx(ecx, cid, Some(mir), param_env)
6969
})
@@ -121,7 +121,7 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>(
121121
cid: GlobalId<'tcx>,
122122
mir: Option<&'mir mir::Mir<'tcx>>,
123123
param_env: ty::ParamEnv<'tcx>,
124-
) -> (EvalResult<'tcx, (Value, Scalar, Ty<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) {
124+
) -> (EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) {
125125
debug!("eval_body_and_ecx: {:?}, {:?}", cid, param_env);
126126
// we start out with the best span we have
127127
// and try improving it down the road when more information is available
@@ -137,7 +137,7 @@ fn eval_body_using_ecx<'a, 'mir, 'tcx>(
137137
cid: GlobalId<'tcx>,
138138
mir: Option<&'mir mir::Mir<'tcx>>,
139139
param_env: ty::ParamEnv<'tcx>,
140-
) -> EvalResult<'tcx, (Value, Scalar, Ty<'tcx>)> {
140+
) -> EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)> {
141141
debug!("eval_body: {:?}, {:?}", cid, param_env);
142142
let tcx = ecx.tcx.tcx;
143143
let mut mir = match mir {
@@ -182,7 +182,7 @@ fn eval_body_using_ecx<'a, 'mir, 'tcx>(
182182
// point at the allocation
183183
_ => Value::ByRef(ptr, layout.align),
184184
};
185-
Ok((value, ptr, layout.ty))
185+
Ok((value, ptr, layout))
186186
}
187187

188188
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
@@ -434,19 +434,7 @@ pub fn const_val_field<'a, 'tcx>(
434434
let ty = value.ty;
435435
let value = ecx.const_to_value(value.val)?;
436436
let layout = ecx.layout_of(ty)?;
437-
let (ptr, align) = match value {
438-
Value::ByRef(ptr, align) => (ptr, align),
439-
Value::ScalarPair(..) | Value::Scalar(_) => {
440-
let ptr = ecx.alloc_ptr(ty)?.into();
441-
ecx.write_value_to_ptr(value, ptr, layout.align, ty)?;
442-
(ptr, layout.align)
443-
},
444-
};
445-
let place = Place::Ptr {
446-
ptr,
447-
align,
448-
extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant),
449-
};
437+
let place = ecx.allocate_place_for_value(value, layout, variant)?;
450438
let (place, layout) = ecx.place_field(place, field, layout)?;
451439
let (ptr, align) = place.to_ptr_align();
452440
let mut new_value = Value::ByRef(ptr, align);
@@ -484,17 +472,17 @@ pub fn const_variant_index<'a, 'tcx>(
484472
trace!("const_variant_index: {:?}, {:?}", instance, val);
485473
let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
486474
let value = ecx.const_to_value(val.val)?;
475+
let layout = ecx.layout_of(val.ty)?;
487476
let (ptr, align) = match value {
488477
Value::ScalarPair(..) | Value::Scalar(_) => {
489-
let layout = ecx.layout_of(val.ty)?;
490478
let ptr = ecx.memory.allocate(layout.size, layout.align, MemoryKind::Stack)?.into();
491479
ecx.write_value_to_ptr(value, ptr, layout.align, val.ty)?;
492480
(ptr, layout.align)
493481
},
494482
Value::ByRef(ptr, align) => (ptr, align),
495483
};
496484
let place = Place::from_scalar_ptr(ptr, align);
497-
ecx.read_discriminant_as_variant_index(place, val.ty)
485+
ecx.read_discriminant_as_variant_index(place, layout)
498486
}
499487

500488
pub fn const_value_to_allocation_provider<'a, 'tcx>(
@@ -560,11 +548,11 @@ pub fn const_eval_provider<'a, 'tcx>(
560548
};
561549

562550
let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
563-
res.and_then(|(mut val, _, miri_ty)| {
551+
res.and_then(|(mut val, _, layout)| {
564552
if tcx.is_static(def_id).is_none() && cid.promoted.is_none() {
565-
val = ecx.try_read_by_ref(val, miri_ty)?;
553+
val = ecx.try_read_by_ref(val, layout.ty)?;
566554
}
567-
Ok(value_to_const_value(&ecx, val, miri_ty))
555+
Ok(value_to_const_value(&ecx, val, layout.ty))
568556
}).map_err(|err| {
569557
let (trace, span) = ecx.generate_stacktrace(None);
570558
let err = ConstEvalErr {

0 commit comments

Comments
 (0)