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