]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_const_eval/src/interpret/validity.rs
New upstream version 1.70.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
f2b60f7d 7use std::fmt::{Display, Write};
ba9703b0 8use std::num::NonZeroUsize;
b7449926 9
487cf647
FG
10use either::{Left, Right};
11
f2b60f7d 12use rustc_ast::Mutability;
b7449926 13use rustc_data_structures::fx::FxHashSet;
dfeec247 14use rustc_hir as hir;
6a06907d 15use rustc_middle::mir::interpret::InterpError;
ba9703b0 16use rustc_middle::ty;
c295e0f8 17use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
dfeec247 18use rustc_span::symbol::{sym, Symbol};
353b0b11
FG
19use rustc_target::abi::{
20 Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
21};
b7449926 22
dc9dc135
XL
23use std::hash::Hash;
24
f2b60f7d
FG
25// for the validation errors
26use super::UndefinedBehaviorInfo::*;
b7449926 27use super::{
9ffffee4
FG
28 AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy,
29 Machine, MemPlaceMeta, OpTy, Pointer, Scalar, ValueVisitor,
b7449926
XL
30};
31
416331ca 32macro_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 85macro_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)]
113pub 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.
127pub 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 139pub struct RefTracking<T, PATH = ()> {
9fa01778 140 pub seen: FxHashSet<T>,
dc9dc135 141 pub todo: Vec<(T, PATH)>,
0bf4aa26
XL
142}
143
dc9dc135
XL
144impl<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 166fn 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
192fn 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 210struct 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
221impl<'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 672impl<'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 917impl<'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}