• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::{AllocId, AllocRange, ConstAlloc, Pointer, Scalar};
2 
3 use crate::mir::interpret::ConstValue;
4 use crate::query::TyCtxtAt;
5 use crate::ty::{layout, tls, Ty, ValTree};
6 
7 use rustc_data_structures::sync::Lock;
8 use rustc_errors::{
9     struct_span_err, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
10     IntoDiagnosticArg,
11 };
12 use rustc_macros::HashStable;
13 use rustc_session::CtfeBacktrace;
14 use rustc_span::def_id::DefId;
15 use rustc_target::abi::{call, Align, Size, WrappingRange};
16 use std::borrow::Cow;
17 use std::{any::Any, backtrace::Backtrace, fmt};
18 
19 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
20 pub enum ErrorHandled {
21     /// Already reported an error for this evaluation, and the compilation is
22     /// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`.
23     Reported(ReportedErrorInfo),
24     /// Don't emit an error, the evaluation failed because the MIR was generic
25     /// and the substs didn't fully monomorphize it.
26     TooGeneric,
27 }
28 
29 impl From<ErrorGuaranteed> for ErrorHandled {
30     #[inline]
from(error: ErrorGuaranteed) -> ErrorHandled31     fn from(error: ErrorGuaranteed) -> ErrorHandled {
32         ErrorHandled::Reported(error.into())
33     }
34 }
35 
36 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
37 pub struct ReportedErrorInfo {
38     error: ErrorGuaranteed,
39     is_tainted_by_errors: bool,
40 }
41 
42 impl ReportedErrorInfo {
43     #[inline]
tainted_by_errors(error: ErrorGuaranteed) -> ReportedErrorInfo44     pub fn tainted_by_errors(error: ErrorGuaranteed) -> ReportedErrorInfo {
45         ReportedErrorInfo { is_tainted_by_errors: true, error }
46     }
47 
48     /// Returns true if evaluation failed because MIR was tainted by errors.
49     #[inline]
is_tainted_by_errors(self) -> bool50     pub fn is_tainted_by_errors(self) -> bool {
51         self.is_tainted_by_errors
52     }
53 }
54 
55 impl From<ErrorGuaranteed> for ReportedErrorInfo {
56     #[inline]
from(error: ErrorGuaranteed) -> ReportedErrorInfo57     fn from(error: ErrorGuaranteed) -> ReportedErrorInfo {
58         ReportedErrorInfo { is_tainted_by_errors: false, error }
59     }
60 }
61 
62 impl Into<ErrorGuaranteed> for ReportedErrorInfo {
63     #[inline]
into(self) -> ErrorGuaranteed64     fn into(self) -> ErrorGuaranteed {
65         self.error
66     }
67 }
68 
69 TrivialTypeTraversalAndLiftImpls! {
70     ErrorHandled,
71 }
72 
73 pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
74 pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
75 pub type EvalToValTreeResult<'tcx> = Result<Option<ValTree<'tcx>>, ErrorHandled>;
76 
struct_error<'tcx>( tcx: TyCtxtAt<'tcx>, msg: &str, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>77 pub fn struct_error<'tcx>(
78     tcx: TyCtxtAt<'tcx>,
79     msg: &str,
80 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
81     struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
82 }
83 
84 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
85 static_assert_size!(InterpErrorInfo<'_>, 8);
86 
87 /// Packages the kind of error we got from the const code interpreter
88 /// up with a Rust-level backtrace of where the error occurred.
89 /// These should always be constructed by calling `.into()` on
90 /// an `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*`
91 /// macros for this.
92 #[derive(Debug)]
93 pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>);
94 
95 #[derive(Debug)]
96 struct InterpErrorInfoInner<'tcx> {
97     kind: InterpError<'tcx>,
98     backtrace: InterpErrorBacktrace,
99 }
100 
101 #[derive(Debug)]
102 pub struct InterpErrorBacktrace {
103     backtrace: Option<Box<Backtrace>>,
104 }
105 
106 impl InterpErrorBacktrace {
new() -> InterpErrorBacktrace107     pub fn new() -> InterpErrorBacktrace {
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         InterpErrorBacktrace { backtrace }
128     }
129 
print_backtrace(&self)130     pub fn print_backtrace(&self) {
131         if let Some(backtrace) = self.backtrace.as_ref() {
132             print_backtrace(backtrace);
133         }
134     }
135 }
136 
137 impl<'tcx> InterpErrorInfo<'tcx> {
from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self138     pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self {
139         Self(Box::new(InterpErrorInfoInner { kind, backtrace }))
140     }
141 
into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace)142     pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) {
143         let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self;
144         (kind, backtrace)
145     }
146 
into_kind(self) -> InterpError<'tcx>147     pub fn into_kind(self) -> InterpError<'tcx> {
148         let InterpErrorInfo(box InterpErrorInfoInner { kind, .. }) = self;
149         kind
150     }
151 
152     #[inline]
kind(&self) -> &InterpError<'tcx>153     pub fn kind(&self) -> &InterpError<'tcx> {
154         &self.0.kind
155     }
156 }
157 
print_backtrace(backtrace: &Backtrace)158 fn print_backtrace(backtrace: &Backtrace) {
159     eprintln!("\n\nAn error occurred in miri:\n{}", backtrace);
160 }
161 
162 impl From<ErrorGuaranteed> for InterpErrorInfo<'_> {
from(err: ErrorGuaranteed) -> Self163     fn from(err: ErrorGuaranteed) -> Self {
164         InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err.into())).into()
165     }
166 }
167 
168 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
from(kind: InterpError<'tcx>) -> Self169     fn from(kind: InterpError<'tcx>) -> Self {
170         InterpErrorInfo(Box::new(InterpErrorInfoInner {
171             kind,
172             backtrace: InterpErrorBacktrace::new(),
173         }))
174     }
175 }
176 
177 /// Error information for when the program we executed turned out not to actually be a valid
178 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
179 /// where we work on generic code or execution does not have all information available.
180 #[derive(Debug)]
181 pub enum InvalidProgramInfo<'tcx> {
182     /// Resolution can fail if we are in a too generic context.
183     TooGeneric,
184     /// Abort in case errors are already reported.
185     AlreadyReported(ReportedErrorInfo),
186     /// An error occurred during layout computation.
187     Layout(layout::LayoutError<'tcx>),
188     /// An error occurred during FnAbi computation: the passed --target lacks FFI support
189     /// (which unfortunately typeck does not reject).
190     /// Not using `FnAbiError` as that contains a nested `LayoutError`.
191     FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError),
192     /// SizeOf of unsized type was requested.
193     SizeOfUnsizedType(Ty<'tcx>),
194     /// An unsized local was accessed without having been initialized.
195     /// This is not meaningful as we can't even have backing memory for such locals.
196     UninitUnsizedLocal,
197 }
198 
199 /// Details of why a pointer had to be in-bounds.
200 #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
201 pub enum CheckInAllocMsg {
202     /// We are dereferencing a pointer (i.e., creating a place).
203     DerefTest,
204     /// We are access memory.
205     MemoryAccessTest,
206     /// We are doing pointer arithmetic.
207     PointerArithmeticTest,
208     /// We are doing pointer offset_from.
209     OffsetFromTest,
210     /// None of the above -- generic/unspecific inbounds test.
211     InboundsTest,
212 }
213 
214 #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
215 pub enum InvalidMetaKind {
216     /// Size of a `[T]` is too big
217     SliceTooBig,
218     /// Size of a DST is too big
219     TooBig,
220 }
221 
222 impl IntoDiagnosticArg for InvalidMetaKind {
into_diagnostic_arg(self) -> DiagnosticArgValue<'static>223     fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
224         DiagnosticArgValue::Str(Cow::Borrowed(match self {
225             InvalidMetaKind::SliceTooBig => "slice_too_big",
226             InvalidMetaKind::TooBig => "too_big",
227         }))
228     }
229 }
230 
231 /// Details of an access to uninitialized bytes where it is not allowed.
232 #[derive(Debug, Clone, Copy)]
233 pub struct UninitBytesAccess {
234     /// Range of the original memory access.
235     pub access: AllocRange,
236     /// Range of the uninit memory that was encountered. (Might not be maximal.)
237     pub uninit: AllocRange,
238 }
239 
240 /// Information about a size mismatch.
241 #[derive(Debug)]
242 pub struct ScalarSizeMismatch {
243     pub target_size: u64,
244     pub data_size: u64,
245 }
246 
247 macro_rules! impl_into_diagnostic_arg_through_debug {
248     ($($ty:ty),*$(,)?) => {$(
249         impl IntoDiagnosticArg for $ty {
250             fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
251                 DiagnosticArgValue::Str(Cow::Owned(format!("{self:?}")))
252             }
253         }
254     )*}
255 }
256 
257 // These types have nice `Debug` output so we can just use them in diagnostics.
258 impl_into_diagnostic_arg_through_debug! {
259     AllocId,
260     Pointer,
261     AllocRange,
262 }
263 
264 /// Error information for when the program caused Undefined Behavior.
265 #[derive(Debug)]
266 pub enum UndefinedBehaviorInfo<'a> {
267     /// Free-form case. Only for errors that are never caught! Used by miri
268     Ub(String),
269     /// Unreachable code was executed.
270     Unreachable,
271     /// A slice/array index projection went out-of-bounds.
272     BoundsCheckFailed { len: u64, index: u64 },
273     /// Something was divided by 0 (x / 0).
274     DivisionByZero,
275     /// Something was "remainded" by 0 (x % 0).
276     RemainderByZero,
277     /// Signed division overflowed (INT_MIN / -1).
278     DivisionOverflow,
279     /// Signed remainder overflowed (INT_MIN % -1).
280     RemainderOverflow,
281     /// Overflowing inbounds pointer arithmetic.
282     PointerArithOverflow,
283     /// Invalid metadata in a wide pointer
284     InvalidMeta(InvalidMetaKind),
285     /// Reading a C string that does not end within its allocation.
286     UnterminatedCString(Pointer),
287     /// Dereferencing a dangling pointer after it got freed.
288     PointerUseAfterFree(AllocId),
289     /// Used a pointer outside the bounds it is valid for.
290     /// (If `ptr_size > 0`, determines the size of the memory range that was expected to be in-bounds.)
291     PointerOutOfBounds {
292         alloc_id: AllocId,
293         alloc_size: Size,
294         ptr_offset: i64,
295         ptr_size: Size,
296         msg: CheckInAllocMsg,
297     },
298     /// Using an integer as a pointer in the wrong way.
299     DanglingIntPointer(u64, CheckInAllocMsg),
300     /// Used a pointer with bad alignment.
301     AlignmentCheckFailed { required: Align, has: Align },
302     /// Writing to read-only memory.
303     WriteToReadOnly(AllocId),
304     /// Trying to access the data behind a function pointer.
305     DerefFunctionPointer(AllocId),
306     /// Trying to access the data behind a vtable pointer.
307     DerefVTablePointer(AllocId),
308     /// Using a non-boolean `u8` as bool.
309     InvalidBool(u8),
310     /// Using a non-character `u32` as character.
311     InvalidChar(u32),
312     /// The tag of an enum does not encode an actual discriminant.
313     InvalidTag(Scalar),
314     /// Using a pointer-not-to-a-function as function pointer.
315     InvalidFunctionPointer(Pointer),
316     /// Using a pointer-not-to-a-vtable as vtable pointer.
317     InvalidVTablePointer(Pointer),
318     /// Using a string that is not valid UTF-8,
319     InvalidStr(std::str::Utf8Error),
320     /// Using uninitialized data where it is not allowed.
321     InvalidUninitBytes(Option<(AllocId, UninitBytesAccess)>),
322     /// Working with a local that is not currently live.
323     DeadLocal,
324     /// Data size is not equal to target size.
325     ScalarSizeMismatch(ScalarSizeMismatch),
326     /// A discriminant of an uninhabited enum variant is written.
327     UninhabitedEnumVariantWritten,
328     /// Validation error.
329     Validation(ValidationErrorInfo<'a>),
330     // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
331     // dispatched
332     /// A custom (free-form) error, created by `err_ub_custom!`.
333     Custom(crate::error::CustomSubdiagnostic<'a>),
334 }
335 
336 #[derive(Debug, Clone, Copy)]
337 pub enum PointerKind {
338     Ref,
339     Box,
340 }
341 
342 impl IntoDiagnosticArg for PointerKind {
into_diagnostic_arg(self) -> DiagnosticArgValue<'static>343     fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
344         DiagnosticArgValue::Str(
345             match self {
346                 Self::Ref => "ref",
347                 Self::Box => "box",
348             }
349             .into(),
350         )
351     }
352 }
353 
354 #[derive(Debug)]
355 pub struct ValidationErrorInfo<'tcx> {
356     pub path: Option<String>,
357     pub kind: ValidationErrorKind<'tcx>,
358 }
359 
360 #[derive(Debug)]
361 pub enum ExpectedKind {
362     Reference,
363     Box,
364     RawPtr,
365     InitScalar,
366     Bool,
367     Char,
368     Float,
369     Int,
370     FnPtr,
371 }
372 
373 impl From<PointerKind> for ExpectedKind {
from(x: PointerKind) -> ExpectedKind374     fn from(x: PointerKind) -> ExpectedKind {
375         match x {
376             PointerKind::Box => ExpectedKind::Box,
377             PointerKind::Ref => ExpectedKind::Reference,
378         }
379     }
380 }
381 
382 #[derive(Debug)]
383 pub enum ValidationErrorKind<'tcx> {
384     PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> },
385     PtrToStatic { ptr_kind: PointerKind },
386     PtrToMut { ptr_kind: PointerKind },
387     ExpectedNonPtr { value: String },
388     MutableRefInConst,
389     NullFnPtr,
390     NeverVal,
391     NullablePtrOutOfRange { range: WrappingRange, max_value: u128 },
392     PtrOutOfRange { range: WrappingRange, max_value: u128 },
393     OutOfRange { value: String, range: WrappingRange, max_value: u128 },
394     UnsafeCell,
395     UninhabitedVal { ty: Ty<'tcx> },
396     InvalidEnumTag { value: String },
397     UninitEnumTag,
398     UninitStr,
399     Uninit { expected: ExpectedKind },
400     UninitVal,
401     InvalidVTablePtr { value: String },
402     InvalidMetaSliceTooLarge { ptr_kind: PointerKind },
403     InvalidMetaTooLarge { ptr_kind: PointerKind },
404     UnalignedPtr { ptr_kind: PointerKind, required_bytes: u64, found_bytes: u64 },
405     NullPtr { ptr_kind: PointerKind },
406     DanglingPtrNoProvenance { ptr_kind: PointerKind, pointer: String },
407     DanglingPtrOutOfBounds { ptr_kind: PointerKind },
408     DanglingPtrUseAfterFree { ptr_kind: PointerKind },
409     InvalidBool { value: String },
410     InvalidChar { value: String },
411     InvalidFnPtr { value: String },
412 }
413 
414 /// Error information for when the program did something that might (or might not) be correct
415 /// to do according to the Rust spec, but due to limitations in the interpreter, the
416 /// operation could not be carried out. These limitations can differ between CTFE and the
417 /// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
418 #[derive(Debug)]
419 pub enum UnsupportedOpInfo {
420     /// Free-form case. Only for errors that are never caught!
421     // FIXME still use translatable diagnostics
422     Unsupported(String),
423     //
424     // The variants below are only reachable from CTFE/const prop, miri will never emit them.
425     //
426     /// Overwriting parts of a pointer; without knowing absolute addresses, the resulting state
427     /// cannot be represented by the CTFE interpreter.
428     PartialPointerOverwrite(Pointer<AllocId>),
429     /// Attempting to `copy` parts of a pointer to somewhere else; without knowing absolute
430     /// addresses, the resulting state cannot be represented by the CTFE interpreter.
431     PartialPointerCopy(Pointer<AllocId>),
432     /// Encountered a pointer where we needed raw bytes.
433     ReadPointerAsBytes,
434     /// Accessing thread local statics
435     ThreadLocalStatic(DefId),
436     /// Accessing an unsupported extern static.
437     ReadExternStatic(DefId),
438 }
439 
440 /// Error information for when the program exhausted the resources granted to it
441 /// by the interpreter.
442 #[derive(Debug)]
443 pub enum ResourceExhaustionInfo {
444     /// The stack grew too big.
445     StackFrameLimitReached,
446     /// There is not enough memory (on the host) to perform an allocation.
447     MemoryExhausted,
448     /// The address space (of the target) is full.
449     AddressSpaceFull,
450 }
451 
452 /// A trait for machine-specific errors (or other "machine stop" conditions).
453 pub trait MachineStopType: Any + fmt::Debug + Send {
454     /// The diagnostic message for this error
diagnostic_message(&self) -> DiagnosticMessage455     fn diagnostic_message(&self) -> DiagnosticMessage;
456     /// Add diagnostic arguments by passing name and value pairs to `adder`, which are passed to
457     /// fluent for formatting the translated diagnostic message.
add_args( self: Box<Self>, adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>), )458     fn add_args(
459         self: Box<Self>,
460         adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>),
461     );
462 }
463 
464 impl dyn MachineStopType {
465     #[inline(always)]
downcast_ref<T: Any>(&self) -> Option<&T>466     pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
467         let x: &dyn Any = self;
468         x.downcast_ref()
469     }
470 }
471 
472 #[derive(Debug)]
473 pub enum InterpError<'tcx> {
474     /// The program caused undefined behavior.
475     UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
476     /// The program did something the interpreter does not support (some of these *might* be UB
477     /// but the interpreter is not sure).
478     Unsupported(UnsupportedOpInfo),
479     /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
480     InvalidProgram(InvalidProgramInfo<'tcx>),
481     /// The program exhausted the interpreter's resources (stack/heap too big,
482     /// execution takes too long, ...).
483     ResourceExhaustion(ResourceExhaustionInfo),
484     /// Stop execution for a machine-controlled reason. This is never raised by
485     /// the core engine itself.
486     MachineStop(Box<dyn MachineStopType>),
487 }
488 
489 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
490 
491 impl InterpError<'_> {
492     /// Some errors do string formatting even if the error is never printed.
493     /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors,
494     /// so this method lets us detect them and `bug!` on unexpected errors.
formatted_string(&self) -> bool495     pub fn formatted_string(&self) -> bool {
496         matches!(
497             self,
498             InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
499                 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Validation { .. })
500                 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
501         )
502     }
503 }
504