Skip to content

Commit b742eda

Browse files
committed
panic when an interpreter error gets unintentionally discarded
1 parent f2becdf commit b742eda

File tree

11 files changed

+261
-142
lines changed

11 files changed

+261
-142
lines changed

compiler/rustc_const_eval/src/interpret/place.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ use rustc_target::abi::{Abi, Align, HasDataLayout, Size};
1313
use tracing::{instrument, trace};
1414

1515
use super::{
16-
AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, InterpResult,
17-
Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, Projectable, Provenance,
18-
Scalar, alloc_range, mir_assign_valid_types,
16+
AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, DiscardInterpError, ImmTy, Immediate,
17+
InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer,
18+
Projectable, Provenance, Scalar, alloc_range, mir_assign_valid_types,
1919
};
2020

2121
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
@@ -490,9 +490,16 @@ where
490490
// If an access is both OOB and misaligned, we want to see the bounds error.
491491
// However we have to call `check_misalign` first to make the borrow checker happy.
492492
let misalign_err = self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn);
493-
let a = self.get_ptr_alloc_mut(mplace.ptr(), size)?;
494-
misalign_err?;
495-
Ok(a)
493+
match self.get_ptr_alloc_mut(mplace.ptr(), size) {
494+
Ok(a) => {
495+
misalign_err?;
496+
Ok(a)
497+
}
498+
Err(e) => {
499+
misalign_err.discard_interp_error();
500+
Err(e)
501+
}
502+
}
496503
}
497504

498505
/// Turn a local in the current frame into a place.

compiler/rustc_const_eval/src/interpret/validity.rs

+21-17
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use rustc_hir as hir;
1717
use rustc_middle::bug;
1818
use rustc_middle::mir::interpret::ValidationErrorKind::{self, *};
1919
use rustc_middle::mir::interpret::{
20-
ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance,
21-
UnsupportedOpInfo, ValidationErrorInfo, alloc_range,
20+
ExpectedKind, InterpError, InterpErrorInfo, InvalidMetaKind, Misalignment, PointerKind,
21+
Provenance, UnsupportedOpInfo, ValidationErrorInfo, alloc_range,
2222
};
2323
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
2424
use rustc_middle::ty::{self, Ty};
@@ -95,16 +95,19 @@ macro_rules! try_validation {
9595
Ok(x) => x,
9696
// We catch the error and turn it into a validation failure. We are okay with
9797
// allocation here as this can only slow down builds that fail anyway.
98-
Err(e) => match e.kind() {
99-
$(
100-
$($p)|+ =>
101-
throw_validation_failure!(
102-
$where,
103-
$kind
104-
)
105-
),+,
106-
#[allow(unreachable_patterns)]
107-
_ => Err::<!, _>(e)?,
98+
Err(e) => {
99+
let (kind, backtrace) = e.into_parts();
100+
match kind {
101+
$(
102+
$($p)|+ => {
103+
throw_validation_failure!(
104+
$where,
105+
$kind
106+
)
107+
}
108+
),+,
109+
_ => Err::<!, _>(InterpErrorInfo::from_parts(kind, backtrace))?,
110+
}
108111
}
109112
}
110113
}};
@@ -510,7 +513,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
510513
Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance {
511514
ptr_kind,
512515
// FIXME this says "null pointer" when null but we need translate
513-
pointer: format!("{}", Pointer::<Option<AllocId>>::from_addr_invalid(*i))
516+
pointer: format!("{}", Pointer::<Option<AllocId>>::from_addr_invalid(i))
514517
},
515518
Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds {
516519
ptr_kind
@@ -1231,7 +1234,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
12311234
Err(err) => {
12321235
// For some errors we might be able to provide extra information.
12331236
// (This custom logic does not fit the `try_validation!` macro.)
1234-
match err.kind() {
1237+
let (kind, backtrace) = err.into_parts();
1238+
match kind {
12351239
Ub(InvalidUninitBytes(Some((_alloc_id, access)))) | Unsup(ReadPointerAsInt(Some((_alloc_id, access)))) => {
12361240
// Some byte was uninitialized, determine which
12371241
// element that byte belongs to so we can
@@ -1242,15 +1246,15 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
12421246
.unwrap();
12431247
self.path.push(PathElem::ArrayElem(i));
12441248

1245-
if matches!(err.kind(), Ub(InvalidUninitBytes(_))) {
1249+
if matches!(kind, Ub(InvalidUninitBytes(_))) {
12461250
throw_validation_failure!(self.path, Uninit { expected })
12471251
} else {
12481252
throw_validation_failure!(self.path, PointerAsInt { expected })
12491253
}
12501254
}
12511255

12521256
// Propagate upwards (that will also check for unexpected errors).
1253-
_ => return Err(err),
1257+
_ => return Err(InterpErrorInfo::from_parts(kind, backtrace)),
12541258
}
12551259
}
12561260
}
@@ -1282,7 +1286,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
12821286
// It's not great to catch errors here, since we can't give a very good path,
12831287
// but it's better than ICEing.
12841288
Ub(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }) => {
1285-
InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type: *expected_dyn_type }
1289+
InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type }
12861290
},
12871291
);
12881292
}

