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