Skip to content

Commit

Permalink
panic when an interpreter error gets unintentionally discarded
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Sep 29, 2024
1 parent 1d9162b commit ff32edc
Show file tree
Hide file tree
Showing 12 changed files with 262 additions and 147 deletions.
19 changes: 13 additions & 6 deletions compiler/rustc_const_eval/src/interpret/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ use rustc_target::abi::{Abi, Align, HasDataLayout, Size};
use tracing::{instrument, trace};

use super::{
AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, InterpResult,
Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, Projectable, Provenance,
Scalar, alloc_range, mir_assign_valid_types,
AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, DiscardInterpError, ImmTy, Immediate,
InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer,
Projectable, Provenance, Scalar, alloc_range, mir_assign_valid_types,
};

#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
Expand Down Expand Up @@ -490,9 +490,16 @@ where
// If an access is both OOB and misaligned, we want to see the bounds error.
// However we have to call `check_misalign` first to make the borrow checker happy.
let misalign_err = self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn);
let a = self.get_ptr_alloc_mut(mplace.ptr(), size)?;
misalign_err?;
Ok(a)
match self.get_ptr_alloc_mut(mplace.ptr(), size) {
Ok(a) => {
misalign_err?;
Ok(a)
}
Err(e) => {
misalign_err.discard_interp_err();
Err(e)
}
}
}

/// Turn a local in the current frame into a place.
Expand Down
38 changes: 21 additions & 17 deletions compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ use rustc_hir as hir;
use rustc_middle::bug;
use rustc_middle::mir::interpret::ValidationErrorKind::{self, *};
use rustc_middle::mir::interpret::{
ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance,
UnsupportedOpInfo, ValidationErrorInfo, alloc_range,
ExpectedKind, InterpError, InterpErrorInfo, InvalidMetaKind, Misalignment, PointerKind,
Provenance, UnsupportedOpInfo, ValidationErrorInfo, alloc_range,
};
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, Ty};
Expand Down Expand Up @@ -95,16 +95,19 @@ macro_rules! try_validation {
Ok(x) => x,
// We catch the error and turn it into a validation failure. We are okay with
// allocation here as this can only slow down builds that fail anyway.
Err(e) => match e.kind() {
$(
$($p)|+ =>
throw_validation_failure!(
$where,
$kind
)
),+,
#[allow(unreachable_patterns)]
_ => Err::<!, _>(e)?,
Err(e) => {
let (kind, backtrace) = e.into_parts();
match kind {
$(
$($p)|+ => {
throw_validation_failure!(
$where,
$kind
)
}
),+,
_ => Err::<!, _>(InterpErrorInfo::from_parts(kind, backtrace))?,
}
}
}
}};
Expand Down Expand Up @@ -510,7 +513,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance {
ptr_kind,
// FIXME this says "null pointer" when null but we need translate
pointer: format!("{}", Pointer::<Option<AllocId>>::from_addr_invalid(*i))
pointer: format!("{}", Pointer::<Option<AllocId>>::from_addr_invalid(i))
},
Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds {
ptr_kind
Expand Down Expand Up @@ -1231,7 +1234,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
Err(err) => {
// For some errors we might be able to provide extra information.
// (This custom logic does not fit the `try_validation!` macro.)
match err.kind() {
let (kind, backtrace) = err.into_parts();
match kind {
Ub(InvalidUninitBytes(Some((_alloc_id, access)))) | Unsup(ReadPointerAsInt(Some((_alloc_id, access)))) => {
// Some byte was uninitialized, determine which
// element that byte belongs to so we can
Expand All @@ -1242,15 +1246,15 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
.unwrap();
self.path.push(PathElem::ArrayElem(i));

if matches!(err.kind(), Ub(InvalidUninitBytes(_))) {
if matches!(kind, Ub(InvalidUninitBytes(_))) {
throw_validation_failure!(self.path, Uninit { expected })
} else {
throw_validation_failure!(self.path, PointerAsInt { expected })
}
}

// Propagate upwards (that will also check for unexpected errors).
_ => return Err(err),
_ => return Err(InterpErrorInfo::from_parts(kind, backtrace)),
}
}
}
Expand Down Expand Up @@ -1282,7 +1286,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
// It's not great to catch errors here, since we can't give a very good path,
// but it's better than ICEing.
Ub(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }) => {
InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type: *expected_dyn_type }
InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type }
},
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use rustc_middle::bug;
use rustc_middle::mir::interpret::DiscardInterpError;
use rustc_middle::ty::layout::{
HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement,
};
Expand Down Expand Up @@ -75,7 +76,8 @@ fn check_validity_requirement_strict<'tcx>(
/*recursive*/ false,
/*reset_provenance_and_padding*/ false,
)
.is_ok())
.discard_interp_err()
.is_some())
}