compiler/rustc_middle/src/mir/interpret/error.rs

+58-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::any::Any;
22
use std::backtrace::Backtrace;
33
use std::borrow::Cow;
4-
use std::fmt;
4+
use std::{fmt, mem};
55

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

114+
/// Calling `.ok()` on an `InterpResult` leads to a panic because of the guard.
115+
/// To still let people opt-in to discarding interpreter errors, we have this extension trait.
116+
pub trait DiscardInterpError {
117+
type Output;
118+
119+
fn discard_interp_error(self) -> Option<Self::Output>;
120+
}
121+
122+
impl<'tcx, T> DiscardInterpError for InterpResult<'tcx, T> {
123+
type Output = T;
124+
125+
fn discard_interp_error(self) -> Option<Self::Output> {
126+
match self {
127+
Ok(v) => Some(v),
128+
Err(e) => {
129+
// Disarm the guard.
130+
mem::forget(e.0.guard);
131+
None
132+
}
133+
}
134+
}
135+
}
136+
137+
#[derive(Debug)]
138+
struct Guard;
139+
140+
impl Drop for Guard {
141+
fn drop(&mut self) {
142+
// We silence the guard if we are already panicking, to avoid double-panics.
143+
if !std::thread::panicking() {
144+
panic!(
145+
"an interpreter error got improperly discarded; use `discard_interp_error()` instead of `ok()` if this is intentional"
146+
);
147+
}
148+
}
149+
}
150+
110151
#[derive(Debug)]
111152
struct InterpErrorInfoInner<'tcx> {
112153
kind: InterpError<'tcx>,
113154
backtrace: InterpErrorBacktrace,
155+
/// This makes us panic on drop. This is used to catch
156+
/// accidentally discarding an interpreter error.
157+
guard: Guard,
114158
}
115159

116160
#[derive(Debug)]
@@ -151,15 +195,25 @@ impl InterpErrorBacktrace {
151195

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

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

209+
pub fn discard_interp_error(self) {
210+
mem::forget(self.0.guard); // The error got explicitly discarded right here.
211+
}
212+
213+
pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self {
214+
Self(Box::new(InterpErrorInfoInner { kind, backtrace, guard: Guard }))
215+
}
216+
163217
#[inline]
164218
pub fn kind(&self) -> &InterpError<'tcx> {
165219
&self.0.kind
@@ -191,6 +245,7 @@ impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
191245
InterpErrorInfo(Box::new(InterpErrorInfoInner {
192246
kind,
193247
backtrace: InterpErrorBacktrace::new(),
248+
guard: Guard,
194249
}))
195250
}
196251
}

