]>
Commit | Line | Data |
---|---|---|
e1599b0c XL |
1 | //! Check the validity invariant of a given value, and tell the user |
2 | //! where in the value it got violated. | |
3 | //! In const context, this goes even further and tries to approximate const safety. | |
4 | //! That's useful because it means other passes (e.g. promotion) can rely on `const`s | |
5 | //! to be const-safe. | |
6 | ||
ba9703b0 | 7 | use std::convert::TryFrom; |
b7449926 | 8 | use std::fmt::Write; |
ba9703b0 | 9 | use std::num::NonZeroUsize; |
a1dfa0c6 | 10 | use std::ops::RangeInclusive; |
b7449926 | 11 | |
b7449926 | 12 | use rustc_data_structures::fx::FxHashSet; |
dfeec247 | 13 | use rustc_hir as hir; |
6a06907d | 14 | use rustc_middle::mir::interpret::InterpError; |
ba9703b0 XL |
15 | use rustc_middle::ty; |
16 | use rustc_middle::ty::layout::TyAndLayout; | |
dfeec247 | 17 | use rustc_span::symbol::{sym, Symbol}; |
f9f354fc | 18 | use rustc_target::abi::{Abi, LayoutOf, Scalar, Size, VariantIdx, Variants}; |
b7449926 | 19 | |
dc9dc135 XL |
20 | use std::hash::Hash; |
21 | ||
b7449926 | 22 | use super::{ |
dfeec247 | 23 | CheckInAllocMsg, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, |
6a06907d | 24 | ScalarMaybeUninit, ValueVisitor, |
b7449926 XL |
25 | }; |
26 | ||
416331ca | 27 | macro_rules! throw_validation_failure { |
f9f354fc | 28 | ($where:expr, { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )?) => {{ |
1b1a35ee XL |
29 | let msg = rustc_middle::ty::print::with_no_trimmed_paths(|| { |
30 | let mut msg = String::new(); | |
31 | msg.push_str("encountered "); | |
32 | write!(&mut msg, $($what_fmt),+).unwrap(); | |
33 | let where_ = &$where; | |
34 | if !where_.is_empty() { | |
35 | msg.push_str(" at "); | |
36 | write_path(&mut msg, where_); | |
37 | } | |
38 | $( | |
39 | msg.push_str(", but expected "); | |
40 | write!(&mut msg, $($expected_fmt),+).unwrap(); | |
41 | )? | |
42 | ||
43 | msg | |
44 | }); | |
ba9703b0 | 45 | throw_ub!(ValidationFailure(msg)) |
b7449926 XL |
46 | }}; |
47 | } | |
48 | ||
f9f354fc XL |
49 | /// If $e throws an error matching the pattern, throw a validation failure. |
50 | /// Other errors are passed back to the caller, unchanged -- and if they reach the root of | |
51 | /// the visitor, we make sure only validation errors and `InvalidProgram` errors are left. | |
f035d41b | 52 | /// This lets you use the patterns as a kind of validation list, asserting which errors |
f9f354fc XL |
53 | /// can possibly happen: |
54 | /// | |
55 | /// ``` | |
56 | /// let v = try_validation!(some_fn(), some_path, { | |
57 | /// Foo | Bar | Baz => { "some failure" }, | |
58 | /// }); | |
59 | /// ``` | |
60 | /// | |
61 | /// An additional expected parameter can also be added to the failure message: | |
62 | /// | |
63 | /// ``` | |
64 | /// let v = try_validation!(some_fn(), some_path, { | |
65 | /// Foo | Bar | Baz => { "some failure" } expected { "something that wasn't a failure" }, | |
66 | /// }); | |
67 | /// ``` | |
68 | /// | |
69 | /// An additional nicety is that both parameters actually take format args, so you can just write | |
70 | /// the format string in directly: | |
71 | /// | |
72 | /// ``` | |
73 | /// let v = try_validation!(some_fn(), some_path, { | |
74 | /// Foo | Bar | Baz => { "{:?}", some_failure } expected { "{}", expected_value }, | |
75 | /// }); | |
76 | /// ``` | |
77 | /// | |
0bf4aa26 | 78 | macro_rules! try_validation { |
f9f354fc | 79 | ($e:expr, $where:expr, |
6a06907d | 80 | $( $( $p:pat )|+ => { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? ),+ $(,)? |
f9f354fc | 81 | ) => {{ |
0bf4aa26 XL |
82 | match $e { |
83 | Ok(x) => x, | |
f9f354fc XL |
84 | // We catch the error and turn it into a validation failure. We are okay with |
85 | // allocation here as this can only slow down builds that fail anyway. | |
6a06907d XL |
86 | Err(e) => match e.kind() { |
87 | $( | |
88 | $($p)|+ => | |
89 | throw_validation_failure!( | |
90 | $where, | |
91 | { $( $what_fmt ),+ } $( expected { $( $expected_fmt ),+ } )? | |
92 | ) | |
93 | ),+, | |
94 | #[allow(unreachable_patterns)] | |
95 | _ => Err::<!, _>(e)?, | |
96 | } | |
0bf4aa26 | 97 | } |
60c5eb7d | 98 | }}; |
0bf4aa26 XL |
99 | } |
100 | ||
a1dfa0c6 | 101 | /// We want to show a nice path to the invalid field for diagnostics, |
b7449926 XL |
102 | /// but avoid string operations in the happy case where no error happens. |
103 | /// So we track a `Vec<PathElem>` where `PathElem` contains all the data we | |
104 | /// need to later print something for the user. | |
105 | #[derive(Copy, Clone, Debug)] | |
106 | pub enum PathElem { | |
107 | Field(Symbol), | |
a1dfa0c6 | 108 | Variant(Symbol), |
48663c56 | 109 | GeneratorState(VariantIdx), |
74b04a01 | 110 | CapturedVar(Symbol), |
b7449926 XL |
111 | ArrayElem(usize), |
112 | TupleElem(usize), | |
113 | Deref, | |
74b04a01 XL |
114 | EnumTag, |
115 | GeneratorTag, | |
a1dfa0c6 | 116 | DynDowncast, |
b7449926 XL |
117 | } |
118 | ||
29967ef6 XL |
119 | /// Extra things to check for during validation of CTFE results. |
120 | pub enum CtfeValidationMode { | |
121 | /// Regular validation, nothing special happening. | |
122 | Regular, | |
fc512014 XL |
123 | /// Validation of a `const`. |
124 | /// `inner` says if this is an inner, indirect allocation (as opposed to the top-level const | |
125 | /// allocation). Being an inner allocation makes a difference because the top-level allocation | |
126 | /// of a `const` is copied for each use, but the inner allocations are implicitly shared. | |
127 | /// `allow_static_ptrs` says if pointers to statics are permitted (which is the case for promoteds in statics). | |
128 | Const { inner: bool, allow_static_ptrs: bool }, | |
29967ef6 XL |
129 | } |
130 | ||
0bf4aa26 | 131 | /// State for tracking recursive validation of references |
dc9dc135 | 132 | pub struct RefTracking<T, PATH = ()> { |
9fa01778 | 133 | pub seen: FxHashSet<T>, |
dc9dc135 | 134 | pub todo: Vec<(T, PATH)>, |
0bf4aa26 XL |
135 | } |
136 | ||
dc9dc135 XL |
137 | impl<T: Copy + Eq + Hash + std::fmt::Debug, PATH: Default> RefTracking<T, PATH> { |
138 | pub fn empty() -> Self { | |
60c5eb7d | 139 | RefTracking { seen: FxHashSet::default(), todo: vec![] } |
dc9dc135 | 140 | } |
9fa01778 | 141 | pub fn new(op: T) -> Self { |
60c5eb7d XL |
142 | let mut ref_tracking_for_consts = |
143 | RefTracking { seen: FxHashSet::default(), todo: vec![(op, PATH::default())] }; | |
dc9dc135 XL |
144 | ref_tracking_for_consts.seen.insert(op); |
145 | ref_tracking_for_consts | |
146 | } | |
147 | ||
148 | pub fn track(&mut self, op: T, path: impl FnOnce() -> PATH) { | |
149 | if self.seen.insert(op) { | |
150 | trace!("Recursing below ptr {:#?}", op); | |
151 | let path = path(); | |
152 | // Remember to come back to this later. | |
153 | self.todo.push((op, path)); | |
154 | } | |
0bf4aa26 XL |
155 | } |
156 | } | |
157 | ||
b7449926 | 158 | /// Format a path |
5869c6ff | 159 | fn write_path(out: &mut String, path: &[PathElem]) { |
b7449926 XL |
160 | use self::PathElem::*; |
161 | ||
b7449926 XL |
162 | for elem in path.iter() { |
163 | match elem { | |
a1dfa0c6 | 164 | Field(name) => write!(out, ".{}", name), |
74b04a01 XL |
165 | EnumTag => write!(out, ".<enum-tag>"), |
166 | Variant(name) => write!(out, ".<enum-variant({})>", name), | |
167 | GeneratorTag => write!(out, ".<generator-tag>"), | |
48663c56 | 168 | GeneratorState(idx) => write!(out, ".<generator-state({})>", idx.index()), |
74b04a01 | 169 | CapturedVar(name) => write!(out, ".<captured-var({})>", name), |
a1dfa0c6 XL |
170 | TupleElem(idx) => write!(out, ".{}", idx), |
171 | ArrayElem(idx) => write!(out, "[{}]", idx), | |
dfeec247 | 172 | // `.<deref>` does not match Rust syntax, but it is more readable for long paths -- and |
60c5eb7d XL |
173 | // some of the other items here also are not Rust syntax. Actually we can't |
174 | // even use the usual syntax because we are just showing the projections, | |
175 | // not the root. | |
dfeec247 | 176 | Deref => write!(out, ".<deref>"), |
a1dfa0c6 | 177 | DynDowncast => write!(out, ".<dyn-downcast>"), |
60c5eb7d XL |
178 | } |
179 | .unwrap() | |
b7449926 | 180 | } |
b7449926 XL |
181 | } |
182 | ||
a1dfa0c6 XL |
183 | // Test if a range that wraps at overflow contains `test` |
184 | fn wrapping_range_contains(r: &RangeInclusive<u128>, test: u128) -> bool { | |
185 | let (lo, hi) = r.clone().into_inner(); | |
186 | if lo > hi { | |
187 | // Wrapped | |
188 | (..=hi).contains(&test) || (lo..).contains(&test) | |
189 | } else { | |
190 | // Normal | |
191 | r.contains(&test) | |
0bf4aa26 XL |
192 | } |
193 | } | |
194 | ||
a1dfa0c6 XL |
195 | // Formats such that a sentence like "expected something {}" to mean |
196 | // "expected something <in the given range>" makes sense. | |
197 | fn wrapping_range_format(r: &RangeInclusive<u128>, max_hi: u128) -> String { | |
198 | let (lo, hi) = r.clone().into_inner(); | |
74b04a01 | 199 | assert!(hi <= max_hi); |
a1dfa0c6 XL |
200 | if lo > hi { |
201 | format!("less or equal to {}, or greater or equal to {}", hi, lo) | |
416331ca XL |
202 | } else if lo == hi { |
203 | format!("equal to {}", lo) | |
204 | } else if lo == 0 { | |
74b04a01 | 205 | assert!(hi < max_hi, "should not be printing if the range covers everything"); |
416331ca XL |
206 | format!("less or equal to {}", hi) |
207 | } else if hi == max_hi { | |
74b04a01 | 208 | assert!(lo > 0, "should not be printing if the range covers everything"); |
416331ca | 209 | format!("greater or equal to {}", lo) |
a1dfa0c6 | 210 | } else { |
416331ca | 211 | format!("in the range {:?}", r) |
a1dfa0c6 XL |
212 | } |
213 | } | |
214 | ||
dc9dc135 | 215 | struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { |
a1dfa0c6 XL |
216 | /// The `path` may be pushed to, but the part that is present when a function |
217 | /// starts must not be changed! `visit_fields` and `visit_array` rely on | |
218 | /// this stack discipline. | |
219 | path: Vec<PathElem>, | |
29967ef6 XL |
220 | ref_tracking: Option<&'rt mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>>, |
221 | /// `None` indicates this is not validating for CTFE (but for runtime). | |
222 | ctfe_mode: Option<CtfeValidationMode>, | |
416331ca | 223 | ecx: &'rt InterpCx<'mir, 'tcx, M>, |
a1dfa0c6 XL |
224 | } |
225 | ||
ba9703b0 XL |
226 | impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M> { |
227 | fn aggregate_field_path_elem(&mut self, layout: TyAndLayout<'tcx>, field: usize) -> PathElem { | |
74b04a01 XL |
228 | // First, check if we are projecting to a variant. |
229 | match layout.variants { | |
f035d41b XL |
230 | Variants::Multiple { tag_field, .. } => { |
231 | if tag_field == field { | |
1b1a35ee | 232 | return match layout.ty.kind() { |
74b04a01 XL |
233 | ty::Adt(def, ..) if def.is_enum() => PathElem::EnumTag, |
234 | ty::Generator(..) => PathElem::GeneratorTag, | |
235 | _ => bug!("non-variant type {:?}", layout.ty), | |
236 | }; | |
237 | } | |
238 | } | |
ba9703b0 | 239 | Variants::Single { .. } => {} |
74b04a01 XL |
240 | } |
241 | ||
242 | // Now we know we are projecting to a field, so figure out which one. | |
1b1a35ee | 243 | match layout.ty.kind() { |
a1dfa0c6 XL |
244 | // generators and closures. |
245 | ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { | |
48663c56 | 246 | let mut name = None; |
6a06907d XL |
247 | // FIXME this should be more descriptive i.e. CapturePlace instead of CapturedVar |
248 | // https://github.com/rust-lang/project-rfc-2229/issues/46 | |
249 | if let Some(local_def_id) = def_id.as_local() { | |
250 | let tables = self.ecx.tcx.typeck(local_def_id); | |
251 | if let Some(captured_place) = | |
252 | tables.closure_min_captures_flattened(*def_id).nth(field) | |
253 | { | |
48663c56 XL |
254 | // Sometimes the index is beyond the number of upvars (seen |
255 | // for a generator). | |
6a06907d XL |
256 | let var_hir_id = captured_place.get_root_variable(); |
257 | let node = self.ecx.tcx.hir().get(var_hir_id); | |
258 | if let hir::Node::Binding(pat) = node { | |
259 | if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { | |
260 | name = Some(ident.name); | |
48663c56 XL |
261 | } |
262 | } | |
263 | } | |
a1dfa0c6 | 264 | } |
48663c56 | 265 | |
74b04a01 | 266 | PathElem::CapturedVar(name.unwrap_or_else(|| { |
48663c56 | 267 | // Fall back to showing the field index. |
dc9dc135 | 268 | sym::integer(field) |
48663c56 | 269 | })) |
a1dfa0c6 XL |
270 | } |
271 | ||
272 | // tuples | |
273 | ty::Tuple(_) => PathElem::TupleElem(field), | |
274 | ||
275 | // enums | |
276 | ty::Adt(def, ..) if def.is_enum() => { | |
74b04a01 | 277 | // we might be projecting *to* a variant, or to a field *in* a variant. |
a1dfa0c6 | 278 | match layout.variants { |
ba9703b0 | 279 | Variants::Single { index } => { |
dfeec247 | 280 | // Inside a variant |
60c5eb7d XL |
281 | PathElem::Field(def.variants[index].fields[field].ident.name) |
282 | } | |
ba9703b0 | 283 | Variants::Multiple { .. } => bug!("we handled variants above"), |
a1dfa0c6 XL |
284 | } |
285 | } | |
286 | ||
287 | // other ADTs | |
288 | ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name), | |
289 | ||
290 | // arrays/slices | |
291 | ty::Array(..) | ty::Slice(..) => PathElem::ArrayElem(field), | |
292 | ||
293 | // dyn traits | |
294 | ty::Dynamic(..) => PathElem::DynDowncast, | |
295 | ||
296 | // nothing else has an aggregate layout | |
297 | _ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", layout.ty), | |
298 | } | |
299 | } | |
300 | ||
f035d41b | 301 | fn with_elem<R>( |
a1dfa0c6 | 302 | &mut self, |
a1dfa0c6 | 303 | elem: PathElem, |
f035d41b XL |
304 | f: impl FnOnce(&mut Self) -> InterpResult<'tcx, R>, |
305 | ) -> InterpResult<'tcx, R> { | |
a1dfa0c6 XL |
306 | // Remember the old state |
307 | let path_len = self.path.len(); | |
f035d41b | 308 | // Record new element |
a1dfa0c6 | 309 | self.path.push(elem); |
f035d41b XL |
310 | // Perform operation |
311 | let r = f(self)?; | |
a1dfa0c6 XL |
312 | // Undo changes |
313 | self.path.truncate(path_len); | |
f035d41b XL |
314 | // Done |
315 | Ok(r) | |
a1dfa0c6 | 316 | } |
e1599b0c XL |
317 | |
318 | fn check_wide_ptr_meta( | |
319 | &mut self, | |
dfeec247 | 320 | meta: MemPlaceMeta<M::PointerTag>, |
ba9703b0 | 321 | pointee: TyAndLayout<'tcx>, |
e1599b0c XL |
322 | ) -> InterpResult<'tcx> { |
323 | let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env); | |
1b1a35ee | 324 | match tail.kind() { |
e1599b0c | 325 | ty::Dynamic(..) => { |
dfeec247 | 326 | let vtable = meta.unwrap_meta(); |
f9f354fc | 327 | // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines. |
e1599b0c | 328 | try_validation!( |
f9f354fc | 329 | self.ecx.memory.check_ptr_access_align( |
e1599b0c | 330 | vtable, |
60c5eb7d | 331 | 3 * self.ecx.tcx.data_layout.pointer_size, // drop, size, align |
f9f354fc XL |
332 | Some(self.ecx.tcx.data_layout.pointer_align.abi), |
333 | CheckInAllocMsg::InboundsTest, | |
e1599b0c | 334 | ), |
f9f354fc XL |
335 | self.path, |
336 | err_ub!(DanglingIntPointer(..)) | | |
337 | err_ub!(PointerUseAfterFree(..)) | | |
338 | err_unsup!(ReadBytesAsPointer) => | |
339 | { "dangling vtable pointer in wide pointer" }, | |
340 | err_ub!(AlignmentCheckFailed { .. }) => | |
341 | { "unaligned vtable pointer in wide pointer" }, | |
342 | err_ub!(PointerOutOfBounds { .. }) => | |
343 | { "too small vtable" }, | |
e1599b0c | 344 | ); |
60c5eb7d XL |
345 | try_validation!( |
346 | self.ecx.read_drop_type_from_vtable(vtable), | |
f9f354fc XL |
347 | self.path, |
348 | err_ub!(DanglingIntPointer(..)) | | |
349 | err_ub!(InvalidFunctionPointer(..)) | | |
350 | err_unsup!(ReadBytesAsPointer) => | |
351 | { "invalid drop function pointer in vtable (not pointing to a function)" }, | |
352 | err_ub!(InvalidDropFn(..)) => | |
353 | { "invalid drop function pointer in vtable (function has incompatible signature)" }, | |
60c5eb7d XL |
354 | ); |
355 | try_validation!( | |
356 | self.ecx.read_size_and_align_from_vtable(vtable), | |
f9f354fc XL |
357 | self.path, |
358 | err_unsup!(ReadPointerAsBytes) => { "invalid size or align in vtable" }, | |
60c5eb7d | 359 | ); |
e1599b0c XL |
360 | // FIXME: More checks for the vtable. |
361 | } | |
362 | ty::Slice(..) | ty::Str => { | |
60c5eb7d | 363 | let _len = try_validation!( |
dfeec247 | 364 | meta.unwrap_meta().to_machine_usize(self.ecx), |
f9f354fc XL |
365 | self.path, |
366 | err_unsup!(ReadPointerAsBytes) => { "non-integer slice length in wide pointer" }, | |
60c5eb7d | 367 | ); |
e1599b0c XL |
368 | // We do not check that `len * elem_size <= isize::MAX`: |
369 | // that is only required for references, and there it falls out of the | |
60c5eb7d | 370 | // "dereferenceable" check performed by Stacked Borrows. |
e1599b0c XL |
371 | } |
372 | ty::Foreign(..) => { | |
373 | // Unsized, but not wide. | |
374 | } | |
60c5eb7d | 375 | _ => bug!("Unexpected unsized type tail: {:?}", tail), |
e1599b0c XL |
376 | } |
377 | ||
378 | Ok(()) | |
379 | } | |
a1dfa0c6 | 380 | |
74b04a01 XL |
381 | /// Check a reference or `Box`. |
382 | fn check_safe_pointer( | |
a1dfa0c6 | 383 | &mut self, |
6a06907d | 384 | value: &OpTy<'tcx, M::PointerTag>, |
74b04a01 | 385 | kind: &str, |
dc9dc135 | 386 | ) -> InterpResult<'tcx> { |
6a06907d XL |
387 | let value = try_validation!( |
388 | self.ecx.read_immediate(value), | |
389 | self.path, | |
390 | err_unsup!(ReadPointerAsBytes) => { "part of a pointer" } expected { "a proper pointer or integer value" }, | |
391 | ); | |
74b04a01 XL |
392 | // Handle wide pointers. |
393 | // Check metadata early, for better diagnostics | |
ba9703b0 | 394 | let place = try_validation!( |
6a06907d | 395 | self.ecx.ref_to_mplace(&value), |
f9f354fc | 396 | self.path, |
f035d41b | 397 | err_ub!(InvalidUninitBytes(None)) => { "uninitialized {}", kind }, |
ba9703b0 | 398 | ); |
74b04a01 XL |
399 | if place.layout.is_unsized() { |
400 | self.check_wide_ptr_meta(place.meta, place.layout)?; | |
401 | } | |
402 | // Make sure this is dereferenceable and all. | |
f9f354fc | 403 | let size_and_align = try_validation!( |
6a06907d | 404 | self.ecx.size_and_align_of_mplace(&place), |
f9f354fc XL |
405 | self.path, |
406 | err_ub!(InvalidMeta(msg)) => { "invalid {} metadata: {}", kind, msg }, | |
407 | ); | |
74b04a01 XL |
408 | let (size, align) = size_and_align |
409 | // for the purpose of validity, consider foreign types to have | |
410 | // alignment and size determined by the layout (size will be 0, | |
411 | // alignment should take attributes into account). | |
412 | .unwrap_or_else(|| (place.layout.size, place.layout.align.abi)); | |
f9f354fc XL |
413 | // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines. |
414 | let ptr: Option<_> = try_validation!( | |
415 | self.ecx.memory.check_ptr_access_align( | |
416 | place.ptr, | |
417 | size, | |
418 | Some(align), | |
419 | CheckInAllocMsg::InboundsTest, | |
420 | ), | |
421 | self.path, | |
422 | err_ub!(AlignmentCheckFailed { required, has }) => | |
423 | { | |
424 | "an unaligned {} (required {} byte alignment but found {})", | |
425 | kind, | |
426 | required.bytes(), | |
427 | has.bytes() | |
428 | }, | |
429 | err_ub!(DanglingIntPointer(0, _)) => | |
430 | { "a NULL {}", kind }, | |
431 | err_ub!(DanglingIntPointer(i, _)) => | |
432 | { "a dangling {} (address 0x{:x} is unallocated)", kind, i }, | |
433 | err_ub!(PointerOutOfBounds { .. }) => | |
434 | { "a dangling {} (going beyond the bounds of its allocation)", kind }, | |
435 | err_unsup!(ReadBytesAsPointer) => | |
436 | { "a dangling {} (created from integer)", kind }, | |
437 | // This cannot happen during const-eval (because interning already detects | |
438 | // dangling pointers), but it can happen in Miri. | |
439 | err_ub!(PointerUseAfterFree(..)) => | |
440 | { "a dangling {} (use-after-free)", kind }, | |
441 | ); | |
74b04a01 | 442 | // Recursive checking |
29967ef6 | 443 | if let Some(ref mut ref_tracking) = self.ref_tracking { |
74b04a01 XL |
444 | if let Some(ptr) = ptr { |
445 | // not a ZST | |
446 | // Skip validation entirely for some external statics | |
f9f354fc | 447 | let alloc_kind = self.ecx.tcx.get_global_alloc(ptr.alloc_id); |
74b04a01 | 448 | if let Some(GlobalAlloc::Static(did)) = alloc_kind { |
f9f354fc | 449 | assert!(!self.ecx.tcx.is_thread_local_static(did)); |
1b1a35ee | 450 | assert!(self.ecx.tcx.is_static(did)); |
fc512014 XL |
451 | if matches!( |
452 | self.ctfe_mode, | |
453 | Some(CtfeValidationMode::Const { allow_static_ptrs: false, .. }) | |
454 | ) { | |
1b1a35ee XL |
455 | // See const_eval::machine::MemoryExtra::can_access_statics for why |
456 | // this check is so important. | |
457 | // This check is reachable when the const just referenced the static, | |
458 | // but never read it (so we never entered `before_access_global`). | |
f9f354fc XL |
459 | throw_validation_failure!(self.path, |
460 | { "a {} pointing to a static variable", kind } | |
461 | ); | |
462 | } | |
29967ef6 XL |
463 | // We skip checking other statics. These statics must be sound by |
464 | // themselves, and the only way to get broken statics here is by using | |
465 | // unsafe code. | |
466 | // The reasons we don't check other statics is twofold. For one, in all | |
467 | // sound cases, the static was already validated on its own, and second, we | |
468 | // trigger cycle errors if we try to compute the value of the other static | |
469 | // and that static refers back to us. | |
470 | // We might miss const-invalid data, | |
471 | // but things are still sound otherwise (in particular re: consts | |
472 | // referring to statics). | |
473 | return Ok(()); | |
60c5eb7d | 474 | } |
74b04a01 XL |
475 | } |
476 | // Proceed recursively even for ZST, no reason to skip them! | |
477 | // `!` is a ZST and we want to validate it. | |
478 | // Normalize before handing `place` to tracking because that will | |
479 | // check for duplicates. | |
480 | let place = if size.bytes() > 0 { | |
481 | self.ecx.force_mplace_ptr(place).expect("we already bounds-checked") | |
482 | } else { | |
483 | place | |
484 | }; | |
485 | let path = &self.path; | |
486 | ref_tracking.track(place, || { | |
487 | // We need to clone the path anyway, make sure it gets created | |
488 | // with enough space for the additional `Deref`. | |
489 | let mut new_path = Vec::with_capacity(path.len() + 1); | |
490 | new_path.clone_from(path); | |
491 | new_path.push(PathElem::Deref); | |
492 | new_path | |
493 | }); | |
a1dfa0c6 | 494 | } |
74b04a01 | 495 | Ok(()) |
a1dfa0c6 XL |
496 | } |
497 | ||
6a06907d XL |
498 | fn read_scalar( |
499 | &self, | |
500 | op: &OpTy<'tcx, M::PointerTag>, | |
501 | ) -> InterpResult<'tcx, ScalarMaybeUninit<M::PointerTag>> { | |
502 | Ok(try_validation!( | |
503 | self.ecx.read_scalar(op), | |
504 | self.path, | |
505 | err_unsup!(ReadPointerAsBytes) => { "(potentially part of) a pointer" } expected { "plain (non-pointer) bytes" }, | |
506 | )) | |
507 | } | |
508 | ||
74b04a01 XL |
509 | /// Check if this is a value of primitive type, and if yes check the validity of the value |
510 | /// at that type. Return `true` if the type is indeed primitive. | |
511 | fn try_visit_primitive( | |
512 | &mut self, | |
6a06907d | 513 | value: &OpTy<'tcx, M::PointerTag>, |
74b04a01 | 514 | ) -> InterpResult<'tcx, bool> { |
0bf4aa26 XL |
515 | // Go over all the primitive types |
516 | let ty = value.layout.ty; | |
1b1a35ee | 517 | match ty.kind() { |
0bf4aa26 | 518 | ty::Bool => { |
6a06907d | 519 | let value = self.read_scalar(value)?; |
f9f354fc XL |
520 | try_validation!( |
521 | value.to_bool(), | |
522 | self.path, | |
f035d41b XL |
523 | err_ub!(InvalidBool(..)) | err_ub!(InvalidUninitBytes(None)) => |
524 | { "{}", value } expected { "a boolean" }, | |
f9f354fc | 525 | ); |
74b04a01 | 526 | Ok(true) |
60c5eb7d | 527 | } |
0bf4aa26 | 528 | ty::Char => { |
6a06907d | 529 | let value = self.read_scalar(value)?; |
f9f354fc XL |
530 | try_validation!( |
531 | value.to_char(), | |
532 | self.path, | |
f035d41b XL |
533 | err_ub!(InvalidChar(..)) | err_ub!(InvalidUninitBytes(None)) => |
534 | { "{}", value } expected { "a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`)" }, | |
f9f354fc | 535 | ); |
74b04a01 | 536 | Ok(true) |
60c5eb7d | 537 | } |
0bf4aa26 | 538 | ty::Float(_) | ty::Int(_) | ty::Uint(_) => { |
6a06907d | 539 | let value = self.read_scalar(value)?; |
0bf4aa26 XL |
540 | // NOTE: Keep this in sync with the array optimization for int/float |
541 | // types below! | |
29967ef6 | 542 | if self.ctfe_mode.is_some() { |
0bf4aa26 | 543 | // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous |
3dfed10e | 544 | let is_bits = value.check_init().map_or(false, |v| v.is_bits()); |
74b04a01 | 545 | if !is_bits { |
f9f354fc XL |
546 | throw_validation_failure!(self.path, |
547 | { "{}", value } expected { "initialized plain (non-pointer) bytes" } | |
74b04a01 XL |
548 | ) |
549 | } | |
0bf4aa26 XL |
550 | } else { |
551 | // At run-time, for now, we accept *anything* for these types, including | |
3dfed10e | 552 | // uninit. We should fix that, but let's start low. |
0bf4aa26 | 553 | } |
74b04a01 | 554 | Ok(true) |
0bf4aa26 | 555 | } |
a1dfa0c6 | 556 | ty::RawPtr(..) => { |
3dfed10e | 557 | // We are conservative with uninit for integers, but try to |
ba9703b0 XL |
558 | // actually enforce the strict rules for raw pointers (mostly because |
559 | // that lets us re-use `ref_to_mplace`). | |
74b04a01 | 560 | let place = try_validation!( |
6a06907d | 561 | self.ecx.read_immediate(value).and_then(|ref i| self.ecx.ref_to_mplace(i)), |
f9f354fc | 562 | self.path, |
f035d41b | 563 | err_ub!(InvalidUninitBytes(None)) => { "uninitialized raw pointer" }, |
6a06907d | 564 | err_unsup!(ReadPointerAsBytes) => { "part of a pointer" } expected { "a proper pointer or integer value" }, |
74b04a01 | 565 | ); |
60c5eb7d XL |
566 | if place.layout.is_unsized() { |
567 | self.check_wide_ptr_meta(place.meta, place.layout)?; | |
e1599b0c | 568 | } |
74b04a01 | 569 | Ok(true) |
a1dfa0c6 | 570 | } |
29967ef6 XL |
571 | ty::Ref(_, ty, mutbl) => { |
572 | if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) | |
573 | && *mutbl == hir::Mutability::Mut | |
574 | { | |
575 | // A mutable reference inside a const? That does not seem right (except if it is | |
576 | // a ZST). | |
577 | let layout = self.ecx.layout_of(ty)?; | |
578 | if !layout.is_zst() { | |
579 | throw_validation_failure!(self.path, { "mutable reference in a `const`" }); | |
580 | } | |
581 | } | |
74b04a01 XL |
582 | self.check_safe_pointer(value, "reference")?; |
583 | Ok(true) | |
584 | } | |
585 | ty::Adt(def, ..) if def.is_box() => { | |
586 | self.check_safe_pointer(value, "box")?; | |
587 | Ok(true) | |
0bf4aa26 XL |
588 | } |
589 | ty::FnPtr(_sig) => { | |
6a06907d XL |
590 | let value = try_validation!( |
591 | self.ecx.read_immediate(value), | |
592 | self.path, | |
593 | err_unsup!(ReadPointerAsBytes) => { "part of a pointer" } expected { "a proper pointer or integer value" }, | |
594 | ); | |
595 | // Make sure we print a `ScalarMaybeUninit` (and not an `ImmTy`) in the error | |
596 | // message below. | |
597 | let value = value.to_scalar_or_uninit(); | |
416331ca | 598 | let _fn = try_validation!( |
3dfed10e | 599 | value.check_init().and_then(|ptr| self.ecx.memory.get_fn(ptr)), |
60c5eb7d | 600 | self.path, |
f9f354fc XL |
601 | err_ub!(DanglingIntPointer(..)) | |
602 | err_ub!(InvalidFunctionPointer(..)) | | |
f035d41b | 603 | err_ub!(InvalidUninitBytes(None)) | |
f9f354fc XL |
604 | err_unsup!(ReadBytesAsPointer) => |
605 | { "{}", value } expected { "a function pointer" }, | |
416331ca | 606 | ); |
0bf4aa26 | 607 | // FIXME: Check if the signature matches |
74b04a01 XL |
608 | Ok(true) |
609 | } | |
f9f354fc | 610 | ty::Never => throw_validation_failure!(self.path, { "a value of the never type `!`" }), |
74b04a01 XL |
611 | ty::Foreign(..) | ty::FnDef(..) => { |
612 | // Nothing to check. | |
613 | Ok(true) | |
0bf4aa26 | 614 | } |
29967ef6 | 615 | // The above should be all the primitive types. The rest is compound, we |
74b04a01 | 616 | // check them by visiting their fields/variants. |
74b04a01 XL |
617 | ty::Adt(..) |
618 | | ty::Tuple(..) | |
619 | | ty::Array(..) | |
620 | | ty::Slice(..) | |
621 | | ty::Str | |
622 | | ty::Dynamic(..) | |
623 | | ty::Closure(..) | |
624 | | ty::Generator(..) => Ok(false), | |
625 | // Some types only occur during typechecking, they have no layout. | |
626 | // We should not see them here and we could not check them anyway. | |
f035d41b | 627 | ty::Error(_) |
74b04a01 XL |
628 | | ty::Infer(..) |
629 | | ty::Placeholder(..) | |
630 | | ty::Bound(..) | |
631 | | ty::Param(..) | |
632 | | ty::Opaque(..) | |
74b04a01 XL |
633 | | ty::Projection(..) |
634 | | ty::GeneratorWitness(..) => bug!("Encountered invalid type {:?}", ty), | |
0bf4aa26 | 635 | } |
a1dfa0c6 XL |
636 | } |
637 | ||
638 | fn visit_scalar( | |
639 | &mut self, | |
6a06907d | 640 | op: &OpTy<'tcx, M::PointerTag>, |
ba9703b0 | 641 | scalar_layout: &Scalar, |
dc9dc135 | 642 | ) -> InterpResult<'tcx> { |
6a06907d | 643 | let value = self.read_scalar(op)?; |
74b04a01 XL |
644 | let valid_range = &scalar_layout.valid_range; |
645 | let (lo, hi) = valid_range.clone().into_inner(); | |
a1dfa0c6 | 646 | // Determine the allowed range |
a1dfa0c6 | 647 | // `max_hi` is as big as the size fits |
74b04a01 | 648 | let max_hi = u128::MAX >> (128 - op.layout.size.bits()); |
0bf4aa26 | 649 | assert!(hi <= max_hi); |
a1dfa0c6 | 650 | // We could also write `(hi + 1) % (max_hi + 1) == lo` but `max_hi + 1` overflows for `u128` |
0bf4aa26 XL |
651 | if (lo == 0 && hi == max_hi) || (hi + 1 == lo) { |
652 | // Nothing to check | |
653 | return Ok(()); | |
654 | } | |
655 | // At least one value is excluded. Get the bits. | |
60c5eb7d | 656 | let value = try_validation!( |
3dfed10e | 657 | value.check_init(), |
9fa01778 | 658 | self.path, |
f035d41b | 659 | err_ub!(InvalidUninitBytes(None)) => { "{}", value } |
f9f354fc | 660 | expected { "something {}", wrapping_range_format(valid_range, max_hi) }, |
9fa01778 | 661 | ); |
dc9dc135 XL |
662 | let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) { |
663 | Err(ptr) => { | |
0bf4aa26 | 664 | if lo == 1 && hi == max_hi { |
dc9dc135 XL |
665 | // Only NULL is the niche. So make sure the ptr is NOT NULL. |
666 | if self.ecx.memory.ptr_may_be_null(ptr) { | |
f9f354fc XL |
667 | throw_validation_failure!(self.path, |
668 | { "a potentially NULL pointer" } | |
669 | expected { | |
416331ca | 670 | "something that cannot possibly fail to be {}", |
74b04a01 | 671 | wrapping_range_format(valid_range, max_hi) |
f9f354fc | 672 | } |
416331ca | 673 | ) |
b7449926 | 674 | } |
0bf4aa26 | 675 | return Ok(()); |
b7449926 | 676 | } else { |
416331ca | 677 | // Conservatively, we reject, because the pointer *could* have a bad |
0bf4aa26 | 678 | // value. |
f9f354fc XL |
679 | throw_validation_failure!(self.path, |
680 | { "a pointer" } | |
681 | expected { | |
a1dfa0c6 | 682 | "something that cannot possibly fail to be {}", |
74b04a01 | 683 | wrapping_range_format(valid_range, max_hi) |
f9f354fc | 684 | } |
416331ca | 685 | ) |
b7449926 XL |
686 | } |
687 | } | |
60c5eb7d | 688 | Ok(data) => data, |
0bf4aa26 XL |
689 | }; |
690 | // Now compare. This is slightly subtle because this is a special "wrap-around" range. | |
74b04a01 | 691 | if wrapping_range_contains(&valid_range, bits) { |
a1dfa0c6 | 692 | Ok(()) |
b7449926 | 693 | } else { |
f9f354fc XL |
694 | throw_validation_failure!(self.path, |
695 | { "{}", bits } | |
696 | expected { "something {}", wrapping_range_format(valid_range, max_hi) } | |
a1dfa0c6 XL |
697 | ) |
698 | } | |
699 | } | |
74b04a01 XL |
700 | } |
701 | ||
ba9703b0 | 702 | impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> |
74b04a01 XL |
703 | for ValidityVisitor<'rt, 'mir, 'tcx, M> |
704 | { | |
705 | type V = OpTy<'tcx, M::PointerTag>; | |
706 | ||
707 | #[inline(always)] | |
708 | fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> { | |
709 | &self.ecx | |
710 | } | |
711 | ||
f035d41b XL |
712 | fn read_discriminant( |
713 | &mut self, | |
6a06907d | 714 | op: &OpTy<'tcx, M::PointerTag>, |
f035d41b XL |
715 | ) -> InterpResult<'tcx, VariantIdx> { |
716 | self.with_elem(PathElem::EnumTag, move |this| { | |
717 | Ok(try_validation!( | |
718 | this.ecx.read_discriminant(op), | |
719 | this.path, | |
720 | err_ub!(InvalidTag(val)) => | |
721 | { "{}", val } expected { "a valid enum tag" }, | |
722 | err_ub!(InvalidUninitBytes(None)) => | |
723 | { "uninitialized bytes" } expected { "a valid enum tag" }, | |
724 | err_unsup!(ReadPointerAsBytes) => | |
725 | { "a pointer" } expected { "a valid enum tag" }, | |
726 | ) | |
727 | .1) | |
728 | }) | |
729 | } | |
730 | ||
74b04a01 XL |
731 | #[inline] |
732 | fn visit_field( | |
733 | &mut self, | |
6a06907d | 734 | old_op: &OpTy<'tcx, M::PointerTag>, |
74b04a01 | 735 | field: usize, |
6a06907d | 736 | new_op: &OpTy<'tcx, M::PointerTag>, |
74b04a01 XL |
737 | ) -> InterpResult<'tcx> { |
738 | let elem = self.aggregate_field_path_elem(old_op.layout, field); | |
f035d41b | 739 | self.with_elem(elem, move |this| this.visit_value(new_op)) |
74b04a01 XL |
740 | } |
741 | ||
742 | #[inline] | |
743 | fn visit_variant( | |
744 | &mut self, | |
6a06907d | 745 | old_op: &OpTy<'tcx, M::PointerTag>, |
74b04a01 | 746 | variant_id: VariantIdx, |
6a06907d | 747 | new_op: &OpTy<'tcx, M::PointerTag>, |
74b04a01 | 748 | ) -> InterpResult<'tcx> { |
1b1a35ee | 749 | let name = match old_op.layout.ty.kind() { |
74b04a01 XL |
750 | ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name), |
751 | // Generators also have variants | |
752 | ty::Generator(..) => PathElem::GeneratorState(variant_id), | |
753 | _ => bug!("Unexpected type with variant: {:?}", old_op.layout.ty), | |
754 | }; | |
f035d41b | 755 | self.with_elem(name, move |this| this.visit_value(new_op)) |
74b04a01 XL |
756 | } |
757 | ||
758 | #[inline(always)] | |
ba9703b0 XL |
759 | fn visit_union( |
760 | &mut self, | |
6a06907d | 761 | _op: &OpTy<'tcx, M::PointerTag>, |
ba9703b0 XL |
762 | _fields: NonZeroUsize, |
763 | ) -> InterpResult<'tcx> { | |
74b04a01 XL |
764 | Ok(()) |
765 | } | |
766 | ||
767 | #[inline] | |
6a06907d | 768 | fn visit_value(&mut self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { |
74b04a01 XL |
769 | trace!("visit_value: {:?}, {:?}", *op, op.layout); |
770 | ||
771 | // Check primitive types -- the leafs of our recursive descend. | |
772 | if self.try_visit_primitive(op)? { | |
773 | return Ok(()); | |
774 | } | |
775 | // Sanity check: `builtin_deref` does not know any pointers that are not primitive. | |
776 | assert!(op.layout.ty.builtin_deref(true).is_none()); | |
777 | ||
fc512014 | 778 | // Special check preventing `UnsafeCell` in the inner part of constants |
29967ef6 | 779 | if let Some(def) = op.layout.ty.ty_adt_def() { |
fc512014 | 780 | if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. })) |
29967ef6 XL |
781 | && Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() |
782 | { | |
783 | throw_validation_failure!(self.path, { "`UnsafeCell` in a `const`" }); | |
784 | } | |
785 | } | |
786 | ||
f035d41b XL |
787 | // Recursively walk the value at its type. |
788 | self.walk_value(op)?; | |
74b04a01 XL |
789 | |
790 | // *After* all of this, check the ABI. We need to check the ABI to handle | |
791 | // types like `NonNull` where the `Scalar` info is more restrictive than what | |
792 | // the fields say (`rustc_layout_scalar_valid_range_start`). | |
793 | // But in most cases, this will just propagate what the fields say, | |
794 | // and then we want the error to point at the field -- so, first recurse, | |
795 | // then check ABI. | |
796 | // | |
797 | // FIXME: We could avoid some redundant checks here. For newtypes wrapping | |
798 | // scalars, we do the same check on every "level" (e.g., first we check | |
799 | // MyNewtype and then the scalar in there). | |
800 | match op.layout.abi { | |
ba9703b0 | 801 | Abi::Uninhabited => { |
f9f354fc XL |
802 | throw_validation_failure!(self.path, |
803 | { "a value of uninhabited type {:?}", op.layout.ty } | |
74b04a01 XL |
804 | ); |
805 | } | |
ba9703b0 | 806 | Abi::Scalar(ref scalar_layout) => { |
74b04a01 XL |
807 | self.visit_scalar(op, scalar_layout)?; |
808 | } | |
ba9703b0 | 809 | Abi::ScalarPair { .. } | Abi::Vector { .. } => { |
74b04a01 XL |
810 | // These have fields that we already visited above, so we already checked |
811 | // all their scalar-level restrictions. | |
812 | // There is also no equivalent to `rustc_layout_scalar_valid_range_start` | |
813 | // that would make skipping them here an issue. | |
814 | } | |
ba9703b0 | 815 | Abi::Aggregate { .. } => { |
74b04a01 XL |
816 | // Nothing to do. |
817 | } | |
818 | } | |
819 | ||
820 | Ok(()) | |
821 | } | |
a1dfa0c6 XL |
822 | |
823 | fn visit_aggregate( | |
824 | &mut self, | |
6a06907d | 825 | op: &OpTy<'tcx, M::PointerTag>, |
60c5eb7d | 826 | fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>, |
dc9dc135 | 827 | ) -> InterpResult<'tcx> { |
1b1a35ee | 828 | match op.layout.ty.kind() { |
a1dfa0c6 | 829 | ty::Str => { |
dfeec247 | 830 | let mplace = op.assert_mem_place(self.ecx); // strings are never immediate |
f9f354fc | 831 | let len = mplace.len(self.ecx)?; |
60c5eb7d | 832 | try_validation!( |
f9f354fc XL |
833 | self.ecx.memory.read_bytes(mplace.ptr, Size::from_bytes(len)), |
834 | self.path, | |
835 | err_ub!(InvalidUninitBytes(..)) => { "uninitialized data in `str`" }, | |
60c5eb7d | 836 | ); |
a1dfa0c6 | 837 | } |
60c5eb7d | 838 | ty::Array(tys, ..) | ty::Slice(tys) |
29967ef6 XL |
839 | // This optimization applies for types that can hold arbitrary bytes (such as |
840 | // integer and floating point types) or for structs or tuples with no fields. | |
841 | // FIXME(wesleywiser) This logic could be extended further to arbitrary structs | |
842 | // or tuples made up of integer/floating point types or inhabited ZSTs with no | |
843 | // padding. | |
844 | if matches!(tys.kind(), ty::Int(..) | ty::Uint(..) | ty::Float(..)) | |
845 | => | |
60c5eb7d XL |
846 | { |
847 | // Optimized handling for arrays of integer/float type. | |
848 | ||
dfeec247 XL |
849 | // Arrays cannot be immediate, slices are never immediate. |
850 | let mplace = op.assert_mem_place(self.ecx); | |
a1dfa0c6 XL |
851 | // This is the length of the array/slice. |
852 | let len = mplace.len(self.ecx)?; | |
dfeec247 | 853 | // Zero length slices have nothing to be checked. |
a1dfa0c6 XL |
854 | if len == 0 { |
855 | return Ok(()); | |
856 | } | |
857 | // This is the element type size. | |
dfeec247 | 858 | let layout = self.ecx.layout_of(tys)?; |
ba9703b0 | 859 | // This is the size in bytes of the whole array. (This checks for overflow.) |
dfeec247 | 860 | let size = layout.size * len; |
416331ca | 861 | // Size is not 0, get a pointer. |
dc9dc135 | 862 | let ptr = self.ecx.force_ptr(mplace.ptr)?; |
a1dfa0c6 | 863 | |
60c5eb7d | 864 | // Optimization: we just check the entire range at once. |
a1dfa0c6 XL |
865 | // NOTE: Keep this in sync with the handling of integer and float |
866 | // types above, in `visit_primitive`. | |
867 | // In run-time mode, we accept pointers in here. This is actually more | |
0731742a | 868 | // permissive than a per-element check would be, e.g., we accept |
a1dfa0c6 XL |
869 | // an &[u8] that contains a pointer even though bytewise checking would |
870 | // reject it. However, that's good: We don't inherently want | |
871 | // to reject those pointers, we just do not have the machinery to | |
872 | // talk about parts of a pointer. | |
3dfed10e | 873 | // We also accept uninit, for consistency with the slow path. |
60c5eb7d | 874 | match self.ecx.memory.get_raw(ptr.alloc_id)?.check_bytes( |
a1dfa0c6 XL |
875 | self.ecx, |
876 | ptr, | |
877 | size, | |
29967ef6 | 878 | /*allow_uninit_and_ptr*/ self.ctfe_mode.is_none(), |
a1dfa0c6 XL |
879 | ) { |
880 | // In the happy case, we needn't check anything else. | |
60c5eb7d | 881 | Ok(()) => {} |
a1dfa0c6 XL |
882 | // Some error happened, try to provide a more detailed description. |
883 | Err(err) => { | |
f9f354fc XL |
884 | // For some errors we might be able to provide extra information. |
885 | // (This custom logic does not fit the `try_validation!` macro.) | |
6a06907d | 886 | match err.kind() { |
f9f354fc | 887 | err_ub!(InvalidUninitBytes(Some(access))) => { |
ba9703b0 | 888 | // Some byte was uninitialized, determine which |
a1dfa0c6 XL |
889 | // element that byte belongs to so we can |
890 | // provide an index. | |
f9f354fc XL |
891 | let i = usize::try_from( |
892 | access.uninit_ptr.offset.bytes() / layout.size.bytes(), | |
893 | ) | |
894 | .unwrap(); | |
a1dfa0c6 XL |
895 | self.path.push(PathElem::ArrayElem(i)); |
896 | ||
f9f354fc | 897 | throw_validation_failure!(self.path, { "uninitialized bytes" }) |
60c5eb7d | 898 | } |
f035d41b XL |
899 | err_unsup!(ReadPointerAsBytes) => { |
900 | throw_validation_failure!(self.path, { "a pointer" } expected { "plain (non-pointer) bytes" }) | |
901 | } | |
902 | ||
f9f354fc | 903 | // Propagate upwards (that will also check for unexpected errors). |
a1dfa0c6 XL |
904 | _ => return Err(err), |
905 | } | |
0bf4aa26 | 906 | } |
a1dfa0c6 XL |
907 | } |
908 | } | |
dfeec247 XL |
909 | // Fast path for arrays and slices of ZSTs. We only need to check a single ZST element |
910 | // of an array and not all of them, because there's only a single value of a specific | |
911 | // ZST type, so either validation fails for all elements or none. | |
912 | ty::Array(tys, ..) | ty::Slice(tys) if self.ecx.layout_of(tys)?.is_zst() => { | |
29967ef6 | 913 | // Validate just the first element (if any). |
dfeec247 XL |
914 | self.walk_aggregate(op, fields.take(1))? |
915 | } | |
a1dfa0c6 XL |
916 | _ => { |
917 | self.walk_aggregate(op, fields)? // default handler | |
b7449926 XL |
918 | } |
919 | } | |
a1dfa0c6 | 920 | Ok(()) |
b7449926 | 921 | } |
a1dfa0c6 | 922 | } |
b7449926 | 923 | |
ba9703b0 | 924 | impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { |
74b04a01 | 925 | fn validate_operand_internal( |
b7449926 | 926 | &self, |
6a06907d | 927 | op: &OpTy<'tcx, M::PointerTag>, |
a1dfa0c6 | 928 | path: Vec<PathElem>, |
29967ef6 XL |
929 | ref_tracking: Option<&mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>>, |
930 | ctfe_mode: Option<CtfeValidationMode>, | |
dc9dc135 | 931 | ) -> InterpResult<'tcx> { |
74b04a01 | 932 | trace!("validate_operand_internal: {:?}, {:?}", *op, op.layout.ty); |
0bf4aa26 | 933 | |
a1dfa0c6 | 934 | // Construct a visitor |
29967ef6 | 935 | let mut visitor = ValidityVisitor { path, ref_tracking, ctfe_mode, ecx: self }; |
b7449926 | 936 | |
416331ca | 937 | // Try to cast to ptr *once* instead of all the time. |
6a06907d | 938 | let op = self.force_op_ptr(&op).unwrap_or(*op); |
416331ca | 939 | |
74b04a01 | 940 | // Run it. |
6a06907d | 941 | match visitor.visit_value(&op) { |
74b04a01 | 942 | Ok(()) => Ok(()), |
f9f354fc | 943 | // Pass through validation failures. |
6a06907d | 944 | Err(err) if matches!(err.kind(), err_ub!(ValidationFailure { .. })) => Err(err), |
f9f354fc XL |
945 | // Also pass through InvalidProgram, those just indicate that we could not |
946 | // validate and each caller will know best what to do with them. | |
6a06907d | 947 | Err(err) if matches!(err.kind(), InterpError::InvalidProgram(_)) => Err(err), |
f9f354fc XL |
948 | // Avoid other errors as those do not show *where* in the value the issue lies. |
949 | Err(err) => { | |
950 | err.print_backtrace(); | |
951 | bug!("Unexpected error during validation: {}", err); | |
952 | } | |
74b04a01 XL |
953 | } |
954 | } | |
955 | ||
956 | /// This function checks the data at `op` to be const-valid. | |
957 | /// `op` is assumed to cover valid memory if it is an indirect operand. | |
958 | /// It will error if the bits at the destination do not match the ones described by the layout. | |
959 | /// | |
960 | /// `ref_tracking` is used to record references that we encounter so that they | |
961 | /// can be checked recursively by an outside driving loop. | |
962 | /// | |
29967ef6 XL |
963 | /// `constant` controls whether this must satisfy the rules for constants: |
964 | /// - no pointers to statics. | |
965 | /// - no `UnsafeCell` or non-ZST `&mut`. | |
74b04a01 XL |
966 | #[inline(always)] |
967 | pub fn const_validate_operand( | |
968 | &self, | |
6a06907d | 969 | op: &OpTy<'tcx, M::PointerTag>, |
74b04a01 XL |
970 | path: Vec<PathElem>, |
971 | ref_tracking: &mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>, | |
29967ef6 | 972 | ctfe_mode: CtfeValidationMode, |
74b04a01 | 973 | ) -> InterpResult<'tcx> { |
29967ef6 | 974 | self.validate_operand_internal(op, path, Some(ref_tracking), Some(ctfe_mode)) |
74b04a01 XL |
975 | } |
976 | ||
977 | /// This function checks the data at `op` to be runtime-valid. | |
978 | /// `op` is assumed to cover valid memory if it is an indirect operand. | |
979 | /// It will error if the bits at the destination do not match the ones described by the layout. | |
980 | #[inline(always)] | |
6a06907d | 981 | pub fn validate_operand(&self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { |
29967ef6 | 982 | self.validate_operand_internal(op, vec![], None, None) |
b7449926 XL |
983 | } |
984 | } |