]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/validity.rs
factor wide ptr metadata checking into separate method
[rust.git] / src / librustc_mir / interpret / validity.rs
1 use std::fmt::Write;
2 use std::ops::RangeInclusive;
3
4 use syntax_pos::symbol::{sym, Symbol};
5 use rustc::hir;
6 use rustc::ty::layout::{self, TyLayout, LayoutOf, VariantIdx};
7 use rustc::ty;
8 use rustc_data_structures::fx::FxHashSet;
9
10 use std::hash::Hash;
11
12 use super::{
13     GlobalAlloc, InterpResult,
14     Scalar, OpTy, Machine, InterpCx, ValueVisitor, MPlaceTy,
15 };
16
17 macro_rules! throw_validation_failure {
18     ($what:expr, $where:expr, $details:expr) => {{
19         let where_ = path_format(&$where);
20         let where_ = if where_.is_empty() {
21             String::new()
22         } else {
23             format!(" at {}", where_)
24         };
25         throw_unsup!(ValidationFailure(format!(
26             "encountered {}{}, but expected {}",
27             $what, where_, $details,
28         )))
29     }};
30     ($what:expr, $where:expr) => {{
31         let where_ = path_format(&$where);
32         let where_ = if where_.is_empty() {
33             String::new()
34         } else {
35             format!(" at {}", where_)
36         };
37         throw_unsup!(ValidationFailure(format!(
38             "encountered {}{}",
39             $what, where_,
40         )))
41     }};
42 }
43
44 macro_rules! try_validation {
45     ($e:expr, $what:expr, $where:expr, $details:expr) => {{
46         match $e {
47             Ok(x) => x,
48             Err(_) => throw_validation_failure!($what, $where, $details),
49         }
50     }};
51
52     ($e:expr, $what:expr, $where:expr) => {{
53         match $e {
54             Ok(x) => x,
55             Err(_) => throw_validation_failure!($what, $where),
56         }
57     }}
58 }
59
60 /// We want to show a nice path to the invalid field for diagnostics,
61 /// but avoid string operations in the happy case where no error happens.
62 /// So we track a `Vec<PathElem>` where `PathElem` contains all the data we
63 /// need to later print something for the user.
64 #[derive(Copy, Clone, Debug)]
65 pub enum PathElem {
66     Field(Symbol),
67     Variant(Symbol),
68     GeneratorState(VariantIdx),
69     ClosureVar(Symbol),
70     ArrayElem(usize),
71     TupleElem(usize),
72     Deref,
73     Tag,
74     DynDowncast,
75 }
76
77 /// State for tracking recursive validation of references
78 pub struct RefTracking<T, PATH = ()> {
79     pub seen: FxHashSet<T>,
80     pub todo: Vec<(T, PATH)>,
81 }
82
83 impl<T: Copy + Eq + Hash + std::fmt::Debug, PATH: Default> RefTracking<T, PATH> {
84     pub fn empty() -> Self {
85         RefTracking {
86             seen: FxHashSet::default(),
87             todo: vec![],
88         }
89     }
90     pub fn new(op: T) -> Self {
91         let mut ref_tracking_for_consts = RefTracking {
92             seen: FxHashSet::default(),
93             todo: vec![(op, PATH::default())],
94         };
95         ref_tracking_for_consts.seen.insert(op);
96         ref_tracking_for_consts
97     }
98
99     pub fn track(&mut self, op: T, path: impl FnOnce() -> PATH) {
100         if self.seen.insert(op) {
101             trace!("Recursing below ptr {:#?}", op);
102             let path = path();
103             // Remember to come back to this later.
104             self.todo.push((op, path));
105         }
106     }
107 }
108
109 /// Format a path
110 fn path_format(path: &Vec<PathElem>) -> String {
111     use self::PathElem::*;
112
113     let mut out = String::new();
114     for elem in path.iter() {
115         match elem {
116             Field(name) => write!(out, ".{}", name),
117             Variant(name) => write!(out, ".<downcast-variant({})>", name),
118             GeneratorState(idx) => write!(out, ".<generator-state({})>", idx.index()),
119             ClosureVar(name) => write!(out, ".<closure-var({})>", name),
120             TupleElem(idx) => write!(out, ".{}", idx),
121             ArrayElem(idx) => write!(out, "[{}]", idx),
122             Deref =>
123                 // This does not match Rust syntax, but it is more readable for long paths -- and
124                 // some of the other items here also are not Rust syntax.  Actually we can't
125                 // even use the usual syntax because we are just showing the projections,
126                 // not the root.
127                 write!(out, ".<deref>"),
128             Tag => write!(out, ".<enum-tag>"),
129             DynDowncast => write!(out, ".<dyn-downcast>"),
130         }.unwrap()
131     }
132     out
133 }
134
135 // Test if a range that wraps at overflow contains `test`
136 fn wrapping_range_contains(r: &RangeInclusive<u128>, test: u128) -> bool {
137     let (lo, hi) = r.clone().into_inner();
138     if lo > hi {
139         // Wrapped
140         (..=hi).contains(&test) || (lo..).contains(&test)
141     } else {
142         // Normal
143         r.contains(&test)
144     }
145 }
146
147 // Formats such that a sentence like "expected something {}" to mean
148 // "expected something <in the given range>" makes sense.
149 fn wrapping_range_format(r: &RangeInclusive<u128>, max_hi: u128) -> String {
150     let (lo, hi) = r.clone().into_inner();
151     debug_assert!(hi <= max_hi);
152     if lo > hi {
153         format!("less or equal to {}, or greater or equal to {}", hi, lo)
154     } else if lo == hi {
155         format!("equal to {}", lo)
156     } else if lo == 0 {
157         debug_assert!(hi < max_hi, "should not be printing if the range covers everything");
158         format!("less or equal to {}", hi)
159     } else if hi == max_hi {
160         debug_assert!(lo > 0, "should not be printing if the range covers everything");
161         format!("greater or equal to {}", lo)
162     } else {
163         format!("in the range {:?}", r)
164     }
165 }
166
167 struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
168     /// The `path` may be pushed to, but the part that is present when a function
169     /// starts must not be changed!  `visit_fields` and `visit_array` rely on
170     /// this stack discipline.
171     path: Vec<PathElem>,
172     ref_tracking_for_consts: Option<&'rt mut RefTracking<
173         MPlaceTy<'tcx, M::PointerTag>,
174         Vec<PathElem>,
175     >>,
176     ecx: &'rt InterpCx<'mir, 'tcx, M>,
177 }
178
179 impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M> {
180     fn aggregate_field_path_elem(
181         &mut self,
182         layout: TyLayout<'tcx>,
183         field: usize,
184     ) -> PathElem {
185         match layout.ty.sty {
186             // generators and closures.
187             ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
188                 let mut name = None;
189                 if def_id.is_local() {
190                     let tables = self.ecx.tcx.typeck_tables_of(def_id);
191                     if let Some(upvars) = tables.upvar_list.get(&def_id) {
192                         // Sometimes the index is beyond the number of upvars (seen
193                         // for a generator).
194                         if let Some((&var_hir_id, _)) = upvars.get_index(field) {
195                             let node = self.ecx.tcx.hir().get(var_hir_id);
196                             if let hir::Node::Binding(pat) = node {
197                                 if let hir::PatKind::Binding(_, _, ident, _) = pat.node {
198                                     name = Some(ident.name);
199                                 }
200                             }
201                         }
202                     }
203                 }
204
205                 PathElem::ClosureVar(name.unwrap_or_else(|| {
206                     // Fall back to showing the field index.
207                     sym::integer(field)
208                 }))
209             }
210
211             // tuples
212             ty::Tuple(_) => PathElem::TupleElem(field),
213
214             // enums
215             ty::Adt(def, ..) if def.is_enum() => {
216                 // we might be projecting *to* a variant, or to a field *in*a variant.
217                 match layout.variants {
218                     layout::Variants::Single { index } =>
219                         // Inside a variant
220                         PathElem::Field(def.variants[index].fields[field].ident.name),
221                     _ => bug!(),
222                 }
223             }
224
225             // other ADTs
226             ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name),
227
228             // arrays/slices
229             ty::Array(..) | ty::Slice(..) => PathElem::ArrayElem(field),
230
231             // dyn traits
232             ty::Dynamic(..) => PathElem::DynDowncast,
233
234             // nothing else has an aggregate layout
235             _ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", layout.ty),
236         }
237     }
238
239     fn visit_elem(
240         &mut self,
241         new_op: OpTy<'tcx, M::PointerTag>,
242         elem: PathElem,
243     ) -> InterpResult<'tcx> {
244         // Remember the old state
245         let path_len = self.path.len();
246         // Perform operation
247         self.path.push(elem);
248         self.visit_value(new_op)?;
249         // Undo changes
250         self.path.truncate(path_len);
251         Ok(())
252     }
253
254     fn check_wide_ptr_meta(
255         &mut self,
256         meta: Option<Scalar<M::PointerTag>>,
257         pointee: TyLayout<'tcx>,
258     ) -> InterpResult<'tcx> {
259         let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env);
260         match tail.sty {
261             ty::Dynamic(..) => {
262                 let vtable = meta.unwrap();
263                 try_validation!(
264                     self.ecx.memory.check_ptr_access(
265                         vtable,
266                         3*self.ecx.tcx.data_layout.pointer_size, // drop, size, align
267                         self.ecx.tcx.data_layout.pointer_align.abi,
268                     ),
269                     "dangling or unaligned vtable pointer in wide pointer or too small vtable",
270                     self.path
271                 );
272                 try_validation!(self.ecx.read_drop_type_from_vtable(vtable),
273                     "invalid drop fn in vtable", self.path);
274                 try_validation!(self.ecx.read_size_and_align_from_vtable(vtable),
275                     "invalid size or align in vtable", self.path);
276                 // FIXME: More checks for the vtable.
277             }
278             ty::Slice(..) | ty::Str => {
279                 try_validation!(meta.unwrap().to_usize(self.ecx),
280                     "non-integer slice length in wide pointer", self.path);
281             }
282             ty::Foreign(..) => {
283                 // Unsized, but not wide.
284             }
285             _ =>
286                 bug!("Unexpected unsized type tail: {:?}", tail),
287         }
288
289         Ok(())
290     }
291 }
292
293 impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
294     for ValidityVisitor<'rt, 'mir, 'tcx, M>
295 {
296     type V = OpTy<'tcx, M::PointerTag>;
297
298     #[inline(always)]
299     fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> {
300         &self.ecx
301     }
302
303     #[inline]
304     fn visit_field(
305         &mut self,
306         old_op: OpTy<'tcx, M::PointerTag>,
307         field: usize,
308         new_op: OpTy<'tcx, M::PointerTag>
309     ) -> InterpResult<'tcx> {
310         let elem = self.aggregate_field_path_elem(old_op.layout, field);
311         self.visit_elem(new_op, elem)
312     }
313
314     #[inline]
315     fn visit_variant(
316         &mut self,
317         old_op: OpTy<'tcx, M::PointerTag>,
318         variant_id: VariantIdx,
319         new_op: OpTy<'tcx, M::PointerTag>
320     ) -> InterpResult<'tcx> {
321         let name = match old_op.layout.ty.sty {
322             ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name),
323             // Generators also have variants
324             ty::Generator(..) => PathElem::GeneratorState(variant_id),
325             _ => bug!("Unexpected type with variant: {:?}", old_op.layout.ty),
326         };
327         self.visit_elem(new_op, name)
328     }
329
330     #[inline]
331     fn visit_value(&mut self, op: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx>
332     {
333         trace!("visit_value: {:?}, {:?}", *op, op.layout);
334         // Translate some possible errors to something nicer.
335         match self.walk_value(op) {
336             Ok(()) => Ok(()),
337             Err(err) => match err.kind {
338                 err_unsup!(InvalidDiscriminant(val)) =>
339                     throw_validation_failure!(
340                         val, self.path, "a valid enum discriminant"
341                     ),
342                 err_unsup!(ReadPointerAsBytes) =>
343                     throw_validation_failure!(
344                         "a pointer", self.path, "plain (non-pointer) bytes"
345                     ),
346                 _ => Err(err),
347             }
348         }
349     }
350
351     fn visit_primitive(&mut self, value: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx>
352     {
353         let value = self.ecx.read_immediate(value)?;
354         // Go over all the primitive types
355         let ty = value.layout.ty;
356         match ty.sty {
357             ty::Bool => {
358                 let value = value.to_scalar_or_undef();
359                 try_validation!(value.to_bool(),
360                     value, self.path, "a boolean");
361             },
362             ty::Char => {
363                 let value = value.to_scalar_or_undef();
364                 try_validation!(value.to_char(),
365                     value, self.path, "a valid unicode codepoint");
366             },
367             ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
368                 // NOTE: Keep this in sync with the array optimization for int/float
369                 // types below!
370                 let size = value.layout.size;
371                 let value = value.to_scalar_or_undef();
372                 if self.ref_tracking_for_consts.is_some() {
373                     // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous
374                     try_validation!(value.to_bits(size),
375                         value, self.path, "initialized plain (non-pointer) bytes");
376                 } else {
377                     // At run-time, for now, we accept *anything* for these types, including
378                     // undef. We should fix that, but let's start low.
379                 }
380             }
381             ty::RawPtr(..) => {
382                 if self.ref_tracking_for_consts.is_some() {
383                     // Integers/floats in CTFE: For consistency with integers, we do not
384                     // accept undef.
385                     let _ptr = try_validation!(value.to_scalar_ptr(),
386                         "undefined address in raw pointer", self.path);
387                     let _meta = try_validation!(value.to_meta(),
388                         "uninitialized data in raw fat pointer metadata", self.path);
389                 } else {
390                     // Remain consistent with `usize`: Accept anything.
391                 }
392             }
393             _ if ty.is_box() || ty.is_region_ptr() => {
394                 // Handle wide pointers.
395                 // Check metadata early, for better diagnostics
396                 let ptr = try_validation!(value.to_scalar_ptr(),
397                     "undefined address in pointer", self.path);
398                 let meta = try_validation!(value.to_meta(),
399                     "uninitialized data in wide pointer metadata", self.path);
400                 let layout = self.ecx.layout_of(value.layout.ty.builtin_deref(true).unwrap().ty)?;
401                 if layout.is_unsized() {
402                     self.check_wide_ptr_meta(meta, layout)?;
403                 }
404                 // Make sure this is dereferencable and all.
405                 let (size, align) = self.ecx.size_and_align_of(meta, layout)?
406                     // for the purpose of validity, consider foreign types to have
407                     // alignment and size determined by the layout (size will be 0,
408                     // alignment should take attributes into account).
409                     .unwrap_or_else(|| (layout.size, layout.align.abi));
410                 let ptr: Option<_> = match
411                     self.ecx.memory.check_ptr_access_align(ptr, size, Some(align))
412                 {
413                     Ok(ptr) => ptr,
414                     Err(err) => {
415                         info!(
416                             "{:?} did not pass access check for size {:?}, align {:?}",
417                             ptr, size, align
418                         );
419                         match err.kind {
420                             err_unsup!(InvalidNullPointerUsage) =>
421                                 throw_validation_failure!("NULL reference", self.path),
422                             err_unsup!(AlignmentCheckFailed { required, has }) =>
423                                 throw_validation_failure!(format!("unaligned reference \
424                                     (required {} byte alignment but found {})",
425                                     required.bytes(), has.bytes()), self.path),
426                             err_unsup!(ReadBytesAsPointer) =>
427                                 throw_validation_failure!(
428                                     "dangling reference (created from integer)",
429                                     self.path
430                                 ),
431                             _ =>
432                                 throw_validation_failure!(
433                                     "dangling reference (not entirely in bounds)",
434                                     self.path
435                                 ),
436                         }
437                     }
438                 };
439                 // Recursive checking
440                 if let Some(ref mut ref_tracking) = self.ref_tracking_for_consts {
441                     let place = self.ecx.ref_to_mplace(value)?;
442                     if let Some(ptr) = ptr { // not a ZST
443                         // Skip validation entirely for some external statics
444                         let alloc_kind = self.ecx.tcx.alloc_map.lock().get(ptr.alloc_id);
445                         if let Some(GlobalAlloc::Static(did)) = alloc_kind {
446                             // `extern static` cannot be validated as they have no body.
447                             // FIXME: Statics from other crates are also skipped.
448                             // They might be checked at a different type, but for now we
449                             // want to avoid recursing too deeply.  This is not sound!
450                             if !did.is_local() || self.ecx.tcx.is_foreign_item(did) {
451                                 return Ok(());
452                             }
453                         }
454                     }
455                     // Proceed recursively even for ZST, no reason to skip them!
456                     // `!` is a ZST and we want to validate it.
457                     // Normalize before handing `place` to tracking because that will
458                     // check for duplicates.
459                     let place = if size.bytes() > 0 {
460                         self.ecx.force_mplace_ptr(place)
461                             .expect("we already bounds-checked")
462                     } else {
463                         place
464                     };
465                     let path = &self.path;
466                     ref_tracking.track(place, || {
467                         // We need to clone the path anyway, make sure it gets created
468                         // with enough space for the additional `Deref`.
469                         let mut new_path = Vec::with_capacity(path.len() + 1);
470                         new_path.clone_from(path);
471                         new_path.push(PathElem::Deref);
472                         new_path
473                     });
474                 }
475             }
476             ty::FnPtr(_sig) => {
477                 let value = value.to_scalar_or_undef();
478                 let _fn = try_validation!(
479                     value.not_undef().and_then(|ptr| self.ecx.memory.get_fn(ptr)),
480                     value, self.path, "a function pointer"
481                 );
482                 // FIXME: Check if the signature matches
483             }
484             // This should be all the primitive types
485             _ => bug!("Unexpected primitive type {}", value.layout.ty)
486         }
487         Ok(())
488     }
489
490     fn visit_uninhabited(&mut self) -> InterpResult<'tcx>
491     {
492         throw_validation_failure!("a value of an uninhabited type", self.path)
493     }
494
495     fn visit_scalar(
496         &mut self,
497         op: OpTy<'tcx, M::PointerTag>,
498         layout: &layout::Scalar,
499     ) -> InterpResult<'tcx> {
500         let value = self.ecx.read_scalar(op)?;
501         // Determine the allowed range
502         let (lo, hi) = layout.valid_range.clone().into_inner();
503         // `max_hi` is as big as the size fits
504         let max_hi = u128::max_value() >> (128 - op.layout.size.bits());
505         assert!(hi <= max_hi);
506         // We could also write `(hi + 1) % (max_hi + 1) == lo` but `max_hi + 1` overflows for `u128`
507         if (lo == 0 && hi == max_hi) || (hi + 1 == lo) {
508             // Nothing to check
509             return Ok(());
510         }
511         // At least one value is excluded. Get the bits.
512         let value = try_validation!(value.not_undef(),
513             value,
514             self.path,
515             format!(
516                 "something {}",
517                 wrapping_range_format(&layout.valid_range, max_hi),
518             )
519         );
520         let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) {
521             Err(ptr) => {
522                 if lo == 1 && hi == max_hi {
523                     // Only NULL is the niche.  So make sure the ptr is NOT NULL.
524                     if self.ecx.memory.ptr_may_be_null(ptr) {
525                         throw_validation_failure!(
526                             "a potentially NULL pointer",
527                             self.path,
528                             format!(
529                                 "something that cannot possibly fail to be {}",
530                                 wrapping_range_format(&layout.valid_range, max_hi)
531                             )
532                         )
533                     }
534                     return Ok(());
535                 } else {
536                     // Conservatively, we reject, because the pointer *could* have a bad
537                     // value.
538                     throw_validation_failure!(
539                         "a pointer",
540                         self.path,
541                         format!(
542                             "something that cannot possibly fail to be {}",
543                             wrapping_range_format(&layout.valid_range, max_hi)
544                         )
545                     )
546                 }
547             }
548             Ok(data) =>
549                 data
550         };
551         // Now compare. This is slightly subtle because this is a special "wrap-around" range.
552         if wrapping_range_contains(&layout.valid_range, bits) {
553             Ok(())
554         } else {
555             throw_validation_failure!(
556                 bits,
557                 self.path,
558                 format!("something {}", wrapping_range_format(&layout.valid_range, max_hi))
559             )
560         }
561     }
562
563     fn visit_aggregate(
564         &mut self,
565         op: OpTy<'tcx, M::PointerTag>,
566         fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
567     ) -> InterpResult<'tcx> {
568         match op.layout.ty.sty {
569             ty::Str => {
570                 let mplace = op.assert_mem_place(); // strings are never immediate
571                 try_validation!(self.ecx.read_str(mplace),
572                     "uninitialized or non-UTF-8 data in str", self.path);
573             }
574             ty::Array(tys, ..) | ty::Slice(tys) if {
575                 // This optimization applies only for integer and floating point types
576                 // (i.e., types that can hold arbitrary bytes).
577                 match tys.sty {
578                     ty::Int(..) | ty::Uint(..) | ty::Float(..) => true,
579                     _ => false,
580                 }
581             } => {
582                 // bailing out for zsts is ok, since the array element type can only be int/float
583                 if op.layout.is_zst() {
584                     return Ok(());
585                 }
586                 // non-ZST array cannot be immediate, slices are never immediate
587                 let mplace = op.assert_mem_place();
588                 // This is the length of the array/slice.
589                 let len = mplace.len(self.ecx)?;
590                 // zero length slices have nothing to be checked
591                 if len == 0 {
592                     return Ok(());
593                 }
594                 // This is the element type size.
595                 let ty_size = self.ecx.layout_of(tys)?.size;
596                 // This is the size in bytes of the whole array.
597                 let size = ty_size * len;
598                 // Size is not 0, get a pointer.
599                 let ptr = self.ecx.force_ptr(mplace.ptr)?;
600
601                 // NOTE: Keep this in sync with the handling of integer and float
602                 // types above, in `visit_primitive`.
603                 // In run-time mode, we accept pointers in here.  This is actually more
604                 // permissive than a per-element check would be, e.g., we accept
605                 // an &[u8] that contains a pointer even though bytewise checking would
606                 // reject it.  However, that's good: We don't inherently want
607                 // to reject those pointers, we just do not have the machinery to
608                 // talk about parts of a pointer.
609                 // We also accept undef, for consistency with the type-based checks.
610                 match self.ecx.memory.get(ptr.alloc_id)?.check_bytes(
611                     self.ecx,
612                     ptr,
613                     size,
614                     /*allow_ptr_and_undef*/ self.ref_tracking_for_consts.is_none(),
615                 ) {
616                     // In the happy case, we needn't check anything else.
617                     Ok(()) => {},
618                     // Some error happened, try to provide a more detailed description.
619                     Err(err) => {
620                         // For some errors we might be able to provide extra information
621                         match err.kind {
622                             err_unsup!(ReadUndefBytes(offset)) => {
623                                 // Some byte was undefined, determine which
624                                 // element that byte belongs to so we can
625                                 // provide an index.
626                                 let i = (offset.bytes() / ty_size.bytes()) as usize;
627                                 self.path.push(PathElem::ArrayElem(i));
628
629                                 throw_validation_failure!("undefined bytes", self.path)
630                             },
631                             // Other errors shouldn't be possible
632                             _ => return Err(err),
633                         }
634                     }
635                 }
636             }
637             _ => {
638                 self.walk_aggregate(op, fields)? // default handler
639             }
640         }
641         Ok(())
642     }
643 }
644
645 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
646     /// This function checks the data at `op`. `op` is assumed to cover valid memory if it
647     /// is an indirect operand.
648     /// It will error if the bits at the destination do not match the ones described by the layout.
649     ///
650     /// `ref_tracking_for_consts` can be `None` to avoid recursive checking below references.
651     /// This also toggles between "run-time" (no recursion) and "compile-time" (with recursion)
652     /// validation (e.g., pointer values are fine in integers at runtime) and various other const
653     /// specific validation checks.
654     pub fn validate_operand(
655         &self,
656         op: OpTy<'tcx, M::PointerTag>,
657         path: Vec<PathElem>,
658         ref_tracking_for_consts: Option<&mut RefTracking<
659             MPlaceTy<'tcx, M::PointerTag>,
660             Vec<PathElem>,
661         >>,
662     ) -> InterpResult<'tcx> {
663         trace!("validate_operand: {:?}, {:?}", *op, op.layout.ty);
664
665         // Construct a visitor
666         let mut visitor = ValidityVisitor {
667             path,
668             ref_tracking_for_consts,
669             ecx: self,
670         };
671
672         // Try to cast to ptr *once* instead of all the time.
673         let op = self.force_op_ptr(op).unwrap_or(op);
674
675         // Run it
676         visitor.visit_value(op)
677     }
678 }