compiler/rustc_middle/src/mir/interpret/mod.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ pub use self::allocation::{
3434
InitChunkIter, alloc_range,
3535
};
3636
pub use self::error::{
37-
BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult,
38-
EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind,
39-
InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, InvalidProgramInfo,
40-
MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo,
41-
ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
42-
ValidationErrorKind,
37+
BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, DiscardInterpError, ErrorHandled,
38+
EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult,
39+
EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind,
40+
InvalidProgramInfo, MachineStopType, Misalignment, PointerKind, ReportedErrorInfo,
41+
ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo,
42+
ValidationErrorInfo, ValidationErrorKind,
4343
};
4444
pub use self::pointer::{CtfeProvenance, Pointer, PointerArithmetic, Provenance};
4545
pub use self::value::Scalar;

compiler/rustc_mir_transform/src/dataflow_const_prop.rs

+35-21
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
//! Currently, this pass only propagates scalar values.
44
55
use rustc_const_eval::const_eval::{DummyMachine, throw_machine_stop_str};
6-
use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable};
6+
use rustc_const_eval::interpret::{
7+
DiscardInterpError, ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable,
8+
};
79
use rustc_data_structures::fx::FxHashMap;
810
use rustc_hir::def::DefKind;
911
use rustc_middle::bug;
@@ -364,8 +366,10 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
364366
}
365367
}
366368
Operand::Constant(box constant) => {
367-
if let Ok(constant) =
368-
self.ecx.eval_mir_constant(&constant.const_, constant.span, None)
369+
if let Some(constant) = self
370+
.ecx
371+
.eval_mir_constant(&constant.const_, constant.span, None)
372+
.discard_interp_error()
369373
{
370374
self.assign_constant(state, place, constant, &[]);
371375
}
@@ -387,15 +391,17 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
387391
for &(mut proj_elem) in projection {
388392
if let PlaceElem::Index(index) = proj_elem {
389393
if let FlatSet::Elem(index) = state.get(index.into(), &self.map)
390-
&& let Ok(offset) = index.to_target_usize(&self.tcx)
394+
&& let Some(offset) = index.to_target_usize(&self.tcx).discard_interp_error()
391395
&& let Some(min_length) = offset.checked_add(1)
392396
{
393397
proj_elem = PlaceElem::ConstantIndex { offset, min_length, from_end: false };
394398
} else {
395399
return;
396400
}
397401
}
398-
operand = if let Ok(operand) = self.ecx.project(&operand, proj_elem) {
402+
operand = if let Some(operand) =
403+
self.ecx.project(&operand, proj_elem).discard_interp_error()
404+
{
399405
operand
400406
} else {
401407
return;
@@ -406,24 +412,30 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
406412
place,
407413
operand,
408414
&mut |elem, op| match elem {
409-
TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).ok(),
410-
TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).ok(),
415+
TrackElem::Field(idx) => {
416+
self.ecx.project_field(op, idx.as_usize()).discard_interp_error()
417+
}
418+
TrackElem::Variant(idx) => {
419+
self.ecx.project_downcast(op, idx).discard_interp_error()
420+
}
411421
TrackElem::Discriminant => {
412-
let variant = self.ecx.read_discriminant(op).ok()?;
413-
let discr_value =
414-
self.ecx.discriminant_for_variant(op.layout.ty, variant).ok()?;
422+
let variant = self.ecx.read_discriminant(op).discard_interp_error()?;
423+
let discr_value = self
424+
.ecx
425+
.discriminant_for_variant(op.layout.ty, variant)
426+
.discard_interp_error()?;
415427
Some(discr_value.into())
416428
}
417429
TrackElem::DerefLen => {
418-
let op: OpTy<'_> = self.ecx.deref_pointer(op).ok()?.into();
419-
let len_usize = op.len(&self.ecx).ok()?;
430+
let op: OpTy<'_> = self.ecx.deref_pointer(op).discard_interp_error()?.into();
431+
let len_usize = op.len(&self.ecx).discard_interp_error()?;
420432
let layout =
421433
self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).unwrap();
422434
Some(ImmTy::from_uint(len_usize, layout).into())
423435
}
424436
},
425437
&mut |place, op| {
426-
if let Ok(imm) = self.ecx.read_immediate_raw(op)
438+
if let Some(imm) = self.ecx.read_immediate_raw(op).discard_interp_error()
427439
&& let Some(imm) = imm.right()
428440
{
429441
let elem = self.wrap_immediate(*imm);
@@ -447,11 +459,11 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
447459
(FlatSet::Bottom, _) | (_, FlatSet::Bottom) => (FlatSet::Bottom, FlatSet::Bottom),
448460
// Both sides are known, do the actual computation.
449461
(FlatSet::Elem(left), FlatSet::Elem(right)) => {
450-
match self.ecx.binary_op(op, &left, &right) {
462+
match self.ecx.binary_op(op, &left, &right).discard_interp_error() {
451463
// Ideally this would return an Immediate, since it's sometimes
452464
// a pair and sometimes not. But as a hack we always return a pair
453465
// and just make the 2nd component `Bottom` when it does not exist.
454-
Ok(val) => {
466+
Some(val) => {
455467
if matches!(val.layout.abi, Abi::ScalarPair(..)) {
456468
let (val, overflow) = val.to_scalar_pair();
457469
(FlatSet::Elem(val), FlatSet::Elem(overflow))
@@ -470,7 +482,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
470482
}
471483

472484
let arg_scalar = const_arg.to_scalar();
473-
let Ok(arg_value) = arg_scalar.to_bits(layout.size) else {
485+
let Some(arg_value) = arg_scalar.to_bits(layout.size).discard_interp_error() else {
474486
return (FlatSet::Top, FlatSet::Top);
475487
};
476488

@@ -518,8 +530,10 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
518530
return None;
519531
}
520532
let enum_ty_layout = self.tcx.layout_of(self.param_env.and(enum_ty)).ok()?;
521-
let discr_value =
522-
self.ecx.discriminant_for_variant(enum_ty_layout.ty, variant_index).ok()?;
533+
let discr_value = self
534+
.ecx
535+
.discriminant_for_variant(enum_ty_layout.ty, variant_index)
536+
.discard_interp_error()?;
523537
Some(discr_value.to_scalar())
524538
}
525539

@@ -573,7 +587,7 @@ impl<'a, 'tcx> Collector<'a, 'tcx> {
573587
map: &Map<'tcx>,
574588
) -> Option<Const<'tcx>> {
575589
let ty = place.ty(self.local_decls, self.patch.tcx).ty;
576-
let layout = ecx.layout_of(ty).ok()?;
590+
let layout = ecx.layout_of(ty).discard_interp_error()?;
577591

578592
if layout.is_zst() {
579593
return Some(Const::zero_sized(ty));
@@ -595,7 +609,7 @@ impl<'a, 'tcx> Collector<'a, 'tcx> {
595609
.intern_with_temp_alloc(layout, |ecx, dest| {
596610
try_write_constant(ecx, dest, place, ty, state, map)
597611
})
598-
.ok()?;
612+
.discard_interp_error()?;
599613
return Some(Const::Val(ConstValue::Indirect { alloc_id, offset: Size::ZERO }, ty));
600614
}
601615

@@ -830,7 +844,7 @@ impl<'tcx> MutVisitor<'tcx> for Patch<'tcx> {
830844
if let PlaceElem::Index(local) = elem {
831845
let offset = self.before_effect.get(&(location, local.into()))?;
832846
let offset = offset.try_to_scalar()?;
833-
let offset = offset.to_target_usize(&self.tcx).ok()?;
847+
let offset = offset.to_target_usize(&self.tcx).discard_interp_error()?;
834848
let min_length = offset.checked_add(1)?;
835849
Some(PlaceElem::ConstantIndex { offset, min_length, from_end: false })
836850
} else {

0 commit comments

Comments
 (0)