]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/validity.rs
7e32f3a971f8e91e5ddae256881168a6c775b33b
[rust.git] / src / librustc_mir / interpret / validity.rs
1 use std::fmt::Write;
2
3 use syntax_pos::symbol::Symbol;
4 use rustc::ty::layout::{self, Size, Primitive};
5 use rustc::ty::{self, Ty};
6 use rustc_data_structures::fx::FxHashSet;
7 use rustc::mir::interpret::{
8     Scalar, AllocType, EvalResult, ScalarMaybeUndef,
9 };
10
11 use super::{
12     MPlaceTy, PlaceExtra, Machine, EvalContext
13 };
14
15 macro_rules! validation_failure{
16     ($what:expr, $where:expr, $details:expr) => {{
17         let where_ = path_format($where);
18         let where_ = if where_.is_empty() {
19             String::new()
20         } else {
21             format!(" at {}", where_)
22         };
23         err!(ValidationFailure(format!(
24             "encountered {}{}, but expected {}",
25             $what, where_, $details,
26         )))
27     }};
28     ($what:expr, $where:expr) => {{
29         let where_ = path_format($where);
30         let where_ = if where_.is_empty() {
31             String::new()
32         } else {
33             format!(" at {}", where_)
34         };
35         err!(ValidationFailure(format!(
36             "encountered {}{}",
37             $what, where_,
38         )))
39     }};
40 }
41
42 /// We want to show a nice path to the invalid field for diagnotsics,
43 /// but avoid string operations in the happy case where no error happens.
44 /// So we track a `Vec<PathElem>` where `PathElem` contains all the data we
45 /// need to later print something for the user.
46 #[derive(Copy, Clone, Debug)]
47 pub enum PathElem {
48     Field(Symbol),
49     ClosureVar(Symbol),
50     ArrayElem(usize),
51     TupleElem(usize),
52     Deref,
53     Tag,
54 }
55
56 // Adding a Deref and making a copy of the path to be put into the queue
57 // always go together.  This one does it with only new allocation.
58 fn path_clone_and_deref(path: &Vec<PathElem>) -> Vec<PathElem> {
59     let mut new_path = Vec::with_capacity(path.len()+1);
60     new_path.clone_from(path);
61     new_path.push(PathElem::Deref);
62     new_path
63 }
64
65 /// Format a path
66 fn path_format(path: &Vec<PathElem>) -> String {
67     use self::PathElem::*;
68
69     let mut out = String::new();
70     for elem in path.iter() {
71         match elem {
72             Field(name) => write!(out, ".{}", name).unwrap(),
73             ClosureVar(name) => write!(out, ".<closure-var({})>", name).unwrap(),
74             TupleElem(idx) => write!(out, ".{}", idx).unwrap(),
75             ArrayElem(idx) => write!(out, "[{}]", idx).unwrap(),
76             Deref =>
77                 // This does not match Rust syntax, but it is more readable for long paths -- and
78                 // some of the other items here also are not Rust syntax.  Actually we can't
79                 // even use the usual syntax because we are just showing the projections,
80                 // not the root.
81                 write!(out, ".<deref>").unwrap(),
82             Tag => write!(out, ".<enum-tag>").unwrap(),
83         }
84     }
85     out
86 }
87
88 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
89     fn validate_scalar(
90         &self,
91         value: ScalarMaybeUndef,
92         size: Size,
93         scalar: &layout::Scalar,
94         path: &Vec<PathElem>,
95         ty: Ty,
96     ) -> EvalResult<'tcx> {
97         trace!("validate scalar: {:#?}, {:#?}, {:#?}, {}", value, size, scalar, ty);
98         let (lo, hi) = scalar.valid_range.clone().into_inner();
99
100         let value = match value {
101             ScalarMaybeUndef::Scalar(scalar) => scalar,
102             ScalarMaybeUndef::Undef => return validation_failure!("undefined bytes", path),
103         };
104
105         let bits = match value {
106             Scalar::Bits { bits, size: value_size } => {
107                 assert_eq!(value_size as u64, size.bytes());
108                 bits
109             },
110             Scalar::Ptr(_) => {
111                 let ptr_size = self.memory.pointer_size();
112                 let ptr_max = u128::max_value() >> (128 - ptr_size.bits());
113                 return if lo > hi {
114                     if lo - hi == 1 {
115                         // no gap, all values are ok
116                         Ok(())
117                     } else if hi < ptr_max || lo > 1 {
118                         let max = u128::max_value() >> (128 - size.bits());
119                         validation_failure!(
120                             "pointer",
121                             path,
122                             format!("something in the range {:?} or {:?}", 0..=lo, hi..=max)
123                         )
124                     } else {
125                         Ok(())
126                     }
127                 } else if hi < ptr_max || lo > 1 {
128                     validation_failure!(
129                         "pointer",
130                         path,
131                         format!("something in the range {:?}", scalar.valid_range)
132                     )
133                 } else {
134                     Ok(())
135                 };
136             },
137         };
138
139         // char gets a special treatment, because its number space is not contiguous so `TyLayout`
140         // has no special checks for chars
141         match ty.sty {
142             ty::TyChar => {
143                 debug_assert_eq!(size.bytes(), 4);
144                 if ::std::char::from_u32(bits as u32).is_none() {
145                     return validation_failure!(
146                         "character",
147                         path,
148                         "a valid unicode codepoint"
149                     );
150                 }
151             }
152             _ => {},
153         }
154
155         use std::ops::RangeInclusive;
156         let in_range = |bound: RangeInclusive<u128>| bound.contains(&bits);
157         if lo > hi {
158             if in_range(0..=hi) || in_range(lo..=u128::max_value()) {
159                 Ok(())
160             } else {
161                 validation_failure!(
162                     bits,
163                     path,
164                     format!("something in the range {:?} or {:?}", ..=hi, lo..)
165                 )
166             }
167         } else {
168             if in_range(scalar.valid_range.clone()) {
169                 Ok(())
170             } else {
171                 validation_failure!(
172                     bits,
173                     path,
174                     format!("something in the range {:?}", scalar.valid_range)
175                 )
176             }
177         }
178     }
179
180     /// This function checks the memory where `dest` points to.  The place must be sized
181     /// (i.e., dest.extra == PlaceExtra::None).
182     /// It will error if the bits at the destination do not match the ones described by the layout.
183     /// The `path` may be pushed to, but the part that is present when the function
184     /// starts must not be changed!
185     pub fn validate_mplace(
186         &self,
187         dest: MPlaceTy<'tcx>,
188         path: &mut Vec<PathElem>,
189         seen: &mut FxHashSet<(MPlaceTy<'tcx>)>,
190         todo: &mut Vec<(MPlaceTy<'tcx>, Vec<PathElem>)>,
191     ) -> EvalResult<'tcx> {
192         self.memory.dump_alloc(dest.to_ptr()?.alloc_id);
193         trace!("validate_mplace: {:?}, {:#?}", *dest, dest.layout);
194
195         // Find the right variant.  We have to handle this as a prelude, not via
196         // proper recursion with the new inner layout, to be able to later nicely
197         // print the field names of the enum field that is being accessed.
198         let (variant, dest) = match dest.layout.variants {
199             layout::Variants::NicheFilling { niche: ref tag, .. } |
200             layout::Variants::Tagged { ref tag, .. } => {
201                 let size = tag.value.size(self);
202                 // we first read the tag value as scalar, to be able to validate it
203                 let tag_mplace = self.mplace_field(dest, 0)?;
204                 let tag_value = self.read_scalar(tag_mplace.into())?;
205                 path.push(PathElem::Tag);
206                 self.validate_scalar(
207                     tag_value, size, tag, &path, tag_mplace.layout.ty
208                 )?;
209                 path.pop(); // remove the element again
210                 // then we read it again to get the index, to continue
211                 let variant = self.read_discriminant_as_variant_index(dest.into())?;
212                 let inner_dest = self.mplace_downcast(dest, variant)?;
213                 // Put the variant projection onto the path, as a field
214                 path.push(PathElem::Field(dest.layout.ty.ty_adt_def().unwrap().variants[variant].name));
215                 trace!("variant layout: {:#?}", dest.layout);
216                 (variant, inner_dest)
217             },
218             layout::Variants::Single { index } => {
219                 (index, dest)
220             }
221         };
222
223         // Remember the length, in case we need to truncate
224         let path_len = path.len();
225
226         // Validate all fields
227         match dest.layout.fields {
228             // primitives are unions with zero fields
229             layout::FieldPlacement::Union(0) => {
230                 match dest.layout.abi {
231                     // nothing to do, whatever the pointer points to, it is never going to be read
232                     layout::Abi::Uninhabited =>
233                         return validation_failure!("a value of an uninhabited type", path),
234                     // check that the scalar is a valid pointer or that its bit range matches the
235                     // expectation.
236                     layout::Abi::Scalar(ref scalar_layout) => {
237                         let size = scalar_layout.value.size(self);
238                         let value = self.read_value(dest.into())?;
239                         let scalar = value.to_scalar_or_undef();
240                         self.validate_scalar(scalar, size, scalar_layout, &path, dest.layout.ty)?;
241                         if scalar_layout.value == Primitive::Pointer {
242                             // ignore integer pointers, we can't reason about the final hardware
243                             if let Scalar::Ptr(ptr) = scalar.not_undef()? {
244                                 let alloc_kind = self.tcx.alloc_map.lock().get(ptr.alloc_id);
245                                 if let Some(AllocType::Static(did)) = alloc_kind {
246                                     // statics from other crates are already checked.
247                                     // extern statics should not be validated as they have no body.
248                                     if !did.is_local() || self.tcx.is_foreign_item(did) {
249                                         return Ok(());
250                                     }
251                                 }
252                                 if value.layout.ty.builtin_deref(false).is_some() {
253                                     trace!("Recursing below ptr {:#?}", value);
254                                     let ptr_place = self.ref_to_mplace(value)?;
255                                     // we have not encountered this pointer+layout combination before
256                                     if seen.insert(ptr_place) {
257                                         todo.push((ptr_place, path_clone_and_deref(path)));
258                                     }
259                                 }
260                             }
261                         }
262                     },
263                     _ => bug!("bad abi for FieldPlacement::Union(0): {:#?}", dest.layout.abi),
264                 }
265             }
266             layout::FieldPlacement::Union(_) => {
267                 // We can't check unions, their bits are allowed to be anything.
268                 // The fields don't need to correspond to any bit pattern of the union's fields.
269                 // See https://github.com/rust-lang/rust/issues/32836#issuecomment-406875389
270             },
271             layout::FieldPlacement::Array { .. } => {
272                 for (i, field) in self.mplace_array_fields(dest)?.enumerate() {
273                     let field = field?;
274                     path.push(PathElem::ArrayElem(i));
275                     self.validate_mplace(field, path, seen, todo)?;
276                     path.truncate(path_len);
277                 }
278             },
279             layout::FieldPlacement::Arbitrary { ref offsets, .. } => {
280                 // fat pointers need special treatment
281                 match dest.layout.ty.builtin_deref(false).map(|tam| &tam.ty.sty) {
282                     | Some(ty::TyStr)
283                     | Some(ty::TySlice(_)) => {
284                         // check the length (for nicer error messages)
285                         let len_mplace = self.mplace_field(dest, 1)?;
286                         let len = self.read_scalar(len_mplace.into())?;
287                         let len = match len.to_bits(len_mplace.layout.size) {
288                             Err(_) => return validation_failure!("length is not a valid integer", path),
289                             Ok(len) => len as u64,
290                         };
291                         // get the fat ptr, and recursively check it
292                         let ptr = self.ref_to_mplace(self.read_value(dest.into())?)?;
293                         assert_eq!(ptr.extra, PlaceExtra::Length(len));
294                         let unpacked_ptr = self.unpack_unsized_mplace(ptr)?;
295                         if seen.insert(unpacked_ptr) {
296                             todo.push((unpacked_ptr, path_clone_and_deref(path)));
297                         }
298                     },
299                     Some(ty::TyDynamic(..)) => {
300                         // check the vtable (for nicer error messages)
301                         let vtable = self.read_scalar(self.mplace_field(dest, 1)?.into())?;
302                         let vtable = match vtable.to_ptr() {
303                             Err(_) => return validation_failure!("vtable address is not a pointer", path),
304                             Ok(vtable) => vtable,
305                         };
306                         // get the fat ptr, and recursively check it
307                         let ptr = self.ref_to_mplace(self.read_value(dest.into())?)?;
308                         assert_eq!(ptr.extra, PlaceExtra::Vtable(vtable));
309                         let unpacked_ptr = self.unpack_unsized_mplace(ptr)?;
310                         if seen.insert(unpacked_ptr) {
311                             todo.push((unpacked_ptr, path_clone_and_deref(path)));
312                         }
313                         // FIXME: More checks for the vtable... making sure it is exactly
314                         // the one one would expect for this type.
315                     },
316                     Some(ty) =>
317                         bug!("Unexpected fat pointer target type {:?}", ty),
318                     None => {
319                         // Not a pointer, perform regular aggregate handling below
320                         for i in 0..offsets.len() {
321                             let field = self.mplace_field(dest, i as u64)?;
322                             path.push(self.aggregate_field_path_elem(dest.layout.ty, variant, i));
323                             self.validate_mplace(field, path, seen, todo)?;
324                             path.truncate(path_len);
325                         }
326                         // FIXME: For a TyStr, check that this is valid UTF-8.
327                     },
328                 }
329             }
330         }
331         Ok(())
332     }
333
334     fn aggregate_field_path_elem(&self, ty: Ty<'tcx>, variant: usize, field: usize) -> PathElem {
335         match ty.sty {
336             // generators and closures.
337             ty::TyClosure(def_id, _) | ty::TyGenerator(def_id, _, _) => {
338                 let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
339                 let freevar = self.tcx.with_freevars(node_id, |fv| fv[field]);
340                 PathElem::ClosureVar(self.tcx.hir.name(freevar.var_id()))
341             }
342
343             // tuples
344             ty::TyTuple(_) => PathElem::TupleElem(field),
345
346             // enums
347             ty::TyAdt(def, ..) if def.is_enum() => {
348                 let variant = &def.variants[variant];
349                 PathElem::Field(variant.fields[field].ident.name)
350             }
351
352             // other ADTs
353             ty::TyAdt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name),
354
355             // nothing else has an aggregate layout
356             _ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", ty),
357         }
358     }
359 }