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,
12 MPlaceTy, PlaceExtra, Machine, EvalContext
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() {
21 format!(" at {}", where_)
23 err!(ValidationFailure(format!(
24 "encountered {}{}, but expected {}",
25 $what, where_, $details,
28 ($what:expr, $where:expr) => {{
29 let where_ = path_format($where);
30 let where_ = if where_.is_empty() {
33 format!(" at {}", where_)
35 err!(ValidationFailure(format!(
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)]
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);
66 fn path_format(path: &Vec<PathElem>) -> String {
67 use self::PathElem::*;
69 let mut out = String::new();
70 for elem in path.iter() {
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(),
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,
81 write!(out, ".<deref>").unwrap(),
82 Tag => write!(out, ".<enum-tag>").unwrap(),
88 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
91 value: ScalarMaybeUndef,
93 scalar: &layout::Scalar,
96 ) -> EvalResult<'tcx> {
97 trace!("validate scalar: {:#?}, {:#?}, {:#?}, {}", value, size, scalar, ty);
98 let (lo, hi) = scalar.valid_range.clone().into_inner();
100 let value = match value {
101 ScalarMaybeUndef::Scalar(scalar) => scalar,
102 ScalarMaybeUndef::Undef => return validation_failure!("undefined bytes", path),
105 let bits = match value {
106 Scalar::Bits { bits, size: value_size } => {
107 assert_eq!(value_size as u64, size.bytes());
111 let ptr_size = self.memory.pointer_size();
112 let ptr_max = u128::max_value() >> (128 - ptr_size.bits());
115 // no gap, all values are ok
117 } else if hi < ptr_max || lo > 1 {
118 let max = u128::max_value() >> (128 - size.bits());
122 format!("something in the range {:?} or {:?}", 0..=lo, hi..=max)
127 } else if hi < ptr_max || lo > 1 {
131 format!("something in the range {:?}", scalar.valid_range)
139 // char gets a special treatment, because its number space is not contiguous so `TyLayout`
140 // has no special checks for chars
143 debug_assert_eq!(size.bytes(), 4);
144 if ::std::char::from_u32(bits as u32).is_none() {
145 return validation_failure!(
148 "a valid unicode codepoint"
155 use std::ops::RangeInclusive;
156 let in_range = |bound: RangeInclusive<u128>| bound.contains(&bits);
158 if in_range(0..=hi) || in_range(lo..=u128::max_value()) {
164 format!("something in the range {:?} or {:?}", ..=hi, lo..)
168 if in_range(scalar.valid_range.clone()) {
174 format!("something in the range {:?}", scalar.valid_range)
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(
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);
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
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)
218 layout::Variants::Single { index } => {
223 // Remember the length, in case we need to truncate
224 let path_len = path.len();
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
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) {
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)));
263 _ => bug!("bad abi for FieldPlacement::Union(0): {:#?}", dest.layout.abi),
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
271 layout::FieldPlacement::Array { .. } => {
272 for (i, field) in self.mplace_array_fields(dest)?.enumerate() {
274 path.push(PathElem::ArrayElem(i));
275 self.validate_mplace(field, path, seen, todo)?;
276 path.truncate(path_len);
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) {
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,
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)));
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,
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)));
313 // FIXME: More checks for the vtable... making sure it is exactly
314 // the one one would expect for this type.
317 bug!("Unexpected fat pointer target type {:?}", ty),
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);
326 // FIXME: For a TyStr, check that this is valid UTF-8.
334 fn aggregate_field_path_elem(&self, ty: Ty<'tcx>, variant: usize, field: usize) -> PathElem {
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()))
344 ty::TyTuple(_) => PathElem::TupleElem(field),
347 ty::TyAdt(def, ..) if def.is_enum() => {
348 let variant = &def.variants[variant];
349 PathElem::Field(variant.fields[field].ident.name)
353 ty::TyAdt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name),
355 // nothing else has an aggregate layout
356 _ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", ty),