Skip to content

Commit 120e9f9

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

File tree

11 files changed

+257
-142
lines changed

11 files changed

+257
-142
lines changed

compiler/rustc_const_eval/src/interpret/place.rs

Lines changed: 13 additions & 6 deletions
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

Lines changed: 21 additions & 17 deletions
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

Lines changed: 54 additions & 3 deletions
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;
@@ -107,10 +107,50 @@ rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8);
107107
#[derive(Debug)]
108108
pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>);
109109

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

116156
#[derive(Debug)]
@@ -151,15 +191,25 @@ impl InterpErrorBacktrace {
151191

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

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

205+
pub fn discard_interp_error(self) {
206+
mem::forget(self.0.guard); // The error got explicitly discarded right here.
207+
}
208+
209+
pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self {
210+
Self(Box::new(InterpErrorInfoInner { kind, backtrace, guard: Guard }))
211+
}
212+
163213
#[inline]
164214
pub fn kind(&self) -> &InterpError<'tcx> {
165215
&self.0.kind
@@ -191,6 +241,7 @@ impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
191241
InterpErrorInfo(Box::new(InterpErrorInfoInner {
192242
kind,
193243
backtrace: InterpErrorBacktrace::new(),
244+
guard: Guard,
194245
}))
195246
}
196247
}

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

Lines changed: 6 additions & 6 deletions
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

Lines changed: 35 additions & 21 deletions
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)