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