]> git.lizzy.rs Git - rust.git/blob - miri/validation.rs
Fix it
[rust.git] / miri / validation.rs
1 use rustc::hir::{self, Mutability};
2 use rustc::hir::Mutability::*;
3 use rustc::mir::{self, ValidationOp, ValidationOperand};
4 use rustc::mir::interpret::GlobalId;
5 use rustc::ty::{self, Ty, TypeFoldable, TyCtxt, Instance};
6 use rustc::ty::layout::LayoutOf;
7 use rustc::ty::subst::{Substs, Subst};
8 use rustc::traits::{self, TraitEngine};
9 use rustc::infer::InferCtxt;
10 use rustc::middle::region;
11 use rustc::middle::const_val::ConstVal;
12 use rustc_data_structures::indexed_vec::Idx;
13 use rustc_mir::interpret::{HasMemory, eval_body};
14
15 use super::{EvalContext, Place, PlaceExtra, ValTy};
16 use rustc::mir::interpret::{DynamicLifetime, AccessKind, EvalErrorKind, Value, EvalError, EvalResult};
17 use locks::MemoryExt;
18
19 pub type ValidationQuery<'tcx> = ValidationOperand<'tcx, (AbsPlace<'tcx>, Place)>;
20
21 #[derive(Copy, Clone, Debug, PartialEq)]
22 pub(crate) enum ValidationMode {
23     Acquire,
24     /// Recover because the given region ended
25     Recover(region::Scope),
26     ReleaseUntil(Option<region::Scope>),
27 }
28
29 impl ValidationMode {
30     fn acquiring(self) -> bool {
31         use self::ValidationMode::*;
32         match self {
33             Acquire | Recover(_) => true,
34             ReleaseUntil(_) => false,
35         }
36     }
37 }
38
39 // Abstract places
40 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
41 pub enum AbsPlace<'tcx> {
42     Local(mir::Local),
43     Static(hir::def_id::DefId),
44     Projection(Box<AbsPlaceProjection<'tcx>>),
45 }
46
47 type AbsPlaceProjection<'tcx> = mir::Projection<'tcx, AbsPlace<'tcx>, u64, ()>;
48 type AbsPlaceElem<'tcx> = mir::ProjectionElem<'tcx, u64, ()>;
49
50 impl<'tcx> AbsPlace<'tcx> {
51     pub fn field(self, f: mir::Field) -> AbsPlace<'tcx> {
52         self.elem(mir::ProjectionElem::Field(f, ()))
53     }
54
55     pub fn deref(self) -> AbsPlace<'tcx> {
56         self.elem(mir::ProjectionElem::Deref)
57     }
58
59     pub fn downcast(self, adt_def: &'tcx ty::AdtDef, variant_index: usize) -> AbsPlace<'tcx> {
60         self.elem(mir::ProjectionElem::Downcast(adt_def, variant_index))
61     }
62
63     pub fn index(self, index: u64) -> AbsPlace<'tcx> {
64         self.elem(mir::ProjectionElem::Index(index))
65     }
66
67     fn elem(self, elem: AbsPlaceElem<'tcx>) -> AbsPlace<'tcx> {
68         AbsPlace::Projection(Box::new(AbsPlaceProjection {
69             base: self,
70             elem,
71         }))
72     }
73 }
74
75 pub(crate) trait EvalContextExt<'tcx> {
76     fn abstract_place_projection(&self, proj: &mir::PlaceProjection<'tcx>) -> EvalResult<'tcx, AbsPlaceProjection<'tcx>>;
77     fn abstract_place(&self, place: &mir::Place<'tcx>) -> EvalResult<'tcx, AbsPlace<'tcx>>;
78     fn validation_op(
79         &mut self,
80         op: ValidationOp,
81         operand: &ValidationOperand<'tcx, mir::Place<'tcx>>,
82     ) -> EvalResult<'tcx>;
83     fn end_region(&mut self, scope: Option<region::Scope>) -> EvalResult<'tcx>;
84     fn normalize_type_unerased(&self, ty: Ty<'tcx>) -> Ty<'tcx>;
85     fn field_with_lifetimes(
86         &mut self,
87         base: Place,
88         layout: ty::layout::TyLayout<'tcx>,
89         i: usize,
90     ) -> EvalResult<'tcx, Ty<'tcx>>;
91     fn validate_fields(
92         &mut self,
93         query: ValidationQuery<'tcx>,
94         mode: ValidationMode,
95     ) -> EvalResult<'tcx>;
96     fn validate_ptr(
97         &mut self,
98         val: Value,
99         abs_place: AbsPlace<'tcx>,
100         pointee_ty: Ty<'tcx>,
101         re: Option<region::Scope>,
102         mutbl: Mutability,
103         mode: ValidationMode,
104     ) -> EvalResult<'tcx>;
105     fn validate(
106         &mut self,
107         query: ValidationQuery<'tcx>,
108         mode: ValidationMode,
109     ) -> EvalResult<'tcx>;
110 }
111
112 impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super::Evaluator<'tcx>> {
113     fn abstract_place_projection(&self, proj: &mir::PlaceProjection<'tcx>) -> EvalResult<'tcx, AbsPlaceProjection<'tcx>> {
114         use self::mir::ProjectionElem::*;
115
116         let elem = match proj.elem {
117             Deref => Deref,
118             Field(f, _) => Field(f, ()),
119             Index(v) => {
120                 let value = self.frame().get_local(v)?;
121                 let ty = self.tcx.tcx.types.usize;
122                 let n = self.value_to_primval(ValTy { value, ty })?.to_u64()?;
123                 Index(n)
124             },
125             ConstantIndex { offset, min_length, from_end } =>
126                 ConstantIndex { offset, min_length, from_end },
127             Subslice { from, to } =>
128                 Subslice { from, to },
129             Downcast(adt, sz) => Downcast(adt, sz),
130         };
131         Ok(AbsPlaceProjection {
132             base: self.abstract_place(&proj.base)?,
133             elem
134         })
135     }
136
137     fn abstract_place(&self, place: &mir::Place<'tcx>) -> EvalResult<'tcx, AbsPlace<'tcx>> {
138         Ok(match place {
139             &mir::Place::Local(l) => AbsPlace::Local(l),
140             &mir::Place::Static(ref s) => AbsPlace::Static(s.def_id),
141             &mir::Place::Projection(ref p) =>
142                 AbsPlace::Projection(Box::new(self.abstract_place_projection(&*p)?)),
143         })
144     }
145
146     // Validity checks
147     fn validation_op(
148         &mut self,
149         op: ValidationOp,
150         operand: &ValidationOperand<'tcx, mir::Place<'tcx>>,
151     ) -> EvalResult<'tcx> {
152         // If mir-emit-validate is set to 0 (i.e., disabled), we may still see validation commands
153         // because other crates may have been compiled with mir-emit-validate > 0.  Ignore those
154         // commands.  This makes mir-emit-validate also a flag to control whether miri will do
155         // validation or not.
156         if self.tcx.tcx.sess.opts.debugging_opts.mir_emit_validate == 0 {
157             return Ok(());
158         }
159         debug_assert!(self.memory.cur_frame == self.cur_frame());
160
161         // HACK: Determine if this method is whitelisted and hence we do not perform any validation.
162         // We currently insta-UB on anything passing around uninitialized memory, so we have to whitelist
163         // the places that are allowed to do that.
164         // The second group is stuff libstd does that is forbidden even under relaxed validation.
165         {
166             // The regexp we use for filtering
167             use regex::Regex;
168             lazy_static! {
169                 static ref RE: Regex = Regex::new("^(\
170                     (std|alloc::heap::__core)::mem::(uninitialized|forget)::|\
171                     <(std|alloc)::heap::Heap as (std::heap|alloc::allocator)::Alloc>::|\
172                     <(std|alloc::heap::__core)::mem::ManuallyDrop<T>><.*>::new$|\
173                     <(std|alloc::heap::__core)::mem::ManuallyDrop<T> as std::ops::DerefMut><.*>::deref_mut$|\
174                     (std|alloc::heap::__core)::ptr::read::|\
175                     \
176                     <std::sync::Arc<T>><.*>::inner$|\
177                     <std::sync::Arc<T>><.*>::drop_slow$|\
178                     (std::heap|alloc::allocator)::Layout::for_value::|\
179                     (std|alloc::heap::__core)::mem::(size|align)_of_val::\
180                 )").unwrap();
181             }
182             // Now test
183             let name = self.frame().instance.to_string();
184             if RE.is_match(&name) {
185                 return Ok(());
186             }
187         }
188
189         // We need to monomorphize ty *without* erasing lifetimes
190         trace!("validation_op1: {:?}", operand.ty.sty);
191         let ty = operand.ty.subst(self.tcx.tcx, self.substs());
192         trace!("validation_op2: {:?}", operand.ty.sty);
193         let place = self.eval_place(&operand.place)?;
194         let abs_place = self.abstract_place(&operand.place)?;
195         let query = ValidationQuery {
196             place: (abs_place, place),
197             ty,
198             re: operand.re,
199             mutbl: operand.mutbl,
200         };
201
202         // Check the mode, and also perform mode-specific operations
203         let mode = match op {
204             ValidationOp::Acquire => ValidationMode::Acquire,
205             ValidationOp::Release => ValidationMode::ReleaseUntil(None),
206             ValidationOp::Suspend(scope) => {
207                 if query.mutbl == MutMutable {
208                     let lft = DynamicLifetime {
209                         frame: self.cur_frame(),
210                         region: Some(scope), // Notably, we only ever suspend things for given regions.
211                         // Suspending for the entire function does not make any sense.
212                     };
213                     trace!("Suspending {:?} until {:?}", query, scope);
214                     self.machine.suspended.entry(lft).or_insert_with(Vec::new).push(
215                         query.clone(),
216                     );
217                 }
218                 ValidationMode::ReleaseUntil(Some(scope))
219             }
220         };
221         self.validate(query, mode)
222     }
223
224     /// Release locks and executes suspensions of the given region (or the entire fn, in case of None).
225     fn end_region(&mut self, scope: Option<region::Scope>) -> EvalResult<'tcx> {
226         debug_assert!(self.memory.cur_frame == self.cur_frame());
227         self.memory.locks_lifetime_ended(scope);
228         match scope {
229             Some(scope) => {
230                 // Recover suspended places
231                 let lft = DynamicLifetime {
232                     frame: self.cur_frame(),
233                     region: Some(scope),
234                 };
235                 if let Some(queries) = self.machine.suspended.remove(&lft) {
236                     for query in queries {
237                         trace!("Recovering {:?} from suspension", query);
238                         self.validate(query, ValidationMode::Recover(scope))?;
239                     }
240                 }
241             }
242             None => {
243                 // Clean suspension table of current frame
244                 let cur_frame = self.cur_frame();
245                 self.machine.suspended.retain(|lft, _| {
246                     lft.frame != cur_frame // keep only what is in the other (lower) frames
247                 });
248             }
249         }
250         Ok(())
251     }
252
253     fn normalize_type_unerased(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
254         return normalize_associated_type(self.tcx.tcx, &ty);
255
256         use syntax::codemap::{Span, DUMMY_SP};
257
258         // We copy a bunch of stuff from rustc/infer/mod.rs to be able to tweak its behavior
259         fn normalize_projections_in<'a, 'gcx, 'tcx, T>(
260             self_: &InferCtxt<'a, 'gcx, 'tcx>,
261             param_env: ty::ParamEnv<'tcx>,
262             value: &T,
263         ) -> T::Lifted
264         where
265             T: TypeFoldable<'tcx> + ty::Lift<'gcx>,
266         {
267             let mut selcx = traits::SelectionContext::new(self_);
268             let cause = traits::ObligationCause::dummy();
269             let traits::Normalized {
270                 value: result,
271                 obligations,
272             } = traits::normalize(&mut selcx, param_env, cause, value);
273
274             let mut fulfill_cx = traits::FulfillmentContext::new();
275
276             for obligation in obligations {
277                 fulfill_cx.register_predicate_obligation(self_, obligation);
278             }
279
280             drain_fulfillment_cx_or_panic(self_, DUMMY_SP, &mut fulfill_cx, &result)
281         }
282
283         fn drain_fulfillment_cx_or_panic<'a, 'gcx, 'tcx, T>(
284             self_: &InferCtxt<'a, 'gcx, 'tcx>,
285             span: Span,
286             fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
287             result: &T,
288         ) -> T::Lifted
289         where
290             T: TypeFoldable<'tcx> + ty::Lift<'gcx>,
291         {
292             // In principle, we only need to do this so long as `result`
293             // contains unbound type parameters. It could be a slight
294             // optimization to stop iterating early.
295             match fulfill_cx.select_all_or_error(self_) {
296                 Ok(()) => { }
297                 Err(errors) => {
298                     span_bug!(
299                         span,
300                         "Encountered errors `{:?}` resolving bounds after type-checking",
301                         errors
302                     );
303                 }
304             }
305
306             let result = self_.resolve_type_vars_if_possible(result);
307             let result = self_.tcx.fold_regions(
308                 &result,
309                 &mut false,
310                 |r, _| match *r {
311                     ty::ReVar(_) => self_.tcx.types.re_erased,
312                     _ => r,
313                 },
314             );
315
316             match self_.tcx.lift_to_global(&result) {
317                 Some(result) => result,
318                 None => {
319                     span_bug!(span, "Uninferred types/regions in `{:?}`", result);
320                 }
321             }
322         }
323
324         trait MyTransNormalize<'gcx>: TypeFoldable<'gcx> {
325             fn my_trans_normalize<'a, 'tcx>(
326                 &self,
327                 infcx: &InferCtxt<'a, 'gcx, 'tcx>,
328                 param_env: ty::ParamEnv<'tcx>,
329             ) -> Self;
330         }
331
332         macro_rules! items { ($($item:item)+) => ($($item)+) }
333         macro_rules! impl_trans_normalize {
334             ($lt_gcx:tt, $($ty:ty),+) => {
335                 items!($(impl<$lt_gcx> MyTransNormalize<$lt_gcx> for $ty {
336                     fn my_trans_normalize<'a, 'tcx>(&self,
337                                                 infcx: &InferCtxt<'a, $lt_gcx, 'tcx>,
338                                                 param_env: ty::ParamEnv<'tcx>)
339                                                 -> Self {
340                         normalize_projections_in(infcx, param_env, self)
341                     }
342                 })+);
343             }
344         }
345
346         impl_trans_normalize!('gcx,
347             Ty<'gcx>,
348             &'gcx Substs<'gcx>,
349             ty::FnSig<'gcx>,
350             ty::PolyFnSig<'gcx>,
351             ty::ClosureSubsts<'gcx>,
352             ty::PolyTraitRef<'gcx>,
353             ty::ExistentialTraitRef<'gcx>
354         );
355
356         fn normalize_associated_type<'a, 'tcx, T>(self_: TyCtxt<'a, 'tcx, 'tcx>, value: &T) -> T
357         where
358             T: MyTransNormalize<'tcx>,
359         {
360             let param_env = ty::ParamEnv::reveal_all();
361
362             if !value.has_projections() {
363                 return value.clone();
364             }
365
366             self_.infer_ctxt().enter(|infcx| {
367                 value.my_trans_normalize(&infcx, param_env)
368             })
369         }
370     }
371
372     // This is a copy of `Layout::field`
373     //
374     // FIXME: remove once validation does not depend on lifetimes
375     fn field_with_lifetimes(
376         &mut self,
377         base: Place,
378         mut layout: ty::layout::TyLayout<'tcx>,
379         i: usize,
380     ) -> EvalResult<'tcx, Ty<'tcx>> {
381         match base {
382             Place::Ptr { extra: PlaceExtra::DowncastVariant(variant_index), .. } => {
383                 layout = layout.for_variant(&self, variant_index);
384             }
385             _ => {}
386         }
387         let tcx = self.tcx.tcx;
388         Ok(match layout.ty.sty {
389             ty::TyBool |
390             ty::TyChar |
391             ty::TyInt(_) |
392             ty::TyUint(_) |
393             ty::TyFloat(_) |
394             ty::TyFnPtr(_) |
395             ty::TyNever |
396             ty::TyFnDef(..) |
397             ty::TyGeneratorWitness(..) |
398             ty::TyDynamic(..) |
399             ty::TyForeign(..) => {
400                 bug!("TyLayout::field_type({:?}): not applicable", layout)
401             }
402
403             // Potentially-fat pointers.
404             ty::TyRef(_, ty::TypeAndMut { ty: pointee, .. }) |
405             ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
406                 assert!(i < 2);
407
408                 // Reuse the fat *T type as its own thin pointer data field.
409                 // This provides information about e.g. DST struct pointees
410                 // (which may have no non-DST form), and will work as long
411                 // as the `Abi` or `FieldPlacement` is checked by users.
412                 if i == 0 {
413                     return Ok(layout.ty);
414                 }
415
416                 match tcx.struct_tail(pointee).sty {
417                     ty::TySlice(_) |
418                     ty::TyStr => tcx.types.usize,
419                     ty::TyDynamic(..) => {
420                         // FIXME(eddyb) use an usize/fn() array with
421                         // the correct number of vtables slots.
422                         tcx.mk_imm_ref(tcx.types.re_static, tcx.mk_nil())
423                     }
424                     _ => bug!("TyLayout::field_type({:?}): not applicable", layout)
425                 }
426             }
427
428             // Arrays and slices.
429             ty::TyArray(element, _) |
430             ty::TySlice(element) => element,
431             ty::TyStr => tcx.types.u8,
432
433             // Tuples, generators and closures.
434             ty::TyClosure(def_id, ref substs) => {
435                 substs.upvar_tys(def_id, tcx).nth(i).unwrap()
436             }
437
438             ty::TyGenerator(def_id, ref substs, _) => {
439                 substs.field_tys(def_id, tcx).nth(i).unwrap()
440             }
441
442             ty::TyTuple(tys) => tys[i],
443
444             // SIMD vector types.
445             ty::TyAdt(def, ..) if def.repr.simd() => {
446                 layout.ty.simd_type(tcx)
447             }
448
449             // ADTs.
450             ty::TyAdt(def, substs) => {
451                 use rustc::ty::layout::Variants;
452                 match layout.variants {
453                     Variants::Single { index } => {
454                         def.variants[index].fields[i].ty(tcx, substs)
455                     }
456
457                     // Discriminant field for enums (where applicable).
458                     Variants::Tagged { ref discr, .. } |
459                     Variants::NicheFilling { niche: ref discr, .. } => {
460                         assert_eq!(i, 0);
461                         return Ok(discr.value.to_ty(tcx))
462                     }
463                 }
464             }
465
466             ty::TyProjection(_) | ty::TyAnon(..) | ty::TyParam(_) |
467             ty::TyInfer(_) | ty::TyError => {
468                 bug!("TyLayout::field_type: unexpected type `{}`", layout.ty)
469             }
470         })
471     }
472
473     fn validate_fields(
474         &mut self,
475         query: ValidationQuery<'tcx>,
476         mode: ValidationMode,
477     ) -> EvalResult<'tcx> {
478         let mut layout = self.layout_of(query.ty)?;
479         layout.ty = query.ty;
480
481         // TODO: Maybe take visibility/privacy into account.
482         for idx in 0..layout.fields.count() {
483             let field = mir::Field::new(idx);
484             let (field_place, field_layout) =
485                 self.place_field(query.place.1, field, layout)?;
486             // layout stuff erases lifetimes, get the field ourselves
487             let field_ty = self.field_with_lifetimes(query.place.1, layout, idx)?;
488             trace!("assuming \n{:?}\n == \n{:?}\n except for lifetimes", field_layout.ty, field_ty);
489             self.validate(
490                 ValidationQuery {
491                     place: (query.place.0.clone().field(field), field_place),
492                     ty: field_ty,
493                     ..query
494                 },
495                 mode,
496             )?;
497         }
498
499         Ok(())
500     }
501
502     fn validate_ptr(
503         &mut self,
504         val: Value,
505         abs_place: AbsPlace<'tcx>,
506         pointee_ty: Ty<'tcx>,
507         re: Option<region::Scope>,
508         mutbl: Mutability,
509         mode: ValidationMode,
510     ) -> EvalResult<'tcx> {
511         // Check alignment and non-NULLness
512         let (_, align) = self.size_and_align_of_dst(pointee_ty, val)?;
513         let ptr = self.into_ptr(val)?;
514         self.memory.check_align(ptr, align)?;
515
516         // Recurse
517         let pointee_place = self.val_to_place(val, pointee_ty)?;
518         self.validate(
519             ValidationQuery {
520                 place: (abs_place.deref(), pointee_place),
521                 ty: pointee_ty,
522                 re,
523                 mutbl,
524             },
525             mode,
526         )
527     }
528
529     /// Validate the place at the given type. If `acquire` is false, just do a release of all write locks
530     fn validate(
531         &mut self,
532         mut query: ValidationQuery<'tcx>,
533         mode: ValidationMode,
534     ) -> EvalResult<'tcx> {
535         use rustc::ty::TypeVariants::*;
536         use rustc::ty::RegionKind::*;
537         use rustc::ty::AdtKind;
538
539         // No point releasing shared stuff.
540         if !mode.acquiring() && query.mutbl == MutImmutable {
541             return Ok(());
542         }
543         // When we recover, we may see data whose validity *just* ended.  Do not acquire it.
544         if let ValidationMode::Recover(ending_ce) = mode {
545             if query.re == Some(ending_ce) {
546                 return Ok(());
547             }
548         }
549
550         query.ty = self.normalize_type_unerased(&query.ty);
551         trace!("{:?} on {:#?}", mode, query);
552         trace!("{:#?}", query.ty.sty);
553
554         // Decide whether this type *owns* the memory it covers (like integers), or whether it
555         // just assembles pieces (that each own their memory) together to a larger whole.
556         // TODO: Currently, we don't acquire locks for padding and discriminants. We should.
557         let is_owning = match query.ty.sty {
558             TyInt(_) | TyUint(_) | TyRawPtr(_) | TyBool | TyFloat(_) | TyChar | TyStr |
559             TyRef(..) | TyFnPtr(..) | TyFnDef(..) | TyNever => true,
560             TyAdt(adt, _) if adt.is_box() => true,
561             TySlice(_) | TyAdt(_, _) | TyTuple(..) | TyClosure(..) | TyArray(..) |
562             TyDynamic(..) | TyGenerator(..) | TyForeign(_) => false,
563             TyGeneratorWitness(..) => bug!("I'm not sure what to return here"),
564             TyParam(_) | TyInfer(_) | TyProjection(_) | TyAnon(..) | TyError => {
565                 bug!("I got an incomplete/unnormalized type for validation")
566             }
567         };
568         if is_owning {
569             // We need to lock.  So we need memory.  So we have to force_acquire.
570             // Tracking the same state for locals not backed by memory would just duplicate too
571             // much machinery.
572             // FIXME: We ignore alignment.
573             let (ptr, _, extra) = self.force_allocation(query.place.1)?.to_ptr_align_extra();
574             // Determine the size
575             // FIXME: Can we reuse size_and_align_of_dst for Places?
576             let layout = self.layout_of(query.ty)?;
577             let len = if !layout.is_unsized() {
578                 assert_eq!(extra, PlaceExtra::None, "Got a fat ptr to a sized type");
579                 layout.size.bytes()
580             } else {
581                 // The only unsized typ we concider "owning" is TyStr.
582                 assert_eq!(
583                     query.ty.sty,
584                     TyStr,
585                     "Found a surprising unsized owning type"
586                 );
587                 // The extra must be the length, in bytes.
588                 match extra {
589                     PlaceExtra::Length(len) => len,
590                     _ => bug!("TyStr must have a length as extra"),
591                 }
592             };
593             // Handle locking
594             if len > 0 {
595                 let ptr = ptr.to_ptr()?;
596                 match query.mutbl {
597                     MutImmutable => {
598                         if mode.acquiring() {
599                             self.memory.acquire_lock(
600                                 ptr,
601                                 len,
602                                 query.re,
603                                 AccessKind::Read,
604                             )?;
605                         }
606                     }
607                     // No releasing of read locks, ever.
608                     MutMutable => {
609                         match mode {
610                             ValidationMode::Acquire => {
611                                 self.memory.acquire_lock(
612                                     ptr,
613                                     len,
614                                     query.re,
615                                     AccessKind::Write,
616                                 )?
617                             }
618                             ValidationMode::Recover(ending_ce) => {
619                                 self.memory.recover_write_lock(
620                                     ptr,
621                                     len,
622                                     &query.place.0,
623                                     query.re,
624                                     ending_ce,
625                                 )?
626                             }
627                             ValidationMode::ReleaseUntil(suspended_ce) => {
628                                 self.memory.suspend_write_lock(
629                                     ptr,
630                                     len,
631                                     &query.place.0,
632                                     suspended_ce,
633                                 )?
634                             }
635                         }
636                     }
637                 }
638             }
639         }
640
641         let res = do catch {
642             match query.ty.sty {
643                 TyInt(_) | TyUint(_) | TyRawPtr(_) => {
644                     if mode.acquiring() {
645                         // Make sure we can read this.
646                         let val = self.read_place(query.place.1)?;
647                         self.follow_by_ref_value(val, query.ty)?;
648                         // FIXME: It would be great to rule out Undef here, but that doesn't actually work.
649                         // Passing around undef data is a thing that e.g. Vec::extend_with does.
650                     }
651                     Ok(())
652                 }
653                 TyBool | TyFloat(_) | TyChar => {
654                     if mode.acquiring() {
655                         let val = self.read_place(query.place.1)?;
656                         let val = self.value_to_primval(ValTy { value: val, ty: query.ty })?;
657                         val.to_bytes()?;
658                         // TODO: Check if these are valid bool/float/codepoint/UTF-8
659                     }
660                     Ok(())
661                 }
662                 TyNever => err!(ValidationFailure(format!("The empty type is never valid."))),
663                 TyRef(region,
664                     ty::TypeAndMut {
665                         ty: pointee_ty,
666                         mutbl,
667                     }) => {
668                     let val = self.read_place(query.place.1)?;
669                     // Sharing restricts our context
670                     if mutbl == MutImmutable {
671                         query.mutbl = MutImmutable;
672                     }
673                     // Inner lifetimes *outlive* outer ones, so only if we have no lifetime restriction yet,
674                     // we record the region of this borrow to the context.
675                     if query.re == None {
676                         match *region {
677                             ReScope(scope) => query.re = Some(scope),
678                             // It is possible for us to encounter erased lifetimes here because the lifetimes in
679                             // this functions' Subst will be erased.
680                             _ => {}
681                         }
682                     }
683                     self.validate_ptr(val, query.place.0, pointee_ty, query.re, query.mutbl, mode)
684                 }
685                 TyAdt(adt, _) if adt.is_box() => {
686                     let val = self.read_place(query.place.1)?;
687                     self.validate_ptr(val, query.place.0, query.ty.boxed_ty(), query.re, query.mutbl, mode)
688                 }
689                 TyFnPtr(_sig) => {
690                     let ptr = self.read_place(query.place.1)?;
691                     let ptr = self.into_ptr(ptr)?.to_ptr()?;
692                     self.memory.get_fn(ptr)?;
693                     // TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
694                     Ok(())
695                 }
696                 TyFnDef(..) => {
697                     // This is a zero-sized type with all relevant data sitting in the type.
698                     // There is nothing to validate.
699                     Ok(())
700                 }
701
702                 // Compound types
703                 TyStr => {
704                     // TODO: Validate strings
705                     Ok(())
706                 }
707                 TySlice(elem_ty) => {
708                     let len = match query.place.1 {
709                         Place::Ptr { extra: PlaceExtra::Length(len), .. } => len,
710                         _ => {
711                             bug!(
712                                 "acquire_valid of a TySlice given non-slice place: {:?}",
713                                 query.place
714                             )
715                         }
716                     };
717                     for i in 0..len {
718                         let inner_place = self.place_index(query.place.1, query.ty, i)?;
719                         self.validate(
720                             ValidationQuery {
721                                 place: (query.place.0.clone().index(i), inner_place),
722                                 ty: elem_ty,
723                                 ..query
724                             },
725                             mode,
726                         )?;
727                     }
728                     Ok(())
729                 }
730                 TyArray(elem_ty, len) => {
731                     let len_val = match len.val {
732                         ConstVal::Unevaluated(def_id, substs) => {
733                             eval_body(self.tcx.tcx, GlobalId {
734                                 instance: Instance::new(def_id, substs),
735                                 promoted: None,
736                             }, ty::ParamEnv::reveal_all())
737                                 .ok_or_else(||EvalErrorKind::MachineError("<already reported>".to_string()))?
738                                 .0
739                         }
740                         ConstVal::Value(val) => val,
741                     };
742                     let len = ConstVal::Value(len_val).unwrap_u64();
743                     for i in 0..len {
744                         let inner_place = self.place_index(query.place.1, query.ty, i as u64)?;
745                         self.validate(
746                             ValidationQuery {
747                                 place: (query.place.0.clone().index(i as u64), inner_place),
748                                 ty: elem_ty,
749                                 ..query
750                             },
751                             mode,
752                         )?;
753                     }
754                     Ok(())
755                 }
756                 TyDynamic(_data, _region) => {
757                     // Check that this is a valid vtable
758                     let vtable = match query.place.1 {
759                         Place::Ptr { extra: PlaceExtra::Vtable(vtable), .. } => vtable,
760                         _ => {
761                             bug!(
762                                 "acquire_valid of a TyDynamic given non-trait-object place: {:?}",
763                                 query.place
764                             )
765                         }
766                     };
767                     self.read_size_and_align_from_vtable(vtable)?;
768                     // TODO: Check that the vtable contains all the function pointers we expect it to have.
769                     // Trait objects cannot have any operations performed
770                     // on them directly.  We cannot, in general, even acquire any locks as the trait object *could*
771                     // contain an UnsafeCell.  If we call functions to get access to data, we will validate
772                     // their return values.  So, it doesn't seem like there's anything else to do.
773                     Ok(())
774                 }
775                 TyAdt(adt, _) => {
776                     if Some(adt.did) == self.tcx.tcx.lang_items().unsafe_cell_type() &&
777                         query.mutbl == MutImmutable
778                     {
779                         // No locks for shared unsafe cells.  Also no other validation, the only field is private anyway.
780                         return Ok(());
781                     }
782
783                     match adt.adt_kind() {
784                         AdtKind::Enum => {
785                             let discr = self.read_discriminant_value(query.place.1, query.ty)?;
786
787                             // Get variant index for discriminant
788                             let variant_idx = adt.discriminants(self.tcx.tcx).position(|variant_discr| {
789                                 variant_discr.val == discr
790                             });
791                             let variant_idx = match variant_idx {
792                                 Some(val) => val,
793                                 None => return err!(InvalidDiscriminant),
794                             };
795                             let variant = &adt.variants[variant_idx];
796
797                             if variant.fields.len() > 0 {
798                                 // Downcast to this variant, if needed
799                                 let place = if adt.is_enum() {
800                                     (
801                                         query.place.0.downcast(adt, variant_idx),
802                                         self.eval_place_projection(
803                                             query.place.1,
804                                             query.ty,
805                                             &mir::ProjectionElem::Downcast(adt, variant_idx),
806                                         )?,
807                                     )
808                                 } else {
809                                     query.place
810                                 };
811
812                                 // Recursively validate the fields
813                                 self.validate_fields(
814                                     ValidationQuery { place, ..query },
815                                     mode,
816                                 )
817                             } else {
818                                 // No fields, nothing left to check.  Downcasting may fail, e.g. in case of a CEnum.
819                                 Ok(())
820                             }
821                         }
822                         AdtKind::Struct => {
823                             self.validate_fields(query, mode)
824                         }
825                         AdtKind::Union => {
826                             // No guarantees are provided for union types.
827                             // TODO: Make sure that all access to union fields is unsafe; otherwise, we may have some checking to do (but what exactly?)
828                             Ok(())
829                         }
830                     }
831                 }
832                 TyTuple(..) |
833                 TyClosure(..) => {
834                     // TODO: Check if the signature matches for `TyClosure`
835                     // (should be the same check as what terminator/mod.rs already does on call?).
836                     // Is there other things we can/should check?  Like vtable pointers?
837                     self.validate_fields(query, mode)
838                 }
839                 // FIXME: generators aren't validated right now
840                 TyGenerator(..) => Ok(()),
841                 _ => bug!("We already established that this is a type we support. ({})", query.ty),
842             }
843         };
844         match res {
845             // ReleaseUntil(None) of an uninitalized variable is a NOP.  This is needed because
846             // we have to release the return value of a function; due to destination-passing-style
847             // the callee may directly write there.
848             // TODO: Ideally we would know whether the destination is already initialized, and only
849             // release if it is.  But of course that can't even always be statically determined.
850             Err(EvalError { kind: EvalErrorKind::ReadUndefBytes, .. })
851                 if mode == ValidationMode::ReleaseUntil(None) => {
852                 return Ok(());
853             }
854             res => res,
855         }
856     }
857 }