/// Implements the 'lax' (default) version of the [`check_validity_requirement`] checks; see that
Expand Down
61 changes: 58 additions & 3 deletions compiler/rustc_middle/src/mir/interpret/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::any::Any;
use std::backtrace::Backtrace;
use std::borrow::Cow;
use std::fmt;
use std::{fmt, mem};

use either::Either;
use rustc_ast_ir::Mutability;
Expand Down Expand Up @@ -104,13 +104,57 @@ rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8);
/// These should always be constructed by calling `.into()` on
/// an `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*`
/// macros for this.
///
/// Interpreter errors must *not* be silently discarded (that will lead to a panic). Instead,
/// explicitly call `discard_interp_err` if this is really the right thing to do. Note that if
/// this happens during const-eval or in Miri, it could lead to a UB error being lost!
#[derive(Debug)]
pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>);

/// Calling `.ok()` on an `InterpResult` leads to a panic because of the guard.
/// To still let people opt-in to discarding interpreter errors, we have this extension trait.
pub trait DiscardInterpError {
type Output;

fn discard_interp_err(self) -> Option<Self::Output>;
}

impl<'tcx, T> DiscardInterpError for InterpResult<'tcx, T> {
type Output = T;

fn discard_interp_err(self) -> Option<Self::Output> {
match self {
Ok(v) => Some(v),
Err(e) => {
// Disarm the guard.
mem::forget(e.0.guard);
None
}
}
}
}

#[derive(Debug)]
struct Guard;

impl Drop for Guard {
fn drop(&mut self) {
// We silence the guard if we are already panicking, to avoid double-panics.
if !std::thread::panicking() {
panic!(
"an interpreter error got improperly discarded; use `discard_interp_err()` instead of `ok()` if this is intentional"
);
}
}
}

#[derive(Debug)]
struct InterpErrorInfoInner<'tcx> {
kind: InterpError<'tcx>,
backtrace: InterpErrorBacktrace,
/// This makes us panic on drop. This is used to catch
/// accidentally discarding an interpreter error.
guard: Guard,
}

#[derive(Debug)]
Expand Down Expand Up @@ -151,15 +195,25 @@ impl InterpErrorBacktrace {

impl<'tcx> InterpErrorInfo<'tcx> {
pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) {
let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self;
let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace, guard }) = self;
mem::forget(guard); // The error got explicitly discarded right here.
(kind, backtrace)
}

pub fn into_kind(self) -> InterpError<'tcx> {
let InterpErrorInfo(box InterpErrorInfoInner { kind, .. }) = self;
let InterpErrorInfo(box InterpErrorInfoInner { kind, guard, .. }) = self;
mem::forget(guard); // The error got explicitly discarded right here.
kind
}

pub fn discard_interp_err(self) {
mem::forget(self.0.guard); // The error got explicitly discarded right here.
}

pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self {
Self(Box::new(InterpErrorInfoInner { kind, backtrace, guard: Guard }))
}

#[inline]
pub fn kind(&self) -> &InterpError<'tcx> {
&self.0.kind
Expand Down Expand Up @@ -191,6 +245,7 @@ impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
InterpErrorInfo(Box::new(InterpErrorInfoInner {
kind,
backtrace: InterpErrorBacktrace::new(),
guard: Guard,
}))
}
}
Expand Down
12 changes: 6 additions & 6 deletions compiler/rustc_middle/src/mir/interpret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ pub use self::allocation::{
InitChunkIter, alloc_range,
};
pub use self::error::{
BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult,
EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind,
InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, InvalidProgramInfo,
MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo,
ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
ValidationErrorKind,
BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, DiscardInterpError, ErrorHandled,
EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult,
EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind,
InvalidProgramInfo, MachineStopType, Misalignment, PointerKind, ReportedErrorInfo,
ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo,
ValidationErrorInfo, ValidationErrorKind,
};
pub use self::pointer::{CtfeProvenance, Pointer, PointerArithmetic, Provenance};
pub use self::value::Scalar;
Expand Down
Loading

0 comments on commit ff32edc

Please sign in to comment.