1 use super::{AllocId, ConstAlloc, Pointer, Scalar};
2
3 use crate::mir::interpret::ConstValue;
4 use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty};
5
6 use rustc_data_structures::sync::Lock;
7 use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorReported};
8 use rustc_macros::HashStable;
9 use rustc_session::CtfeBacktrace;
10 use rustc_span::def_id::DefId;
11 use rustc_target::abi::{Align, Size};
12 use std::{any::Any, backtrace::Backtrace, fmt};
13
14 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
15 pub enum ErrorHandled {
16 /// Already reported an error for this evaluation, and the compilation is
17 /// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`.
18 Reported(ErrorReported),
19 /// Already emitted a lint for this evaluation.
20 Linted,
21 /// Don't emit an error, the evaluation failed because the MIR was generic
22 /// and the substs didn't fully monomorphize it.
23 TooGeneric,
24 }
25
26 impl From<ErrorReported> for ErrorHandled {
from(err: ErrorReported) -> ErrorHandled27 fn from(err: ErrorReported) -> ErrorHandled {
28 ErrorHandled::Reported(err)
29 }
30 }
31
32 TrivialTypeFoldableAndLiftImpls! {
33 ErrorHandled,
34 }
35
36 pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
37 pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
38
struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx>39 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
40 struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
41 }
42
43 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
44 static_assert_size!(InterpErrorInfo<'_>, 8);
45
46 /// Packages the kind of error we got from the const code interpreter
47 /// up with a Rust-level backtrace of where the error occurred.
48 /// These should always be constructed by calling `.into()` on
49 /// an `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*`
50 /// macros for this.
51 #[derive(Debug)]
52 pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>);
53
54 #[derive(Debug)]
55 struct InterpErrorInfoInner<'tcx> {
56 kind: InterpError<'tcx>,
57 backtrace: Option<Box<Backtrace>>,
58 }
59
60 impl fmt::Display for InterpErrorInfo<'_> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 write!(f, "{}", self.0.kind)
63 }
64 }
65
66 impl InterpErrorInfo<'tcx> {
print_backtrace(&self)67 pub fn print_backtrace(&self) {
68 if let Some(backtrace) = self.0.backtrace.as_ref() {
69 print_backtrace(backtrace);
70 }
71 }
72
into_kind(self) -> InterpError<'tcx>73 pub fn into_kind(self) -> InterpError<'tcx> {
74 let InterpErrorInfo(box InterpErrorInfoInner { kind, .. }) = self;
75 kind
76 }
77
78 #[inline]
kind(&self) -> &InterpError<'tcx>79 pub fn kind(&self) -> &InterpError<'tcx> {
80 &self.0.kind
81 }
82 }
83
print_backtrace(backtrace: &Backtrace)84 fn print_backtrace(backtrace: &Backtrace) {
85 eprintln!("\n\nAn error occurred in miri:\n{}", backtrace);
86 }
87
88 impl From<ErrorHandled> for InterpErrorInfo<'_> {
from(err: ErrorHandled) -> Self89 fn from(err: ErrorHandled) -> Self {
90 match err {
91 ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {
92 err_inval!(ReferencedConstant)
93 }
94 ErrorHandled::TooGeneric => err_inval!(TooGeneric),
95 }
96 .into()
97 }
98 }
99
100 impl From<ErrorReported> for InterpErrorInfo<'_> {
from(err: ErrorReported) -> Self101 fn from(err: ErrorReported) -> Self {
102 InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err)).into()
103 }
104 }
105
106 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
from(kind: InterpError<'tcx>) -> Self107 fn from(kind: InterpError<'tcx>) -> Self {
108 let capture_backtrace = tls::with_opt(|tcx| {
109 if let Some(tcx) = tcx {
110 *Lock::borrow(&tcx.sess.ctfe_backtrace)
111 } else {
112 CtfeBacktrace::Disabled
113 }
114 });
115
116 let backtrace = match capture_backtrace {
117 CtfeBacktrace::Disabled => None,
118 CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
119 CtfeBacktrace::Immediate => {
120 // Print it now.
121 let backtrace = Backtrace::force_capture();
122 print_backtrace(&backtrace);
123 None
124 }
125 };
126
127 InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace }))
128 }
129 }
130
131 /// Error information for when the program we executed turned out not to actually be a valid
132 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
133 /// where we work on generic code or execution does not have all information available.
134 pub enum InvalidProgramInfo<'tcx> {
135 /// Resolution can fail if we are in a too generic context.
136 TooGeneric,
137 /// Cannot compute this constant because it depends on another one
138 /// which already produced an error.
139 ReferencedConstant,
140 /// Abort in case errors are already reported.
141 AlreadyReported(ErrorReported),
142 /// An error occurred during layout computation.
143 Layout(layout::LayoutError<'tcx>),
144 /// An invalid transmute happened.
145 TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
146 /// SizeOf of unsized type was requested.
147 SizeOfUnsizedType(Ty<'tcx>),
148 }
149
150 impl fmt::Display for InvalidProgramInfo<'_> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 use InvalidProgramInfo::*;
153 match self {
154 TooGeneric => write!(f, "encountered overly generic constant"),
155 ReferencedConstant => write!(f, "referenced constant has errors"),
156 AlreadyReported(ErrorReported) => {
157 write!(f, "encountered constants with type errors, stopping evaluation")
158 }
159 Layout(ref err) => write!(f, "{}", err),
160 TransmuteSizeDiff(from_ty, to_ty) => write!(
161 f,
162 "transmuting `{}` to `{}` is not possible, because these types do not have the same size",
163 from_ty, to_ty
164 ),
165 SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{}`", ty),
166 }
167 }
168 }
169
170 /// Details of why a pointer had to be in-bounds.
171 #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
172 pub enum CheckInAllocMsg {
173 /// We are dereferencing a pointer (i.e., creating a place).
174 DerefTest,
175 /// We are access memory.
176 MemoryAccessTest,
177 /// We are doing pointer arithmetic.
178 PointerArithmeticTest,
179 /// None of the above -- generic/unspecific inbounds test.
180 InboundsTest,
181 }
182
183 impl fmt::Display for CheckInAllocMsg {
184 /// When this is printed as an error the context looks like this:
185 /// "{msg}0x01 is not a valid pointer".
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 write!(
188 f,
189 "{}",
190 match *self {
191 CheckInAllocMsg::DerefTest => "dereferencing pointer failed: ",
192 CheckInAllocMsg::MemoryAccessTest => "memory access failed: ",
193 CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic failed: ",
194 CheckInAllocMsg::InboundsTest => "",
195 }
196 )
197 }
198 }
199
200 /// Details of an access to uninitialized bytes where it is not allowed.
201 #[derive(Debug)]
202 pub struct UninitBytesAccess {
203 /// Location of the original memory access.
204 pub access_offset: Size,
205 /// Size of the original memory access.
206 pub access_size: Size,
207 /// Location of the first uninitialized byte that was accessed.
208 pub uninit_offset: Size,
209 /// Number of consecutive uninitialized bytes that were accessed.
210 pub uninit_size: Size,
211 }
212
213 /// Error information for when the program caused Undefined Behavior.
214 pub enum UndefinedBehaviorInfo<'tcx> {
215 /// Free-form case. Only for errors that are never caught!
216 Ub(String),
217 /// Unreachable code was executed.
218 Unreachable,
219 /// A slice/array index projection went out-of-bounds.
220 BoundsCheckFailed {
221 len: u64,
222 index: u64,
223 },
224 /// Something was divided by 0 (x / 0).
225 DivisionByZero,
226 /// Something was "remainded" by 0 (x % 0).
227 RemainderByZero,
228 /// Overflowing inbounds pointer arithmetic.
229 PointerArithOverflow,
230 /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
231 InvalidMeta(&'static str),
232 /// Invalid drop function in vtable.
233 InvalidVtableDropFn(FnSig<'tcx>),
234 /// Invalid size in a vtable: too large.
235 InvalidVtableSize,
236 /// Invalid alignment in a vtable: too large, or not a power of 2.
237 InvalidVtableAlignment(String),
238 /// Reading a C string that does not end within its allocation.
239 UnterminatedCString(Pointer),
240 /// Dereferencing a dangling pointer after it got freed.
241 PointerUseAfterFree(AllocId),
242 /// Used a pointer outside the bounds it is valid for.
243 /// (If `ptr_size > 0`, determines the size of the memory range that was expected to be in-bounds.)
244 PointerOutOfBounds {
245 alloc_id: AllocId,
246 alloc_size: Size,
247 ptr_offset: i64,
248 ptr_size: Size,
249 msg: CheckInAllocMsg,
250 },
251 /// Using an integer as a pointer in the wrong way.
252 DanglingIntPointer(u64, CheckInAllocMsg),
253 /// Used a pointer with bad alignment.
254 AlignmentCheckFailed {
255 required: Align,
256 has: Align,
257 },
258 /// Writing to read-only memory.
259 WriteToReadOnly(AllocId),
260 // Trying to access the data behind a function pointer.
261 DerefFunctionPointer(AllocId),
262 /// The value validity check found a problem.
263 /// Should only be thrown by `validity.rs` and always point out which part of the value
264 /// is the problem.
265 ValidationFailure {
266 /// The "path" to the value in question, e.g. `.0[5].field` for a struct
267 /// field in the 6th element of an array that is the first element of a tuple.
268 path: Option<String>,
269 msg: String,
270 },
271 /// Using a non-boolean `u8` as bool.
272 InvalidBool(u8),
273 /// Using a non-character `u32` as character.
274 InvalidChar(u32),
275 /// The tag of an enum does not encode an actual discriminant.
276 InvalidTag(Scalar),
277 /// Using a pointer-not-to-a-function as function pointer.
278 InvalidFunctionPointer(Pointer),
279 /// Using a string that is not valid UTF-8,
280 InvalidStr(std::str::Utf8Error),
281 /// Using uninitialized data where it is not allowed.
282 InvalidUninitBytes(Option<(AllocId, UninitBytesAccess)>),
283 /// Working with a local that is not currently live.
284 DeadLocal,
285 /// Data size is not equal to target size.
286 ScalarSizeMismatch {
287 target_size: u64,
288 data_size: u64,
289 },
290 /// A discriminant of an uninhabited enum variant is written.
291 UninhabitedEnumVariantWritten,
292 }
293
294 impl fmt::Display for UndefinedBehaviorInfo<'_> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result295 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296 use UndefinedBehaviorInfo::*;
297 match self {
298 Ub(msg) => write!(f, "{}", msg),
299 Unreachable => write!(f, "entering unreachable code"),
300 BoundsCheckFailed { ref len, ref index } => {
301 write!(f, "indexing out of bounds: the len is {} but the index is {}", len, index)
302 }
303 DivisionByZero => write!(f, "dividing by zero"),
304 RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
305 PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
306 InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
307 InvalidVtableDropFn(sig) => write!(
308 f,
309 "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type",
310 sig
311 ),
312 InvalidVtableSize => {
313 write!(f, "invalid vtable: size is bigger than largest supported object")
314 }
315 InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {}", msg),
316 UnterminatedCString(p) => write!(
317 f,
318 "reading a null-terminated string starting at {:?} with no null found before end of allocation",
319 p,
320 ),
321 PointerUseAfterFree(a) => {
322 write!(f, "pointer to {} was dereferenced after this allocation got freed", a)
323 }
324 PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size: Size::ZERO, msg } => {
325 write!(
326 f,
327 "{}{alloc_id} has size {alloc_size}, so pointer at offset {ptr_offset} is out-of-bounds",
328 msg,
329 alloc_id = alloc_id,
330 alloc_size = alloc_size.bytes(),
331 ptr_offset = ptr_offset,
332 )
333 }
334 PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => write!(
335 f,
336 "{}{alloc_id} has size {alloc_size}, so pointer to {ptr_size} byte{ptr_size_p} starting at offset {ptr_offset} is out-of-bounds",
337 msg,
338 alloc_id = alloc_id,
339 alloc_size = alloc_size.bytes(),
340 ptr_size = ptr_size.bytes(),
341 ptr_size_p = pluralize!(ptr_size.bytes()),
342 ptr_offset = ptr_offset,
343 ),
344 DanglingIntPointer(0, CheckInAllocMsg::InboundsTest) => {
345 write!(f, "null pointer is not a valid pointer for this operation")
346 }
347 DanglingIntPointer(i, msg) => {
348 write!(f, "{}0x{:x} is not a valid pointer", msg, i)
349 }
350 AlignmentCheckFailed { required, has } => write!(
351 f,
352 "accessing memory with alignment {}, but alignment {} is required",
353 has.bytes(),
354 required.bytes()
355 ),
356 WriteToReadOnly(a) => write!(f, "writing to {} which is read-only", a),
357 DerefFunctionPointer(a) => write!(f, "accessing {} which contains a function", a),
358 ValidationFailure { path: None, msg } => write!(f, "type validation failed: {}", msg),
359 ValidationFailure { path: Some(path), msg } => {
360 write!(f, "type validation failed at {}: {}", path, msg)
361 }
362 InvalidBool(b) => {
363 write!(f, "interpreting an invalid 8-bit value as a bool: 0x{:02x}", b)
364 }
365 InvalidChar(c) => {
366 write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c)
367 }
368 InvalidTag(val) => write!(f, "enum value has invalid tag: {}", val),
369 InvalidFunctionPointer(p) => {
370 write!(f, "using {:?} as function pointer but it does not point to a function", p)
371 }
372 InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err),
373 InvalidUninitBytes(Some((alloc, access))) => write!(
374 f,
375 "reading {} byte{} of memory starting at {:?}, \
376 but {} byte{} {} uninitialized starting at {:?}, \
377 and this operation requires initialized memory",
378 access.access_size.bytes(),
379 pluralize!(access.access_size.bytes()),
380 Pointer::new(*alloc, access.access_offset),
381 access.uninit_size.bytes(),
382 pluralize!(access.uninit_size.bytes()),
383 if access.uninit_size.bytes() != 1 { "are" } else { "is" },
384 Pointer::new(*alloc, access.uninit_offset),
385 ),
386 InvalidUninitBytes(None) => write!(
387 f,
388 "using uninitialized data, but this operation requires initialized memory"
389 ),
390 DeadLocal => write!(f, "accessing a dead local variable"),
391 ScalarSizeMismatch { target_size, data_size } => write!(
392 f,
393 "scalar size mismatch: expected {} bytes but got {} bytes instead",
394 target_size, data_size
395 ),
396 UninhabitedEnumVariantWritten => {
397 write!(f, "writing discriminant of an uninhabited enum")
398 }
399 }
400 }
401 }
402
403 /// Error information for when the program did something that might (or might not) be correct
404 /// to do according to the Rust spec, but due to limitations in the interpreter, the
405 /// operation could not be carried out. These limitations can differ between CTFE and the
406 /// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
407 pub enum UnsupportedOpInfo {
408 /// Free-form case. Only for errors that are never caught!
409 Unsupported(String),
410 /// Encountered a pointer where we needed raw bytes.
411 ReadPointerAsBytes,
412 /// Overwriting parts of a pointer; the resulting state cannot be represented in our
413 /// `Allocation` data structure.
414 PartialPointerOverwrite(Pointer<AllocId>),
415 //
416 // The variants below are only reachable from CTFE/const prop, miri will never emit them.
417 //
418 /// Accessing thread local statics
419 ThreadLocalStatic(DefId),
420 /// Accessing an unsupported extern static.
421 ReadExternStatic(DefId),
422 }
423
424 impl fmt::Display for UnsupportedOpInfo {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result425 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
426 use UnsupportedOpInfo::*;
427 match self {
428 Unsupported(ref msg) => write!(f, "{}", msg),
429 ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes"),
430 PartialPointerOverwrite(ptr) => {
431 write!(f, "unable to overwrite parts of a pointer in memory at {:?}", ptr)
432 }
433 ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did),
434 ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did),
435 }
436 }
437 }
438
439 /// Error information for when the program exhausted the resources granted to it
440 /// by the interpreter.
441 pub enum ResourceExhaustionInfo {
442 /// The stack grew too big.
443 StackFrameLimitReached,
444 /// The program ran for too long.
445 ///
446 /// The exact limit is set by the `const_eval_limit` attribute.
447 StepLimitReached,
448 /// There is not enough memory to perform an allocation.
449 MemoryExhausted,
450 }
451
452 impl fmt::Display for ResourceExhaustionInfo {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result453 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454 use ResourceExhaustionInfo::*;
455 match self {
456 StackFrameLimitReached => {
457 write!(f, "reached the configured maximum number of stack frames")
458 }
459 StepLimitReached => {
460 write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
461 }
462 MemoryExhausted => {
463 write!(f, "tried to allocate more memory than available to compiler")
464 }
465 }
466 }
467 }
468
469 /// A trait to work around not having trait object upcasting.
470 pub trait AsAny: Any {
as_any(&self) -> &dyn Any471 fn as_any(&self) -> &dyn Any;
472 }
473 impl<T: Any> AsAny for T {
474 #[inline(always)]
as_any(&self) -> &dyn Any475 fn as_any(&self) -> &dyn Any {
476 self
477 }
478 }
479
480 /// A trait for machine-specific errors (or other "machine stop" conditions).
481 pub trait MachineStopType: AsAny + fmt::Display + Send {
482 /// If `true`, emit a hard error instead of going through the `CONST_ERR` lint
is_hard_err(&self) -> bool483 fn is_hard_err(&self) -> bool {
484 false
485 }
486 }
487
488 impl dyn MachineStopType {
489 #[inline(always)]
downcast_ref<T: Any>(&self) -> Option<&T>490 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
491 self.as_any().downcast_ref()
492 }
493 }
494
495 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
496 static_assert_size!(InterpError<'_>, 64);
497
498 pub enum InterpError<'tcx> {
499 /// The program caused undefined behavior.
500 UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
501 /// The program did something the interpreter does not support (some of these *might* be UB
502 /// but the interpreter is not sure).
503 Unsupported(UnsupportedOpInfo),
504 /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
505 InvalidProgram(InvalidProgramInfo<'tcx>),
506 /// The program exhausted the interpreter's resources (stack/heap too big,
507 /// execution takes too long, ...).
508 ResourceExhaustion(ResourceExhaustionInfo),
509 /// Stop execution for a machine-controlled reason. This is never raised by
510 /// the core engine itself.
511 MachineStop(Box<dyn MachineStopType>),
512 }
513
514 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
515
516 impl fmt::Display for InterpError<'_> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result517 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
518 use InterpError::*;
519 match *self {
520 Unsupported(ref msg) => write!(f, "{}", msg),
521 InvalidProgram(ref msg) => write!(f, "{}", msg),
522 UndefinedBehavior(ref msg) => write!(f, "{}", msg),
523 ResourceExhaustion(ref msg) => write!(f, "{}", msg),
524 MachineStop(ref msg) => write!(f, "{}", msg),
525 }
526 }
527 }
528
529 // Forward `Debug` to `Display`, so it does not look awful.
530 impl fmt::Debug for InterpError<'_> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result531 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532 fmt::Display::fmt(self, f)
533 }
534 }
535
536 impl InterpError<'_> {
537 /// Some errors do string formatting even if the error is never printed.
538 /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors,
539 /// so this method lets us detect them and `bug!` on unexpected errors.
formatted_string(&self) -> bool540 pub fn formatted_string(&self) -> bool {
541 matches!(
542 self,
543 InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
544 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure { .. })
545 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
546 )
547 }
548
549 /// Should this error be reported as a hard error, preventing compilation, or a soft error,
550 /// causing a deny-by-default lint?
is_hard_err(&self) -> bool551 pub fn is_hard_err(&self) -> bool {
552 use InterpError::*;
553 match *self {
554 MachineStop(ref err) => err.is_hard_err(),
555 UndefinedBehavior(_) => true,
556 ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) => true,
557 _ => false,
558 }
559 }
560 }
561