move validation to its own file
This commit is contained in:
parent
f2aeb5b893
commit
5099933614
@ -7,7 +7,7 @@ use rustc::hir::def::Def;
|
|||||||
use rustc::hir::map::definitions::DefPathData;
|
use rustc::hir::map::definitions::DefPathData;
|
||||||
use rustc::mir;
|
use rustc::mir;
|
||||||
use rustc::ty::layout::{
|
use rustc::ty::layout::{
|
||||||
self, Size, Align, HasDataLayout, LayoutOf, TyLayout, Primitive
|
self, Size, Align, HasDataLayout, LayoutOf, TyLayout
|
||||||
};
|
};
|
||||||
use rustc::ty::subst::{Subst, Substs};
|
use rustc::ty::subst::{Subst, Substs};
|
||||||
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
|
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||||
@ -15,7 +15,7 @@ use rustc::ty::query::TyCtxtAt;
|
|||||||
use rustc_data_structures::fx::{FxHashSet, FxHasher};
|
use rustc_data_structures::fx::{FxHashSet, FxHasher};
|
||||||
use rustc_data_structures::indexed_vec::IndexVec;
|
use rustc_data_structures::indexed_vec::IndexVec;
|
||||||
use rustc::mir::interpret::{
|
use rustc::mir::interpret::{
|
||||||
GlobalId, Scalar, FrameInfo, AllocType,
|
GlobalId, Scalar, FrameInfo,
|
||||||
EvalResult, EvalErrorKind,
|
EvalResult, EvalErrorKind,
|
||||||
ScalarMaybeUndef,
|
ScalarMaybeUndef,
|
||||||
truncate, sign_extend,
|
truncate, sign_extend,
|
||||||
@ -29,31 +29,6 @@ use super::{
|
|||||||
Memory, Machine
|
Memory, Machine
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! validation_failure{
|
|
||||||
($what:expr, $where:expr, $details:expr) => {{
|
|
||||||
let where_ = if $where.is_empty() {
|
|
||||||
String::new()
|
|
||||||
} else {
|
|
||||||
format!(" at {}", $where)
|
|
||||||
};
|
|
||||||
err!(ValidationFailure(format!(
|
|
||||||
"encountered {}{}, but expected {}",
|
|
||||||
$what, where_, $details,
|
|
||||||
)))
|
|
||||||
}};
|
|
||||||
($what:expr, $where:expr) => {{
|
|
||||||
let where_ = if $where.is_empty() {
|
|
||||||
String::new()
|
|
||||||
} else {
|
|
||||||
format!(" at {}", $where)
|
|
||||||
};
|
|
||||||
err!(ValidationFailure(format!(
|
|
||||||
"encountered {}{}",
|
|
||||||
$what, where_,
|
|
||||||
)))
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
|
pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
|
||||||
/// Stores the `Machine` instance.
|
/// Stores the `Machine` instance.
|
||||||
pub machine: M,
|
pub machine: M,
|
||||||
@ -670,243 +645,6 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
|
|||||||
self.tcx.const_eval(param_env.and(gid)).map_err(|err| EvalErrorKind::ReferencedConstant(err).into())
|
self.tcx.const_eval(param_env.and(gid)).map_err(|err| EvalErrorKind::ReferencedConstant(err).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_scalar(
|
|
||||||
&self,
|
|
||||||
value: ScalarMaybeUndef,
|
|
||||||
size: Size,
|
|
||||||
scalar: &layout::Scalar,
|
|
||||||
path: &str,
|
|
||||||
ty: Ty,
|
|
||||||
) -> EvalResult<'tcx> {
|
|
||||||
trace!("validate scalar: {:#?}, {:#?}, {:#?}, {}", value, size, scalar, ty);
|
|
||||||
let (lo, hi) = scalar.valid_range.clone().into_inner();
|
|
||||||
|
|
||||||
let value = match value {
|
|
||||||
ScalarMaybeUndef::Scalar(scalar) => scalar,
|
|
||||||
ScalarMaybeUndef::Undef => return validation_failure!("undefined bytes", path),
|
|
||||||
};
|
|
||||||
|
|
||||||
let bits = match value {
|
|
||||||
Scalar::Bits { bits, size: value_size } => {
|
|
||||||
assert_eq!(value_size as u64, size.bytes());
|
|
||||||
bits
|
|
||||||
},
|
|
||||||
Scalar::Ptr(_) => {
|
|
||||||
let ptr_size = self.memory.pointer_size();
|
|
||||||
let ptr_max = u128::max_value() >> (128 - ptr_size.bits());
|
|
||||||
return if lo > hi {
|
|
||||||
if lo - hi == 1 {
|
|
||||||
// no gap, all values are ok
|
|
||||||
Ok(())
|
|
||||||
} else if hi < ptr_max || lo > 1 {
|
|
||||||
let max = u128::max_value() >> (128 - size.bits());
|
|
||||||
validation_failure!(
|
|
||||||
"pointer",
|
|
||||||
path,
|
|
||||||
format!("something in the range {:?} or {:?}", 0..=lo, hi..=max)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
} else if hi < ptr_max || lo > 1 {
|
|
||||||
validation_failure!(
|
|
||||||
"pointer",
|
|
||||||
path,
|
|
||||||
format!("something in the range {:?}", scalar.valid_range)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// char gets a special treatment, because its number space is not contiguous so `TyLayout`
|
|
||||||
// has no special checks for chars
|
|
||||||
match ty.sty {
|
|
||||||
ty::TyChar => {
|
|
||||||
debug_assert_eq!(size.bytes(), 4);
|
|
||||||
if ::std::char::from_u32(bits as u32).is_none() {
|
|
||||||
return err!(InvalidChar(bits));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::ops::RangeInclusive;
|
|
||||||
let in_range = |bound: RangeInclusive<u128>| bound.contains(&bits);
|
|
||||||
if lo > hi {
|
|
||||||
if in_range(0..=hi) || in_range(lo..=u128::max_value()) {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
validation_failure!(
|
|
||||||
bits,
|
|
||||||
path,
|
|
||||||
format!("something in the range {:?} or {:?}", ..=hi, lo..)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if in_range(scalar.valid_range.clone()) {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
validation_failure!(
|
|
||||||
bits,
|
|
||||||
path,
|
|
||||||
format!("something in the range {:?}", scalar.valid_range)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function checks the memory where `ptr` points to.
|
|
||||||
/// It will error if the bits at the destination do not match the ones described by the layout.
|
|
||||||
pub fn validate_mplace(
|
|
||||||
&self,
|
|
||||||
dest: MPlaceTy<'tcx>,
|
|
||||||
path: String,
|
|
||||||
seen: &mut FxHashSet<(MPlaceTy<'tcx>)>,
|
|
||||||
todo: &mut Vec<(MPlaceTy<'tcx>, String)>,
|
|
||||||
) -> EvalResult<'tcx> {
|
|
||||||
self.memory.dump_alloc(dest.to_ptr()?.alloc_id);
|
|
||||||
trace!("validate_mplace: {:?}, {:#?}", *dest, dest.layout);
|
|
||||||
|
|
||||||
// Find the right variant
|
|
||||||
let (variant, dest) = match dest.layout.variants {
|
|
||||||
layout::Variants::NicheFilling { niche: ref tag, .. } |
|
|
||||||
layout::Variants::Tagged { ref tag, .. } => {
|
|
||||||
let size = tag.value.size(self);
|
|
||||||
// we first read the tag value as scalar, to be able to validate it
|
|
||||||
let tag_mplace = self.mplace_field(dest, 0)?;
|
|
||||||
let tag_value = self.read_scalar(tag_mplace.into())?;
|
|
||||||
let path = format!("{}.TAG", path);
|
|
||||||
self.validate_scalar(
|
|
||||||
tag_value, size, tag, &path, tag_mplace.layout.ty
|
|
||||||
)?;
|
|
||||||
// then we read it again to get the index, to continue
|
|
||||||
let variant = self.read_discriminant_as_variant_index(dest.into())?;
|
|
||||||
let dest = self.mplace_downcast(dest, variant)?;
|
|
||||||
trace!("variant layout: {:#?}", dest.layout);
|
|
||||||
(variant, dest)
|
|
||||||
},
|
|
||||||
layout::Variants::Single { index } => {
|
|
||||||
(index, dest)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Validate all fields
|
|
||||||
match dest.layout.fields {
|
|
||||||
// primitives are unions with zero fields
|
|
||||||
layout::FieldPlacement::Union(0) => {
|
|
||||||
match dest.layout.abi {
|
|
||||||
// nothing to do, whatever the pointer points to, it is never going to be read
|
|
||||||
layout::Abi::Uninhabited => validation_failure!("a value of an uninhabited type", path),
|
|
||||||
// check that the scalar is a valid pointer or that its bit range matches the
|
|
||||||
// expectation.
|
|
||||||
layout::Abi::Scalar(ref scalar_layout) => {
|
|
||||||
let size = scalar_layout.value.size(self);
|
|
||||||
let value = self.read_value(dest.into())?;
|
|
||||||
let scalar = value.to_scalar_or_undef();
|
|
||||||
self.validate_scalar(scalar, size, scalar_layout, &path, dest.layout.ty)?;
|
|
||||||
if scalar_layout.value == Primitive::Pointer {
|
|
||||||
// ignore integer pointers, we can't reason about the final hardware
|
|
||||||
if let Scalar::Ptr(ptr) = scalar.not_undef()? {
|
|
||||||
let alloc_kind = self.tcx.alloc_map.lock().get(ptr.alloc_id);
|
|
||||||
if let Some(AllocType::Static(did)) = alloc_kind {
|
|
||||||
// statics from other crates are already checked
|
|
||||||
// extern statics should not be validated as they have no body
|
|
||||||
if !did.is_local() || self.tcx.is_foreign_item(did) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if value.layout.ty.builtin_deref(false).is_some() {
|
|
||||||
trace!("Recursing below ptr {:#?}", value);
|
|
||||||
let ptr_place = self.ref_to_mplace(value)?;
|
|
||||||
// we have not encountered this pointer+layout combination before
|
|
||||||
if seen.insert(ptr_place) {
|
|
||||||
todo.push((ptr_place, format!("(*{})", path)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
_ => bug!("bad abi for FieldPlacement::Union(0): {:#?}", dest.layout.abi),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
layout::FieldPlacement::Union(_) => {
|
|
||||||
// We can't check unions, their bits are allowed to be anything.
|
|
||||||
// The fields don't need to correspond to any bit pattern of the union's fields.
|
|
||||||
// See https://github.com/rust-lang/rust/issues/32836#issuecomment-406875389
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
layout::FieldPlacement::Array { count, .. } => {
|
|
||||||
for i in 0..count {
|
|
||||||
let mut path = path.clone();
|
|
||||||
self.dump_field_name(&mut path, dest.layout.ty, i as usize, variant).unwrap();
|
|
||||||
let field = self.mplace_field(dest, i)?;
|
|
||||||
self.validate_mplace(field, path, seen, todo)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
layout::FieldPlacement::Arbitrary { ref offsets, .. } => {
|
|
||||||
// fat pointers need special treatment
|
|
||||||
match dest.layout.ty.builtin_deref(false).map(|tam| &tam.ty.sty) {
|
|
||||||
| Some(ty::TyStr)
|
|
||||||
| Some(ty::TySlice(_)) => {
|
|
||||||
// check the length (for nicer error messages)
|
|
||||||
let len_mplace = self.mplace_field(dest, 1)?;
|
|
||||||
let len = self.read_scalar(len_mplace.into())?;
|
|
||||||
let len = match len.to_bits(len_mplace.layout.size) {
|
|
||||||
Err(_) => return validation_failure!("length is not a valid integer", path),
|
|
||||||
Ok(len) => len as u64,
|
|
||||||
};
|
|
||||||
// get the fat ptr, and recursively check it
|
|
||||||
let ptr = self.ref_to_mplace(self.read_value(dest.into())?)?;
|
|
||||||
assert_eq!(ptr.extra, PlaceExtra::Length(len));
|
|
||||||
let unpacked_ptr = self.unpack_unsized_mplace(ptr)?;
|
|
||||||
if seen.insert(unpacked_ptr) {
|
|
||||||
let mut path = path.clone();
|
|
||||||
self.dump_field_name(&mut path, dest.layout.ty, 0, 0).unwrap();
|
|
||||||
todo.push((unpacked_ptr, path))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Some(ty::TyDynamic(..)) => {
|
|
||||||
// check the vtable (for nicer error messages)
|
|
||||||
let vtable = self.read_scalar(self.mplace_field(dest, 1)?.into())?;
|
|
||||||
let vtable = match vtable.to_ptr() {
|
|
||||||
Err(_) => return validation_failure!("vtable address is not a pointer", path),
|
|
||||||
Ok(vtable) => vtable,
|
|
||||||
};
|
|
||||||
// get the fat ptr, and recursively check it
|
|
||||||
let ptr = self.ref_to_mplace(self.read_value(dest.into())?)?;
|
|
||||||
assert_eq!(ptr.extra, PlaceExtra::Vtable(vtable));
|
|
||||||
let unpacked_ptr = self.unpack_unsized_mplace(ptr)?;
|
|
||||||
if seen.insert(unpacked_ptr) {
|
|
||||||
let mut path = path.clone();
|
|
||||||
self.dump_field_name(&mut path, dest.layout.ty, 0, 0).unwrap();
|
|
||||||
todo.push((unpacked_ptr, path))
|
|
||||||
}
|
|
||||||
// FIXME: More checks for the vtable... making sure it is exactly
|
|
||||||
// the one one would expect for this type.
|
|
||||||
},
|
|
||||||
Some(ty) =>
|
|
||||||
bug!("Unexpected fat pointer target type {:?}", ty),
|
|
||||||
None => {
|
|
||||||
// Not a pointer, perform regular aggregate handling below
|
|
||||||
for i in 0..offsets.len() {
|
|
||||||
let mut path = path.clone();
|
|
||||||
self.dump_field_name(&mut path, dest.layout.ty, i, variant).unwrap();
|
|
||||||
let field = self.mplace_field(dest, i as u64)?;
|
|
||||||
self.validate_mplace(field, path, seen, todo)?;
|
|
||||||
}
|
|
||||||
// FIXME: For a TyStr, check that this is valid UTF-8.
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn frame(&self) -> &Frame<'mir, 'tcx> {
|
pub fn frame(&self) -> &Frame<'mir, 'tcx> {
|
||||||
self.stack.last().expect("no call frames exist")
|
self.stack.last().expect("no call frames exist")
|
||||||
@ -1041,72 +779,5 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
|
|||||||
pub fn truncate(&self, value: u128, ty: TyLayout<'_>) -> u128 {
|
pub fn truncate(&self, value: u128, ty: TyLayout<'_>) -> u128 {
|
||||||
truncate(value, ty.size)
|
truncate(value, ty.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_field_name(&self, s: &mut String, ty: Ty<'tcx>, i: usize, variant: usize) -> ::std::fmt::Result {
|
|
||||||
match ty.sty {
|
|
||||||
ty::TyBool |
|
|
||||||
ty::TyChar |
|
|
||||||
ty::TyInt(_) |
|
|
||||||
ty::TyUint(_) |
|
|
||||||
ty::TyFloat(_) |
|
|
||||||
ty::TyFnPtr(_) |
|
|
||||||
ty::TyNever |
|
|
||||||
ty::TyFnDef(..) |
|
|
||||||
ty::TyGeneratorWitness(..) |
|
|
||||||
ty::TyForeign(..) |
|
|
||||||
ty::TyDynamic(..) => {
|
|
||||||
bug!("field_name({:?}): not applicable", ty)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Potentially-fat pointers.
|
|
||||||
ty::TyRef(_, pointee, _) |
|
|
||||||
ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
|
|
||||||
assert!(i < 2);
|
|
||||||
|
|
||||||
// Reuse the fat *T type as its own thin pointer data field.
|
|
||||||
// This provides information about e.g. DST struct pointees
|
|
||||||
// (which may have no non-DST form), and will work as long
|
|
||||||
// as the `Abi` or `FieldPlacement` is checked by users.
|
|
||||||
if i == 0 {
|
|
||||||
return write!(s, ".data_ptr");
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.tcx.struct_tail(pointee).sty {
|
|
||||||
ty::TySlice(_) |
|
|
||||||
ty::TyStr => write!(s, ".len"),
|
|
||||||
ty::TyDynamic(..) => write!(s, ".vtable_ptr"),
|
|
||||||
_ => bug!("field_name({:?}): not applicable", ty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arrays and slices.
|
|
||||||
ty::TyArray(_, _) |
|
|
||||||
ty::TySlice(_) |
|
|
||||||
ty::TyStr => write!(s, "[{}]", i),
|
|
||||||
|
|
||||||
// generators and closures.
|
|
||||||
ty::TyClosure(def_id, _) | ty::TyGenerator(def_id, _, _) => {
|
|
||||||
let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
|
|
||||||
let freevar = self.tcx.with_freevars(node_id, |fv| fv[i]);
|
|
||||||
write!(s, ".upvar({})", self.tcx.hir.name(freevar.var_id()))
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::TyTuple(_) => write!(s, ".{}", i),
|
|
||||||
|
|
||||||
// enums
|
|
||||||
ty::TyAdt(def, ..) if def.is_enum() => {
|
|
||||||
let variant = &def.variants[variant];
|
|
||||||
write!(s, ".{}::{}", variant.name, variant.fields[i].ident)
|
|
||||||
}
|
|
||||||
|
|
||||||
// other ADTs.
|
|
||||||
ty::TyAdt(def, _) => write!(s, ".{}", def.non_enum_variant().fields[i].ident),
|
|
||||||
|
|
||||||
ty::TyProjection(_) | ty::TyAnon(..) | ty::TyParam(_) |
|
|
||||||
ty::TyInfer(_) | ty::TyError => {
|
|
||||||
bug!("dump_field_name: unexpected type `{}`", ty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ mod step;
|
|||||||
mod terminator;
|
mod terminator;
|
||||||
mod traits;
|
mod traits;
|
||||||
mod const_eval;
|
mod const_eval;
|
||||||
|
mod validity;
|
||||||
|
|
||||||
pub use self::eval_context::{
|
pub use self::eval_context::{
|
||||||
EvalContext, Frame, StackPopCleanup, LocalValue,
|
EvalContext, Frame, StackPopCleanup, LocalValue,
|
||||||
|
343
src/librustc_mir/interpret/validity.rs
Normal file
343
src/librustc_mir/interpret/validity.rs
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use rustc::ty::layout::{self, Size, Primitive};
|
||||||
|
use rustc::ty::{self, Ty};
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
|
use rustc::mir::interpret::{
|
||||||
|
Scalar, AllocType, EvalResult, ScalarMaybeUndef,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
MPlaceTy, PlaceExtra, Machine, EvalContext
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! validation_failure{
|
||||||
|
($what:expr, $where:expr, $details:expr) => {{
|
||||||
|
let where_ = if $where.is_empty() {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
format!(" at {}", $where)
|
||||||
|
};
|
||||||
|
err!(ValidationFailure(format!(
|
||||||
|
"encountered {}{}, but expected {}",
|
||||||
|
$what, where_, $details,
|
||||||
|
)))
|
||||||
|
}};
|
||||||
|
($what:expr, $where:expr) => {{
|
||||||
|
let where_ = if $where.is_empty() {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
format!(" at {}", $where)
|
||||||
|
};
|
||||||
|
err!(ValidationFailure(format!(
|
||||||
|
"encountered {}{}",
|
||||||
|
$what, where_,
|
||||||
|
)))
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||||
|
fn validate_scalar(
|
||||||
|
&self,
|
||||||
|
value: ScalarMaybeUndef,
|
||||||
|
size: Size,
|
||||||
|
scalar: &layout::Scalar,
|
||||||
|
path: &str,
|
||||||
|
ty: Ty,
|
||||||
|
) -> EvalResult<'tcx> {
|
||||||
|
trace!("validate scalar: {:#?}, {:#?}, {:#?}, {}", value, size, scalar, ty);
|
||||||
|
let (lo, hi) = scalar.valid_range.clone().into_inner();
|
||||||
|
|
||||||
|
let value = match value {
|
||||||
|
ScalarMaybeUndef::Scalar(scalar) => scalar,
|
||||||
|
ScalarMaybeUndef::Undef => return validation_failure!("undefined bytes", path),
|
||||||
|
};
|
||||||
|
|
||||||
|
let bits = match value {
|
||||||
|
Scalar::Bits { bits, size: value_size } => {
|
||||||
|
assert_eq!(value_size as u64, size.bytes());
|
||||||
|
bits
|
||||||
|
},
|
||||||
|
Scalar::Ptr(_) => {
|
||||||
|
let ptr_size = self.memory.pointer_size();
|
||||||
|
let ptr_max = u128::max_value() >> (128 - ptr_size.bits());
|
||||||
|
return if lo > hi {
|
||||||
|
if lo - hi == 1 {
|
||||||
|
// no gap, all values are ok
|
||||||
|
Ok(())
|
||||||
|
} else if hi < ptr_max || lo > 1 {
|
||||||
|
let max = u128::max_value() >> (128 - size.bits());
|
||||||
|
validation_failure!(
|
||||||
|
"pointer",
|
||||||
|
path,
|
||||||
|
format!("something in the range {:?} or {:?}", 0..=lo, hi..=max)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
} else if hi < ptr_max || lo > 1 {
|
||||||
|
validation_failure!(
|
||||||
|
"pointer",
|
||||||
|
path,
|
||||||
|
format!("something in the range {:?}", scalar.valid_range)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// char gets a special treatment, because its number space is not contiguous so `TyLayout`
|
||||||
|
// has no special checks for chars
|
||||||
|
match ty.sty {
|
||||||
|
ty::TyChar => {
|
||||||
|
debug_assert_eq!(size.bytes(), 4);
|
||||||
|
if ::std::char::from_u32(bits as u32).is_none() {
|
||||||
|
return err!(InvalidChar(bits));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::ops::RangeInclusive;
|
||||||
|
let in_range = |bound: RangeInclusive<u128>| bound.contains(&bits);
|
||||||
|
if lo > hi {
|
||||||
|
if in_range(0..=hi) || in_range(lo..=u128::max_value()) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
validation_failure!(
|
||||||
|
bits,
|
||||||
|
path,
|
||||||
|
format!("something in the range {:?} or {:?}", ..=hi, lo..)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if in_range(scalar.valid_range.clone()) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
validation_failure!(
|
||||||
|
bits,
|
||||||
|
path,
|
||||||
|
format!("something in the range {:?}", scalar.valid_range)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function checks the memory where `ptr` points to.
|
||||||
|
/// It will error if the bits at the destination do not match the ones described by the layout.
|
||||||
|
pub fn validate_mplace(
|
||||||
|
&self,
|
||||||
|
dest: MPlaceTy<'tcx>,
|
||||||
|
path: String,
|
||||||
|
seen: &mut FxHashSet<(MPlaceTy<'tcx>)>,
|
||||||
|
todo: &mut Vec<(MPlaceTy<'tcx>, String)>,
|
||||||
|
) -> EvalResult<'tcx> {
|
||||||
|
self.memory.dump_alloc(dest.to_ptr()?.alloc_id);
|
||||||
|
trace!("validate_mplace: {:?}, {:#?}", *dest, dest.layout);
|
||||||
|
|
||||||
|
// Find the right variant
|
||||||
|
let (variant, dest) = match dest.layout.variants {
|
||||||
|
layout::Variants::NicheFilling { niche: ref tag, .. } |
|
||||||
|
layout::Variants::Tagged { ref tag, .. } => {
|
||||||
|
let size = tag.value.size(self);
|
||||||
|
// we first read the tag value as scalar, to be able to validate it
|
||||||
|
let tag_mplace = self.mplace_field(dest, 0)?;
|
||||||
|
let tag_value = self.read_scalar(tag_mplace.into())?;
|
||||||
|
let path = format!("{}.TAG", path);
|
||||||
|
self.validate_scalar(
|
||||||
|
tag_value, size, tag, &path, tag_mplace.layout.ty
|
||||||
|
)?;
|
||||||
|
// then we read it again to get the index, to continue
|
||||||
|
let variant = self.read_discriminant_as_variant_index(dest.into())?;
|
||||||
|
let dest = self.mplace_downcast(dest, variant)?;
|
||||||
|
trace!("variant layout: {:#?}", dest.layout);
|
||||||
|
(variant, dest)
|
||||||
|
},
|
||||||
|
layout::Variants::Single { index } => {
|
||||||
|
(index, dest)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validate all fields
|
||||||
|
match dest.layout.fields {
|
||||||
|
// primitives are unions with zero fields
|
||||||
|
layout::FieldPlacement::Union(0) => {
|
||||||
|
match dest.layout.abi {
|
||||||
|
// nothing to do, whatever the pointer points to, it is never going to be read
|
||||||
|
layout::Abi::Uninhabited => validation_failure!("a value of an uninhabited type", path),
|
||||||
|
// check that the scalar is a valid pointer or that its bit range matches the
|
||||||
|
// expectation.
|
||||||
|
layout::Abi::Scalar(ref scalar_layout) => {
|
||||||
|
let size = scalar_layout.value.size(self);
|
||||||
|
let value = self.read_value(dest.into())?;
|
||||||
|
let scalar = value.to_scalar_or_undef();
|
||||||
|
self.validate_scalar(scalar, size, scalar_layout, &path, dest.layout.ty)?;
|
||||||
|
if scalar_layout.value == Primitive::Pointer {
|
||||||
|
// ignore integer pointers, we can't reason about the final hardware
|
||||||
|
if let Scalar::Ptr(ptr) = scalar.not_undef()? {
|
||||||
|
let alloc_kind = self.tcx.alloc_map.lock().get(ptr.alloc_id);
|
||||||
|
if let Some(AllocType::Static(did)) = alloc_kind {
|
||||||
|
// statics from other crates are already checked
|
||||||
|
// extern statics should not be validated as they have no body
|
||||||
|
if !did.is_local() || self.tcx.is_foreign_item(did) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if value.layout.ty.builtin_deref(false).is_some() {
|
||||||
|
trace!("Recursing below ptr {:#?}", value);
|
||||||
|
let ptr_place = self.ref_to_mplace(value)?;
|
||||||
|
// we have not encountered this pointer+layout combination before
|
||||||
|
if seen.insert(ptr_place) {
|
||||||
|
todo.push((ptr_place, format!("(*{})", path)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
_ => bug!("bad abi for FieldPlacement::Union(0): {:#?}", dest.layout.abi),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
layout::FieldPlacement::Union(_) => {
|
||||||
|
// We can't check unions, their bits are allowed to be anything.
|
||||||
|
// The fields don't need to correspond to any bit pattern of the union's fields.
|
||||||
|
// See https://github.com/rust-lang/rust/issues/32836#issuecomment-406875389
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
layout::FieldPlacement::Array { count, .. } => {
|
||||||
|
for i in 0..count {
|
||||||
|
let mut path = path.clone();
|
||||||
|
self.dump_field_name(&mut path, dest.layout.ty, i as usize, variant).unwrap();
|
||||||
|
let field = self.mplace_field(dest, i)?;
|
||||||
|
self.validate_mplace(field, path, seen, todo)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
layout::FieldPlacement::Arbitrary { ref offsets, .. } => {
|
||||||
|
// fat pointers need special treatment
|
||||||
|
match dest.layout.ty.builtin_deref(false).map(|tam| &tam.ty.sty) {
|
||||||
|
| Some(ty::TyStr)
|
||||||
|
| Some(ty::TySlice(_)) => {
|
||||||
|
// check the length (for nicer error messages)
|
||||||
|
let len_mplace = self.mplace_field(dest, 1)?;
|
||||||
|
let len = self.read_scalar(len_mplace.into())?;
|
||||||
|
let len = match len.to_bits(len_mplace.layout.size) {
|
||||||
|
Err(_) => return validation_failure!("length is not a valid integer", path),
|
||||||
|
Ok(len) => len as u64,
|
||||||
|
};
|
||||||
|
// get the fat ptr, and recursively check it
|
||||||
|
let ptr = self.ref_to_mplace(self.read_value(dest.into())?)?;
|
||||||
|
assert_eq!(ptr.extra, PlaceExtra::Length(len));
|
||||||
|
let unpacked_ptr = self.unpack_unsized_mplace(ptr)?;
|
||||||
|
if seen.insert(unpacked_ptr) {
|
||||||
|
let mut path = path.clone();
|
||||||
|
self.dump_field_name(&mut path, dest.layout.ty, 0, 0).unwrap();
|
||||||
|
todo.push((unpacked_ptr, path))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(ty::TyDynamic(..)) => {
|
||||||
|
// check the vtable (for nicer error messages)
|
||||||
|
let vtable = self.read_scalar(self.mplace_field(dest, 1)?.into())?;
|
||||||
|
let vtable = match vtable.to_ptr() {
|
||||||
|
Err(_) => return validation_failure!("vtable address is not a pointer", path),
|
||||||
|
Ok(vtable) => vtable,
|
||||||
|
};
|
||||||
|
// get the fat ptr, and recursively check it
|
||||||
|
let ptr = self.ref_to_mplace(self.read_value(dest.into())?)?;
|
||||||
|
assert_eq!(ptr.extra, PlaceExtra::Vtable(vtable));
|
||||||
|
let unpacked_ptr = self.unpack_unsized_mplace(ptr)?;
|
||||||
|
if seen.insert(unpacked_ptr) {
|
||||||
|
let mut path = path.clone();
|
||||||
|
self.dump_field_name(&mut path, dest.layout.ty, 0, 0).unwrap();
|
||||||
|
todo.push((unpacked_ptr, path))
|
||||||
|
}
|
||||||
|
// FIXME: More checks for the vtable... making sure it is exactly
|
||||||
|
// the one one would expect for this type.
|
||||||
|
},
|
||||||
|
Some(ty) =>
|
||||||
|
bug!("Unexpected fat pointer target type {:?}", ty),
|
||||||
|
None => {
|
||||||
|
// Not a pointer, perform regular aggregate handling below
|
||||||
|
for i in 0..offsets.len() {
|
||||||
|
let mut path = path.clone();
|
||||||
|
self.dump_field_name(&mut path, dest.layout.ty, i, variant).unwrap();
|
||||||
|
let field = self.mplace_field(dest, i as u64)?;
|
||||||
|
self.validate_mplace(field, path, seen, todo)?;
|
||||||
|
}
|
||||||
|
// FIXME: For a TyStr, check that this is valid UTF-8.
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_field_name(&self, s: &mut String, ty: Ty<'tcx>, i: usize, variant: usize) -> ::std::fmt::Result {
|
||||||
|
match ty.sty {
|
||||||
|
ty::TyBool |
|
||||||
|
ty::TyChar |
|
||||||
|
ty::TyInt(_) |
|
||||||
|
ty::TyUint(_) |
|
||||||
|
ty::TyFloat(_) |
|
||||||
|
ty::TyFnPtr(_) |
|
||||||
|
ty::TyNever |
|
||||||
|
ty::TyFnDef(..) |
|
||||||
|
ty::TyGeneratorWitness(..) |
|
||||||
|
ty::TyForeign(..) |
|
||||||
|
ty::TyDynamic(..) => {
|
||||||
|
bug!("field_name({:?}): not applicable", ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Potentially-fat pointers.
|
||||||
|
ty::TyRef(_, pointee, _) |
|
||||||
|
ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
|
||||||
|
assert!(i < 2);
|
||||||
|
|
||||||
|
// Reuse the fat *T type as its own thin pointer data field.
|
||||||
|
// This provides information about e.g. DST struct pointees
|
||||||
|
// (which may have no non-DST form), and will work as long
|
||||||
|
// as the `Abi` or `FieldPlacement` is checked by users.
|
||||||
|
if i == 0 {
|
||||||
|
return write!(s, ".data_ptr");
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.tcx.struct_tail(pointee).sty {
|
||||||
|
ty::TySlice(_) |
|
||||||
|
ty::TyStr => write!(s, ".len"),
|
||||||
|
ty::TyDynamic(..) => write!(s, ".vtable_ptr"),
|
||||||
|
_ => bug!("field_name({:?}): not applicable", ty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrays and slices.
|
||||||
|
ty::TyArray(_, _) |
|
||||||
|
ty::TySlice(_) |
|
||||||
|
ty::TyStr => write!(s, "[{}]", i),
|
||||||
|
|
||||||
|
// generators and closures.
|
||||||
|
ty::TyClosure(def_id, _) | ty::TyGenerator(def_id, _, _) => {
|
||||||
|
let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
|
||||||
|
let freevar = self.tcx.with_freevars(node_id, |fv| fv[i]);
|
||||||
|
write!(s, ".upvar({})", self.tcx.hir.name(freevar.var_id()))
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::TyTuple(_) => write!(s, ".{}", i),
|
||||||
|
|
||||||
|
// enums
|
||||||
|
ty::TyAdt(def, ..) if def.is_enum() => {
|
||||||
|
let variant = &def.variants[variant];
|
||||||
|
write!(s, ".{}::{}", variant.name, variant.fields[i].ident)
|
||||||
|
}
|
||||||
|
|
||||||
|
// other ADTs.
|
||||||
|
ty::TyAdt(def, _) => write!(s, ".{}", def.non_enum_variant().fields[i].ident),
|
||||||
|
|
||||||
|
ty::TyProjection(_) | ty::TyAnon(..) | ty::TyParam(_) |
|
||||||
|
ty::TyInfer(_) | ty::TyError => {
|
||||||
|
bug!("dump_field_name: unexpected type `{}`", ty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user