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