]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/validation.rs
Handle trait objects. Only very superficial checking of the vtable for now. (88)
[rust.git] / src / librustc_mir / interpret / validation.rs
1 use rustc::hir::Mutability;
2 use rustc::mir::{self, ValidationOp};
3 use rustc::ty::{self, Ty};
4 use rustc::middle::region::CodeExtent;
5
6 use error::{EvalError, EvalResult};
7 use eval_context::{EvalContext};
8 use memory::AccessKind;
9 use value::Value;
10 use lvalue::{Lvalue, LvalueExtra};
11
12 // Validity checks
13 #[derive(Copy, Clone, Debug)]
14 pub struct ValidationCtx {
15     op: ValidationOp,
16     region: Option<CodeExtent>,
17     mutbl: Mutability,
18 }
19
20 impl ValidationCtx {
21     pub fn new(op: ValidationOp) -> Self {
22         ValidationCtx {
23             op, region: None, mutbl: Mutability::MutMutable,
24         }
25     }
26 }
27
28 impl<'a, 'tcx> EvalContext<'a, 'tcx> {
29     fn validate_variant(
30         &mut self,
31         lvalue: Lvalue<'tcx>,
32         ty: Ty<'tcx>,
33         variant: &ty::VariantDef,
34         subst: &ty::subst::Substs<'tcx>,
35         vctx: ValidationCtx,
36     ) -> EvalResult<'tcx> {
37         // TODO: Take visibility/privacy into account.
38         for (idx, field) in variant.fields.iter().enumerate() {
39             let field_ty = field.ty(self.tcx, subst);
40             let field_lvalue = self.lvalue_field(lvalue, idx, ty, field_ty)?;
41             self.validate(field_lvalue, field_ty, vctx)?;
42         }
43         Ok(())
44     }
45
46     fn validate_ptr(&mut self, val: Value, pointee_ty: Ty<'tcx>, vctx: ValidationCtx) -> EvalResult<'tcx> {
47         // Check alignment and non-NULLness
48         let (_, align) = self.size_and_align_of_dst(pointee_ty, val)?;
49         let ptr = val.into_ptr(&mut self.memory)?;
50         self.memory.check_align(ptr, align)?;
51
52         // Recurse
53         let pointee_lvalue = self.val_to_lvalue(val, pointee_ty)?;
54         self.validate(pointee_lvalue, pointee_ty, vctx)
55     }
56
57     /// Validate the lvalue at the given type. If `release` is true, just do a release of all write locks
58     pub(super) fn validate(&mut self, lvalue: Lvalue<'tcx>, ty: Ty<'tcx>, mut vctx: ValidationCtx) -> EvalResult<'tcx>
59     {
60         use rustc::ty::TypeVariants::*;
61         use rustc::ty::RegionKind::*;
62         use rustc::ty::AdtKind;
63         use self::Mutability::*;
64         trace!("Validating {:?} at type {}, context {:?}", lvalue, ty, vctx);
65
66         // Decide whether this type *owns* the memory it covers (like integers), or whether it
67         // just assembles pieces (that each own their memory) together to a larger whole.
68         // TODO: Currently, we don't acquire locks for padding and discriminants. We should.
69         let is_owning = match ty.sty {
70             TyInt(_) | TyUint(_) | TyRawPtr(_) |
71             TyBool | TyFloat(_) | TyChar | TyStr |
72             TyRef(..) | TyFnPtr(..) | TyNever => true,
73             TyAdt(adt, _) if adt.is_box() => true,
74             TySlice(_) | TyAdt(_, _) | TyTuple(..) | TyClosure(..) | TyArray(..) | TyDynamic(..) => false,
75             TyParam(_) | TyInfer(_) => bug!("I got an incomplete type for validation"),
76             _ => return Err(EvalError::Unimplemented(format!("Unimplemented type encountered when checking validity."))),
77         };
78         if is_owning {
79             match lvalue {
80                 Lvalue::Ptr { ptr, extra, aligned: _ } => {
81                     // Determine the size
82                     // FIXME: Can we reuse size_and_align_of_dst for Lvalues?
83                     let len = match self.type_size(ty)? {
84                         Some(size) => {
85                             assert_eq!(extra, LvalueExtra::None, "Got a fat ptr to a sized type");
86                             size
87                         }
88                         None => {
89                             // The only unsized typ we concider "owning" is TyStr.
90                             assert_eq!(ty.sty, TyStr, "Found a surprising unsized owning type");
91                             // The extra must be the length, in bytes.
92                             match extra {
93                                 LvalueExtra::Length(len) => len,
94                                 _ => bug!("TyStr must have a length as extra"),
95                             }
96                         }
97                     };
98                     // Handle locking
99                     if len > 0 {
100                         let ptr = ptr.to_ptr()?;
101                         let access = match vctx.mutbl { MutMutable => AccessKind::Write, MutImmutable => AccessKind::Read };
102                         match vctx.op {
103                             ValidationOp::Acquire => self.memory.acquire_lock(ptr, len, vctx.region, access)?,
104                             ValidationOp::Release => self.memory.release_write_lock_until(ptr, len, None)?,
105                             ValidationOp::Suspend(region) => self.memory.release_write_lock_until(ptr, len, Some(region))?,
106                         }
107                     }
108                 }
109                 Lvalue::Local { .. } | Lvalue::Global(..) => {
110                     // These are not backed by memory, so we have nothing to do.
111                 }
112             }
113         }
114
115         match ty.sty {
116             TyInt(_) | TyUint(_) | TyRawPtr(_) => {
117                 // TODO: Make sure these are not undef.
118                 // We could do a bounds-check and other sanity checks on the lvalue, but it would be a bug in miri for this to ever fail.
119                 Ok(())
120             }
121             TyBool | TyFloat(_) | TyChar | TyStr => {
122                 // TODO: Check if these are valid bool/float/codepoint/UTF-8, respectively (and in particular, not undef).
123                 Ok(())
124             }
125             TyNever => {
126                 Err(EvalError::ValidationFailure(format!("The empty type is never valid.")))
127             }
128             TyRef(region, ty::TypeAndMut { ty: pointee_ty, mutbl }) => {
129                 let val = self.read_lvalue(lvalue)?;
130                 // Sharing restricts our context
131                 if mutbl == MutImmutable {
132                     // Actually, in case of releasing-validation, this means we are done.
133                     if vctx.op != ValidationOp::Acquire {
134                         return Ok(());
135                     }
136                     vctx.mutbl = MutImmutable;
137                 }
138                 // Inner lifetimes *outlive* outer ones, so only if we have no lifetime restriction yet,
139                 // we record the region of this borrow to the context.
140                 if vctx.region == None {
141                     match *region {
142                         ReScope(ce) => vctx.region = Some(ce),
143                         // It is possible for us to encode erased lifetimes here because the lifetimes in
144                         // this functions' Subst will be erased.
145                         _ => {},
146                     }
147                 }
148                 self.validate_ptr(val, pointee_ty, vctx)
149             }
150             TyAdt(adt, _) if adt.is_box() => {
151                 let val = self.read_lvalue(lvalue)?;
152                 self.validate_ptr(val, ty.boxed_ty(), vctx)
153             }
154             TyFnPtr(_sig) => {
155                 // TODO: The function names here could need some improvement.
156                 let ptr = self.read_lvalue(lvalue)?.into_ptr(&mut self.memory)?.to_ptr()?;
157                 self.memory.get_fn(ptr)?;
158                 // TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
159                 Ok(())
160             }
161
162             // Compound types
163             TySlice(elem_ty) => {
164                 let len = match lvalue {
165                     Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => len,
166                     _ => bug!("acquire_valid of a TySlice given non-slice lvalue: {:?}", lvalue),
167                 };
168                 for i in 0..len {
169                     let inner_lvalue = self.lvalue_index(lvalue, ty, i)?;
170                     self.validate(inner_lvalue, elem_ty, vctx)?;
171                 }
172                 Ok(())
173             }
174             TyArray(elem_ty, len) => {
175                 for i in 0..len {
176                     let inner_lvalue = self.lvalue_index(lvalue, ty, i as u64)?;
177                     self.validate(inner_lvalue, elem_ty, vctx)?;
178                 }
179                 Ok(())
180             }
181             TyDynamic(_data, _region) => {
182                 // Check that this is a valid vtable
183                 let vtable = match lvalue {
184                     Lvalue::Ptr { extra: LvalueExtra::Vtable(vtable), .. } => vtable,
185                     _ => bug!("acquire_valid of a TyDynamic given non-trait-object lvalue: {:?}", lvalue),
186                 };
187                 self.read_size_and_align_from_vtable(vtable)?;
188                 // TODO: Check that the vtable contains all the function pointers we expect it to have.
189                 // TODO: Is there anything we can/should validate here?  Trait objects cannot have any operations performed
190                 // on them directly.  We cannot, in general, even acquire any locks as the trait object *could*
191                 // contain an UnsafeCell.  If we call functions to get access to data, we will validate
192                 // their return values.  So, it doesn't seem like there's anything to do.
193                 Ok(())
194             }
195             TyAdt(adt, subst) => {
196                 if Some(adt.did) == self.tcx.lang_items.unsafe_cell_type() {
197                     // No locks for unsafe cells.  Also no other validation, the only field is private anyway.
198                     return Ok(());
199                 }
200
201                 match adt.adt_kind() {
202                     AdtKind::Enum => {
203                         // TODO: Can we get the discriminant without forcing an allocation?
204                         let ptr = self.force_allocation(lvalue)?.to_ptr()?;
205                         let discr = self.read_discriminant_value(ptr, ty)?;
206
207                         // Get variant index for discriminant
208                         let variant_idx = adt.discriminants(self.tcx)
209                             .position(|variant_discr| variant_discr.to_u128_unchecked() == discr)
210                             .ok_or(EvalError::InvalidDiscriminant)?;
211                         let variant = &adt.variants[variant_idx];
212
213                         if variant.fields.len() > 0 {
214                             // Downcast to this variant, if needed
215                             let lvalue = if adt.variants.len() > 1 {
216                                 self.eval_lvalue_projection(lvalue, ty, &mir::ProjectionElem::Downcast(adt, variant_idx))?
217                             } else {
218                                 lvalue
219                             };
220
221                             // Recursively validate the fields
222                             self.validate_variant(lvalue, ty, variant, subst, vctx)
223                         } else {
224                             // No fields, nothing left to check.  Downcasting may fail, e.g. in case of a CEnum.
225                             Ok(())
226                         }
227                     }
228                     AdtKind::Struct => {
229                         self.validate_variant(lvalue, ty, adt.struct_variant(), subst, vctx)
230                     }
231                     AdtKind::Union => {
232                         // No guarantees are provided for union types.
233                         // TODO: Make sure that all access to union fields is unsafe; otherwise, we may have some checking to do (but what exactly?)
234                         Ok(())
235                     }
236                 }
237             }
238             TyTuple(ref types, _) => {
239                 for (idx, field_ty) in types.iter().enumerate() {
240                     let field_lvalue = self.lvalue_field(lvalue, idx, ty, field_ty)?;
241                     self.validate(field_lvalue, field_ty, vctx)?;
242                 }
243                 Ok(())
244             }
245             TyClosure(def_id, ref closure_substs) => {
246                 for (idx, field_ty) in closure_substs.upvar_tys(def_id, self.tcx).enumerate() {
247                     let field_lvalue = self.lvalue_field(lvalue, idx, ty, field_ty)?;
248                     self.validate(field_lvalue, field_ty, vctx)?;
249                 }
250                 // TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
251                 // Is there other things we can/should check?  Like vtable pointers?
252                 Ok(())
253             }
254             _ => bug!("We already establishd that this is a type we support.")
255         }
256     }
257 }