3 use std::ops::RangeInclusive;
5 use syntax_pos::symbol::Symbol;
7 use rustc::ty::layout::{self, Size, Align, TyLayout, LayoutOf, VariantIdx};
9 use rustc_data_structures::fx::FxHashSet;
10 use rustc::mir::interpret::{
11 Scalar, AllocKind, EvalResult, InterpError,
15 OpTy, Machine, InterpretCx, ValueVisitor, MPlaceTy,
18 macro_rules! validation_failure {
19 ($what:expr, $where:expr, $details:expr) => {{
20 let where_ = path_format(&$where);
21 let where_ = if where_.is_empty() {
24 format!(" at {}", where_)
26 err!(ValidationFailure(format!(
27 "encountered {}{}, but expected {}",
28 $what, where_, $details,
31 ($what:expr, $where:expr) => {{
32 let where_ = path_format(&$where);
33 let where_ = if where_.is_empty() {
36 format!(" at {}", where_)
38 err!(ValidationFailure(format!(
45 macro_rules! try_validation {
46 ($e:expr, $what:expr, $where:expr, $details:expr) => {{
49 Err(_) => return validation_failure!($what, $where, $details),
53 ($e:expr, $what:expr, $where:expr) => {{
56 Err(_) => return validation_failure!($what, $where),
61 /// We want to show a nice path to the invalid field for diagnostics,
62 /// but avoid string operations in the happy case where no error happens.
63 /// So we track a `Vec<PathElem>` where `PathElem` contains all the data we
64 /// need to later print something for the user.
65 #[derive(Copy, Clone, Debug)]
77 /// State for tracking recursive validation of references
78 pub struct RefTracking<T> {
79 pub seen: FxHashSet<T>,
80 pub todo: Vec<(T, Vec<PathElem>)>,
83 impl<'tcx, T: Copy + Eq + Hash> RefTracking<T> {
84 pub fn new(op: T) -> Self {
85 let mut ref_tracking = RefTracking {
86 seen: FxHashSet::default(),
87 todo: vec![(op, Vec::new())],
89 ref_tracking.seen.insert(op);
95 fn path_format(path: &Vec<PathElem>) -> String {
96 use self::PathElem::*;
98 let mut out = String::new();
99 for elem in path.iter() {
101 Field(name) => write!(out, ".{}", name),
102 Variant(name) => write!(out, ".<downcast-variant({})>", name),
103 ClosureVar(name) => write!(out, ".<closure-var({})>", name),
104 TupleElem(idx) => write!(out, ".{}", idx),
105 ArrayElem(idx) => write!(out, "[{}]", idx),
107 // This does not match Rust syntax, but it is more readable for long paths -- and
108 // some of the other items here also are not Rust syntax. Actually we can't
109 // even use the usual syntax because we are just showing the projections,
111 write!(out, ".<deref>"),
112 Tag => write!(out, ".<enum-tag>"),
113 DynDowncast => write!(out, ".<dyn-downcast>"),
119 // Test if a range that wraps at overflow contains `test`
120 fn wrapping_range_contains(r: &RangeInclusive<u128>, test: u128) -> bool {
121 let (lo, hi) = r.clone().into_inner();
124 (..=hi).contains(&test) || (lo..).contains(&test)
131 // Formats such that a sentence like "expected something {}" to mean
132 // "expected something <in the given range>" makes sense.
133 fn wrapping_range_format(r: &RangeInclusive<u128>, max_hi: u128) -> String {
134 let (lo, hi) = r.clone().into_inner();
135 debug_assert!(hi <= max_hi);
137 format!("less or equal to {}, or greater or equal to {}", hi, lo)
140 debug_assert!(hi < max_hi, "should not be printing if the range covers everything");
141 format!("less or equal to {}", hi)
142 } else if hi == max_hi {
143 format!("greater or equal to {}", lo)
145 format!("in the range {:?}", r)
150 struct ValidityVisitor<'rt, 'a: 'rt, 'mir: 'rt, 'tcx: 'a+'rt+'mir, M: Machine<'a, 'mir, 'tcx>+'rt> {
151 /// The `path` may be pushed to, but the part that is present when a function
152 /// starts must not be changed! `visit_fields` and `visit_array` rely on
153 /// this stack discipline.
155 ref_tracking: Option<&'rt mut RefTracking<MPlaceTy<'tcx, M::PointerTag>>>,
157 ecx: &'rt InterpretCx<'a, 'mir, 'tcx, M>,
160 impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> ValidityVisitor<'rt, 'a, 'mir, 'tcx, M> {
161 fn aggregate_field_path_elem(
163 layout: TyLayout<'tcx>,
166 match layout.ty.sty {
167 // generators and closures.
168 ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
170 if def_id.is_local() {
171 let tables = self.ecx.tcx.typeck_tables_of(def_id);
172 if let Some(upvars) = tables.upvar_list.get(&def_id) {
173 // Sometimes the index is beyond the number of freevars (seen
175 if let Some(upvar_id) = upvars.get(field) {
176 let var_hir_id = upvar_id.var_path.hir_id;
177 let var_node_id = self.ecx.tcx.hir().hir_to_node_id(var_hir_id);
178 if let hir::Node::Binding(pat) = self.ecx.tcx.hir().get(var_node_id) {
179 if let hir::PatKind::Binding(_, _, ident, _) = pat.node {
180 name = Some(ident.name);
187 PathElem::ClosureVar(name.unwrap_or_else(|| {
188 // Fall back to showing the field index.
189 Symbol::intern(&field.to_string())
194 ty::Tuple(_) => PathElem::TupleElem(field),
197 ty::Adt(def, ..) if def.is_enum() => {
198 // we might be projecting *to* a variant, or to a field *in*a variant.
199 match layout.variants {
200 layout::Variants::Single { index } =>
202 PathElem::Field(def.variants[index].fields[field].ident.name),
208 ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name),
211 ty::Array(..) | ty::Slice(..) => PathElem::ArrayElem(field),
214 ty::Dynamic(..) => PathElem::DynDowncast,
216 // nothing else has an aggregate layout
217 _ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", layout.ty),
223 new_op: OpTy<'tcx, M::PointerTag>,
225 ) -> EvalResult<'tcx> {
226 // Remember the old state
227 let path_len = self.path.len();
229 self.path.push(elem);
230 self.visit_value(new_op)?;
232 self.path.truncate(path_len);
237 impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
238 ValueVisitor<'a, 'mir, 'tcx, M> for ValidityVisitor<'rt, 'a, 'mir, 'tcx, M>
240 type V = OpTy<'tcx, M::PointerTag>;
243 fn ecx(&self) -> &InterpretCx<'a, 'mir, 'tcx, M> {
250 old_op: OpTy<'tcx, M::PointerTag>,
252 new_op: OpTy<'tcx, M::PointerTag>
253 ) -> EvalResult<'tcx> {
254 let elem = self.aggregate_field_path_elem(old_op.layout, field);
255 self.visit_elem(new_op, elem)
261 old_op: OpTy<'tcx, M::PointerTag>,
262 variant_id: VariantIdx,
263 new_op: OpTy<'tcx, M::PointerTag>
264 ) -> EvalResult<'tcx> {
265 let name = old_op.layout.ty.ty_adt_def().unwrap().variants[variant_id].ident.name;
266 self.visit_elem(new_op, PathElem::Variant(name))
270 fn visit_value(&mut self, op: OpTy<'tcx, M::PointerTag>) -> EvalResult<'tcx>
272 trace!("visit_value: {:?}, {:?}", *op, op.layout);
273 // Translate some possible errors to something nicer.
274 match self.walk_value(op) {
276 Err(err) => match err.kind {
277 InterpError::InvalidDiscriminant(val) =>
279 val, self.path, "a valid enum discriminant"
281 InterpError::ReadPointerAsBytes =>
283 "a pointer", self.path, "plain (non-pointer) bytes"
290 fn visit_primitive(&mut self, value: OpTy<'tcx, M::PointerTag>) -> EvalResult<'tcx>
292 let value = self.ecx.read_immediate(value)?;
293 // Go over all the primitive types
294 let ty = value.layout.ty;
297 let value = value.to_scalar_or_undef();
298 try_validation!(value.to_bool(),
299 value, self.path, "a boolean");
302 let value = value.to_scalar_or_undef();
303 try_validation!(value.to_char(),
304 value, self.path, "a valid unicode codepoint");
306 ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
307 // NOTE: Keep this in sync with the array optimization for int/float
309 let size = value.layout.size;
310 let value = value.to_scalar_or_undef();
312 // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous
313 try_validation!(value.to_bits(size),
314 value, self.path, "initialized plain (non-pointer) bytes");
316 // At run-time, for now, we accept *anything* for these types, including
317 // undef. We should fix that, but let's start low.
322 // Integers/floats in CTFE: For consistency with integers, we do not
324 let _ptr = try_validation!(value.to_scalar_ptr(),
325 "undefined address in raw pointer", self.path);
326 let _meta = try_validation!(value.to_meta(),
327 "uninitialized data in raw fat pointer metadata", self.path);
329 // Remain consistent with `usize`: Accept anything.
332 _ if ty.is_box() || ty.is_region_ptr() => {
333 // Handle fat pointers.
334 // Check metadata early, for better diagnostics
335 let ptr = try_validation!(value.to_scalar_ptr(),
336 "undefined address in pointer", self.path);
337 let meta = try_validation!(value.to_meta(),
338 "uninitialized data in fat pointer metadata", self.path);
339 let layout = self.ecx.layout_of(value.layout.ty.builtin_deref(true).unwrap().ty)?;
340 if layout.is_unsized() {
341 let tail = self.ecx.tcx.struct_tail(layout.ty);
344 let vtable = try_validation!(meta.unwrap().to_ptr(),
345 "non-pointer vtable in fat pointer", self.path);
346 try_validation!(self.ecx.read_drop_type_from_vtable(vtable),
347 "invalid drop fn in vtable", self.path);
348 try_validation!(self.ecx.read_size_and_align_from_vtable(vtable),
349 "invalid size or align in vtable", self.path);
350 // FIXME: More checks for the vtable.
352 ty::Slice(..) | ty::Str => {
353 try_validation!(meta.unwrap().to_usize(self.ecx),
354 "non-integer slice length in fat pointer", self.path);
357 // Unsized, but not fat.
360 bug!("Unexpected unsized type tail: {:?}", tail),
363 // Make sure this is non-NULL and aligned
364 let (size, align) = self.ecx.size_and_align_of(meta, layout)?
365 // for the purpose of validity, consider foreign types to have
366 // alignment and size determined by the layout (size will be 0,
367 // alignment should take attributes into account).
368 .unwrap_or_else(|| (layout.size, layout.align.abi));
369 match self.ecx.memory.check_align(ptr, align) {
372 info!("{:?} is not aligned to {:?}", ptr, align);
374 InterpError::InvalidNullPointerUsage =>
375 return validation_failure!("NULL reference", self.path),
376 InterpError::AlignmentCheckFailed { required, has } =>
377 return validation_failure!(format!("unaligned reference \
378 (required {} byte alignment but found {})",
379 required.bytes(), has.bytes()), self.path),
381 return validation_failure!(
382 "dangling (out-of-bounds) reference (might be NULL at \
389 // Recursive checking
390 if let Some(ref mut ref_tracking) = self.ref_tracking {
391 assert!(self.const_mode, "We should only do recursie checking in const mode");
392 let place = self.ecx.ref_to_mplace(value)?;
393 if size != Size::ZERO {
394 // Non-ZST also have to be dereferencable
395 let ptr = try_validation!(place.ptr.to_ptr(),
396 "integer pointer in non-ZST reference", self.path);
397 // Skip validation entirely for some external statics
398 let alloc_kind = self.ecx.tcx.alloc_map.lock().get(ptr.alloc_id);
399 if let Some(AllocKind::Static(did)) = alloc_kind {
400 // `extern static` cannot be validated as they have no body.
401 // FIXME: Statics from other crates are also skipped.
402 // They might be checked at a different type, but for now we
403 // want to avoid recursing too deeply. This is not sound!
404 if !did.is_local() || self.ecx.tcx.is_foreign_item(did) {
408 // Maintain the invariant that the place we are checking is
409 // already verified to be in-bounds.
413 .check_bounds(self.ecx, ptr, size),
414 "dangling (not entirely in bounds) reference", self.path);
416 // Check if we have encountered this pointer+layout combination
417 // before. Proceed recursively even for integer pointers, no
418 // reason to skip them! They are (recursively) valid for some ZST,
419 // but not for others (e.g., `!` is a ZST).
420 if ref_tracking.seen.insert(place) {
421 trace!("Recursing below ptr {:#?}", *place);
422 // We need to clone the path anyway, make sure it gets created
423 // with enough space for the additional `Deref`.
424 let mut new_path = Vec::with_capacity(self.path.len()+1);
425 new_path.clone_from(&self.path);
426 new_path.push(PathElem::Deref);
427 // Remember to come back to this later.
428 ref_tracking.todo.push((place, new_path));
433 let value = value.to_scalar_or_undef();
434 let ptr = try_validation!(value.to_ptr(),
435 value, self.path, "a pointer");
436 let _fn = try_validation!(self.ecx.memory.get_fn(ptr),
437 value, self.path, "a function pointer");
438 // FIXME: Check if the signature matches
440 // This should be all the primitive types
441 _ => bug!("Unexpected primitive type {}", value.layout.ty)
446 fn visit_uninhabited(&mut self) -> EvalResult<'tcx>
448 validation_failure!("a value of an uninhabited type", self.path)
453 op: OpTy<'tcx, M::PointerTag>,
454 layout: &layout::Scalar,
455 ) -> EvalResult<'tcx> {
456 let value = self.ecx.read_scalar(op)?;
457 // Determine the allowed range
458 let (lo, hi) = layout.valid_range.clone().into_inner();
459 // `max_hi` is as big as the size fits
460 let max_hi = u128::max_value() >> (128 - op.layout.size.bits());
461 assert!(hi <= max_hi);
462 // We could also write `(hi + 1) % (max_hi + 1) == lo` but `max_hi + 1` overflows for `u128`
463 if (lo == 0 && hi == max_hi) || (hi + 1 == lo) {
467 // At least one value is excluded. Get the bits.
468 let value = try_validation!(value.not_undef(),
473 wrapping_range_format(&layout.valid_range, max_hi),
476 let bits = match value {
477 Scalar::Ptr(ptr) => {
478 if lo == 1 && hi == max_hi {
479 // only NULL is not allowed.
480 // We can call `check_align` to check non-NULL-ness, but have to also look
481 // for function pointers.
483 self.ecx.memory.check_align(
484 Scalar::Ptr(ptr), Align::from_bytes(1).unwrap()
486 self.ecx.memory.get_fn(ptr).is_ok();
489 return validation_failure!("a potentially NULL pointer", self.path);
493 // Conservatively, we reject, because the pointer *could* have this
495 return validation_failure!(
499 "something that cannot possibly fail to be {}",
500 wrapping_range_format(&layout.valid_range, max_hi)
505 Scalar::Bits { bits, size } => {
506 assert_eq!(size as u64, op.layout.size.bytes());
510 // Now compare. This is slightly subtle because this is a special "wrap-around" range.
511 if wrapping_range_contains(&layout.valid_range, bits) {
517 format!("something {}", wrapping_range_format(&layout.valid_range, max_hi))
524 op: OpTy<'tcx, M::PointerTag>,
525 fields: impl Iterator<Item=EvalResult<'tcx, Self::V>>,
526 ) -> EvalResult<'tcx> {
527 match op.layout.ty.sty {
529 let mplace = op.to_mem_place(); // strings are never immediate
530 try_validation!(self.ecx.read_str(mplace),
531 "uninitialized or non-UTF-8 data in str", self.path);
533 ty::Array(tys, ..) | ty::Slice(tys) if {
534 // This optimization applies only for integer and floating point types
535 // (i.e., types that can hold arbitrary bytes).
537 ty::Int(..) | ty::Uint(..) | ty::Float(..) => true,
541 // bailing out for zsts is ok, since the array element type can only be int/float
542 if op.layout.is_zst() {
545 // non-ZST array cannot be immediate, slices are never immediate
546 let mplace = op.to_mem_place();
547 // This is the length of the array/slice.
548 let len = mplace.len(self.ecx)?;
549 // zero length slices have nothing to be checked
553 // This is the element type size.
554 let ty_size = self.ecx.layout_of(tys)?.size;
555 // This is the size in bytes of the whole array.
556 let size = ty_size * len;
558 let ptr = mplace.ptr.to_ptr()?;
560 // NOTE: Keep this in sync with the handling of integer and float
561 // types above, in `visit_primitive`.
562 // In run-time mode, we accept pointers in here. This is actually more
563 // permissive than a per-element check would be, e.g., we accept
564 // an &[u8] that contains a pointer even though bytewise checking would
565 // reject it. However, that's good: We don't inherently want
566 // to reject those pointers, we just do not have the machinery to
567 // talk about parts of a pointer.
568 // We also accept undef, for consistency with the type-based checks.
569 match self.ecx.memory.get(ptr.alloc_id)?.check_bytes(
573 /*allow_ptr_and_undef*/!self.const_mode,
575 // In the happy case, we needn't check anything else.
577 // Some error happened, try to provide a more detailed description.
579 // For some errors we might be able to provide extra information
581 InterpError::ReadUndefBytes(offset) => {
582 // Some byte was undefined, determine which
583 // element that byte belongs to so we can
585 let i = (offset.bytes() / ty_size.bytes()) as usize;
586 self.path.push(PathElem::ArrayElem(i));
588 return validation_failure!(
589 "undefined bytes", self.path
592 // Other errors shouldn't be possible
593 _ => return Err(err),
599 self.walk_aggregate(op, fields)? // default handler
606 impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> {
607 /// This function checks the data at `op`. `op` is assumed to cover valid memory if it
608 /// is an indirect operand.
609 /// It will error if the bits at the destination do not match the ones described by the layout.
611 /// `ref_tracking` can be None to avoid recursive checking below references.
612 /// This also toggles between "run-time" (no recursion) and "compile-time" (with recursion)
613 /// validation (e.g., pointer values are fine in integers at runtime).
614 pub fn validate_operand(
616 op: OpTy<'tcx, M::PointerTag>,
618 ref_tracking: Option<&mut RefTracking<MPlaceTy<'tcx, M::PointerTag>>>,
620 ) -> EvalResult<'tcx> {
621 trace!("validate_operand: {:?}, {:?}", *op, op.layout.ty);
623 // Construct a visitor
624 let mut visitor = ValidityVisitor {
632 visitor.visit_value(op)