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