Skip to content

Commit 4abdab6

Browse files
committed
Auto merge of #51361 - oli-obk:sanity_check_consts, r=<try>
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 a8247dd + e1f5264 commit 4abdab6

34 files changed

+1041
-141
lines changed

src/librustc/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
#![feature(in_band_lifetimes)]
7575
#![feature(macro_at_most_once_rep)]
7676
#![feature(inclusive_range_methods)]
77+
#![feature(crate_in_paths)]
7778

7879
#![recursion_limit="512"]
7980

src/librustc/mir/interpret/error.rs

Lines changed: 1 addition & 1 deletion
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

Lines changed: 20 additions & 5 deletions
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

Lines changed: 68 additions & 7 deletions
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;
@@ -1575,20 +1576,77 @@ impl LintPass for UnusedBrokenConst {
15751576
}
15761577
}
15771578

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

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

src/librustc_mir/interpret/const_eval.rs

Lines changed: 12 additions & 24 deletions
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() {
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)