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