Skip to content

Commit 3c4f73d

Browse files
committed
Create a special ValidationMachine that just outright forbids almost every machine hook
1 parent e10064b commit 3c4f73d

File tree

7 files changed

+183
-49
lines changed

7 files changed

+183
-49
lines changed

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::interpret::{
2121
InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking,
2222
StackPopCleanup,
2323
};
24-
use crate::interpret::{eval_nullary_intrinsic, Machine};
24+
use crate::interpret::{eval_nullary_intrinsic, Machine, ValidationMachine};
2525

2626
// Returns a pointer to where the result lives
2727
#[instrument(level = "trace", skip(ecx, body), ret)]
@@ -307,7 +307,9 @@ impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> {
307307
ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
308308
cid: GlobalId<'tcx>,
309309
) -> Result<Self, ErrorHandled> {
310-
const_validate_mplace(&ecx, mplace, cid)?;
310+
let validate_ecx =
311+
InterpCx::new(ecx.tcx.tcx, ecx.tcx.span, ecx.param_env, ValidationMachine(&ecx));
312+
const_validate_mplace(&validate_ecx, mplace, cid)?;
311313
Ok(ConstAlloc {
312314
alloc_id: mplace.ptr().provenance.unwrap().alloc_id(),
313315
ty: mplace.layout.ty,
@@ -406,7 +408,7 @@ fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>(
406408

407409
#[inline(always)]
408410
pub fn const_validate_mplace<'mir, 'tcx>(
409-
ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
411+
ecx: &InterpCx<'mir, 'tcx, ValidationMachine<'_, 'mir, 'tcx>>,
410412
mplace: &MPlaceTy<'tcx>,
411413
cid: GlobalId<'tcx>,
412414
) -> Result<(), ErrorHandled> {

compiler/rustc_const_eval/src/interpret/machine.rs

-4
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,6 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
391391

392392
/// Hook for performing extra checks on a memory read access.
393393
///
394-
/// This will *not* be called during validation!
395-
///
396394
/// Takes read-only access to the allocation so we can keep all the memory read
397395
/// operations take `&self`. Use a `RefCell` in `AllocExtra` if you
398396
/// need to mutate.
@@ -412,8 +410,6 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
412410
/// Hook for performing extra checks on any memory read access,
413411
/// that involves an allocation, even ZST reads.
414412
///
415-
/// This will *not* be called during validation!
416-
///
417413
/// Used to prevent statics from self-initializing by reading from their own memory
418414
/// as it is being initialized.
419415
fn before_alloc_read(

compiler/rustc_const_eval/src/interpret/memory.rs

+6-38
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
99
use std::assert_matches::assert_matches;
1010
use std::borrow::Cow;
11-
use std::cell::Cell;
1211
use std::collections::VecDeque;
1312
use std::fmt;
1413
use std::ptr;
@@ -112,11 +111,6 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
112111
/// that do not exist any more.
113112
// FIXME: this should not be public, but interning currently needs access to it
114113
pub(super) dead_alloc_map: FxIndexMap<AllocId, (Size, Align)>,
115-
116-
/// This stores whether we are currently doing reads purely for the purpose of validation.
117-
/// Those reads do not trigger the machine's hooks for memory reads.
118-
/// Needless to say, this must only be set with great care!
119-
validation_in_progress: Cell<bool>,
120114
}
121115

122116
/// A reference to some allocation that was already bounds-checked for the given region
@@ -143,7 +137,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
143137
alloc_map: M::MemoryMap::default(),
144138
extra_fn_ptr_map: FxIndexMap::default(),
145139
dead_alloc_map: FxIndexMap::default(),
146-
validation_in_progress: Cell::new(false),
147140
}
148141
}
149142

@@ -631,28 +624,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
631624
size,
632625
CheckInAllocMsg::MemoryAccessTest,
633626
|alloc_id, offset, prov| {
634-
if !self.memory.validation_in_progress.get() {
635-
// We want to call the hook on *all* accesses that involve an AllocId,
636-
// including zero-sized accesses. That means we have to do it here
637-
// rather than below in the `Some` branch.
638-
M::before_alloc_read(self, alloc_id)?;
639-
}
627+
// We want to call the hook on *all* accesses that involve an AllocId,
628+
// including zero-sized accesses. That means we have to do it here
629+
// rather than below in the `Some` branch.
630+
M::before_alloc_read(self, alloc_id)?;
640631
let alloc = self.get_alloc_raw(alloc_id)?;
641632
Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc)))
642633
},
643634
)?;
644635

645636
if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc {
646637
let range = alloc_range(offset, size);
647-
if !self.memory.validation_in_progress.get() {
648-
M::before_memory_read(
649-
self.tcx,
650-
&self.machine,
651-
&alloc.extra,
652-
(alloc_id, prov),
653-
range,
654-
)?;
655-
}
638+
M::before_memory_read(self.tcx, &self.machine, &alloc.extra, (alloc_id, prov), range)?;
639+
656640
Ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id }))
657641
} else {
658642
Ok(None)
@@ -926,21 +910,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
926910
}
927911
})
928912
}
929-
930-
/// Runs the close in "validation" mode, which means the machine's memory read hooks will be
931-
/// suppressed. Needless to say, this must only be set with great care! Cannot be nested.
932-
pub(super) fn run_for_validation<R>(&self, f: impl FnOnce() -> R) -> R {
933-
assert!(
934-
self.memory.validation_in_progress.replace(true) == false,
935-
"`validation_in_progress` was already set"
936-
);
937-
let res = f();
938-
assert!(
939-
self.memory.validation_in_progress.replace(false) == true,
940-
"`validation_in_progress` was unset by someone else"
941-
);
942-
res
943-
}
944913
}
945914

946915
#[doc(hidden)]
@@ -1186,7 +1155,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
11861155
};
11871156
let src_alloc = self.get_alloc_raw(src_alloc_id)?;
11881157
let src_range = alloc_range(src_offset, size);
1189-
assert!(!self.memory.validation_in_progress.get(), "we can't be copying during validation");
11901158
M::before_memory_read(
11911159
tcx,
11921160
&self.machine,

compiler/rustc_const_eval/src/interpret/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub use self::operand::{ImmTy, Immediate, OpTy, Readable};
3030
pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable};
3131
pub use self::projection::{OffsetMode, Projectable};
3232
pub use self::terminator::FnArg;
33-
pub use self::validity::{CtfeValidationMode, RefTracking};
33+
pub use self::validity::{CtfeValidationMode, RefTracking, ValidationMachine};
3434
pub use self::visitor::ValueVisitor;
3535

3636
use self::{

compiler/rustc_const_eval/src/interpret/util.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_middle::ty::{
99
use rustc_span::def_id::DefId;
1010
use std::ops::ControlFlow;
1111

12-
use super::{InterpCx, MPlaceTy};
12+
use super::{InterpCx, MPlaceTy, ValidationMachine};
1313

1414
/// Checks whether a type contains generic parameters which must be instantiated.
1515
///
@@ -87,6 +87,10 @@ impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx>
8787
mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
8888
cid: GlobalId<'tcx>,
8989
) -> Result<Self, ErrorHandled> {
90+
let memory = std::mem::take(&mut ecx.memory.alloc_map);
91+
let mut ecx =
92+
InterpCx::new(ecx.tcx.tcx, ecx.tcx.span, ecx.param_env, ValidationMachine(&ecx));
93+
ecx.memory.alloc_map = memory;
9094
crate::const_eval::const_validate_mplace(&ecx, mplace, cid)?;
9195
let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
9296
let alloc = ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1;

compiler/rustc_const_eval/src/interpret/validity.rs

+157-2
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,22 @@ use std::num::NonZero;
1010
use either::{Left, Right};
1111

1212
use hir::def::DefKind;
13+
use hir::def_id::DefId;
1314
use rustc_ast::Mutability;
1415
use rustc_data_structures::fx::FxHashSet;
1516
use rustc_hir as hir;
1617
use rustc_middle::mir::interpret::{
1718
ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance,
1819
ValidationErrorInfo, ValidationErrorKind, ValidationErrorKind::*,
1920
};
20-
use rustc_middle::ty;
21+
use rustc_middle::query::TyCtxtAt;
2122
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
23+
use rustc_middle::{mir, ty};
2224
use rustc_span::symbol::{sym, Symbol};
2325
use rustc_target::abi::{
2426
Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
2527
};
28+
use rustc_target::spec::abi::Abi as CallAbi;
2629

2730
use std::hash::Hash;
2831

@@ -37,6 +40,7 @@ use super::InterpError::UndefinedBehavior as Ub;
3740
use super::InterpError::Unsupported as Unsup;
3841
use super::UndefinedBehaviorInfo::*;
3942
use super::UnsupportedOpInfo::*;
43+
use super::{FnArg, Frame};
4044

4145
macro_rules! throw_validation_failure {
4246
($where:expr, $kind: expr) => {{
@@ -967,7 +971,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
967971
let mut visitor = ValidityVisitor { path, ref_tracking, ctfe_mode, ecx: self };
968972

969973
// Run it.
970-
match self.run_for_validation(|| visitor.visit_value(op)) {
974+
match visitor.visit_value(op) {
971975
Ok(()) => Ok(()),
972976
// Pass through validation failures and "invalid program" issues.
973977
Err(err)
@@ -1022,3 +1026,154 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
10221026
self.validate_operand_internal(op, vec![], None, None)
10231027
}
10241028
}
1029+
1030+
pub struct ValidationMachine<'a, 'mir, 'tcx>(
1031+
pub &'a InterpCx<'mir, 'tcx, crate::const_eval::CompileTimeInterpreter<'mir, 'tcx>>,
1032+
);
1033+
1034+
impl<'a, 'mir, 'tcx> std::ops::Deref for ValidationMachine<'a, 'mir, 'tcx> {
1035+
type Target = crate::const_eval::CompileTimeInterpreter<'mir, 'tcx>;
1036+
1037+
fn deref(&self) -> &Self::Target {
1038+
&self.0.machine
1039+
}
1040+
}
1041+
1042+
impl<'a, 'mir, 'tcx> Machine<'mir, 'tcx> for ValidationMachine<'a, 'mir, 'tcx> {
1043+
super::compile_time_machine!(<'mir, 'tcx>);
1044+
1045+
type MemoryKind = crate::const_eval::MemoryKind;
1046+
1047+
const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error
1048+
1049+
#[inline(always)]
1050+
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
1051+
Machine::enforce_alignment(ecx.machine.0)
1052+
}
1053+
1054+
#[inline(always)]
1055+
fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool {
1056+
ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks || layout.abi.is_uninhabited()
1057+
}
1058+
1059+
fn load_mir(
1060+
_ecx: &InterpCx<'mir, 'tcx, Self>,
1061+
instance: ty::InstanceDef<'tcx>,
1062+
) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> {
1063+
unreachable!("validation tried to load mir of {instance:?}")
1064+
}
1065+
1066+
fn find_mir_or_eval_fn(
1067+
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
1068+
orig_instance: ty::Instance<'tcx>,
1069+
_abi: CallAbi,
1070+
_args: &[FnArg<'tcx>],
1071+
_dest: &MPlaceTy<'tcx>,
1072+
_ret: Option<mir::BasicBlock>,
1073+
_unwind: mir::UnwindAction, // unwinding is not supported in consts
1074+
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
1075+
unreachable!("validation tried to evaluate mir of {orig_instance:?}")
1076+
}
1077+
1078+
fn panic_nounwind(_ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: &str) -> InterpResult<'tcx> {
1079+
unreachable!("validation tried to panic: {msg}")
1080+
}
1081+
1082+
fn call_intrinsic(
1083+
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
1084+
instance: ty::Instance<'tcx>,
1085+
_args: &[OpTy<'tcx>],
1086+
_dest: &MPlaceTy<'tcx, Self::Provenance>,
1087+
_target: Option<mir::BasicBlock>,
1088+
_unwind: mir::UnwindAction,
1089+
) -> InterpResult<'tcx> {
1090+
unreachable!("validation tried to evaluate intrinsic {instance:?}")
1091+
}
1092+
1093+
fn assert_panic(
1094+
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
1095+
msg: &mir::AssertMessage<'tcx>,
1096+
_unwind: mir::UnwindAction,
1097+
) -> InterpResult<'tcx> {
1098+
unreachable!("validation tried to panic: {msg:?}")
1099+
}
1100+
1101+
fn binary_ptr_op(
1102+
_ecx: &InterpCx<'mir, 'tcx, Self>,
1103+
_bin_op: mir::BinOp,
1104+
_left: &ImmTy<'tcx>,
1105+
_right: &ImmTy<'tcx>,
1106+
) -> InterpResult<'tcx, (ImmTy<'tcx>, bool)> {
1107+
unreachable!("validation does not do binary pointer ops");
1108+
}
1109+
1110+
fn increment_const_eval_counter(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
1111+
unreachable!("validation does not interpret statements")
1112+
}
1113+
1114+
#[inline(always)]
1115+
fn expose_ptr(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx> {
1116+
unreachable!("validation does not exposing pointers")
1117+
}
1118+
1119+
#[inline(always)]
1120+
fn init_frame_extra(
1121+
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
1122+
_frame: Frame<'mir, 'tcx>,
1123+
) -> InterpResult<'tcx, Frame<'mir, 'tcx>> {
1124+
unreachable!("validation does not create stack frames")
1125+
}
1126+
1127+
#[inline(always)]
1128+
fn stack<'b>(
1129+
ecx: &'b InterpCx<'mir, 'tcx, Self>,
1130+
) -> &'b [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] {
1131+
ecx.machine.0.stack()
1132+
}
1133+
1134+
#[inline(always)]
1135+
fn stack_mut<'b>(
1136+
_ecx: &'b mut InterpCx<'mir, 'tcx, Self>,
1137+
) -> &'b mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> {
1138+
unreachable!("validation cannot mutate stack")
1139+
}
1140+
1141+
fn before_access_global(
1142+
_tcx: TyCtxtAt<'tcx>,
1143+
_machine: &Self,
1144+
_alloc_id: AllocId,
1145+
_alloc: mir::interpret::ConstAllocation<'tcx>,
1146+
_static_def_id: Option<DefId>,
1147+
is_write: bool,
1148+
) -> InterpResult<'tcx> {
1149+
assert!(!is_write);
1150+
// Do nothing, validation may read globals.
1151+
Ok(())
1152+
}
1153+
1154+
fn retag_ptr_value(
1155+
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
1156+
kind: mir::RetagKind,
1157+
val: &ImmTy<'tcx, mir::interpret::CtfeProvenance>,
1158+
) -> InterpResult<'tcx, ImmTy<'tcx, mir::interpret::CtfeProvenance>> {
1159+
unreachable!("validation does not retag pointers: {val:?}, {kind:?}")
1160+
}
1161+
1162+
fn before_memory_write(
1163+
_tcx: TyCtxtAt<'tcx>,
1164+
_machine: &mut Self,
1165+
_alloc_extra: &mut Self::AllocExtra,
1166+
(_alloc_id, _immutable): (AllocId, bool),
1167+
_range: mir::interpret::AllocRange,
1168+
) -> InterpResult<'tcx> {
1169+
unreachable!("validation does not write to memory")
1170+
}
1171+
1172+
fn before_alloc_read(
1173+
_ecx: &InterpCx<'mir, 'tcx, Self>,
1174+
_alloc_id: AllocId,
1175+
) -> InterpResult<'tcx> {
1176+
// Do nothing, validation may read all memory
1177+
Ok(())
1178+
}
1179+
}

src/tools/miri/tests/pass/alloc-access-tracking.stderr

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ LL | *ptr = 42; // Crucially, only a write is printed here, no read!
1616
= note: BACKTRACE:
1717
= note: inside `start` at $DIR/alloc-access-tracking.rs:LL:CC
1818

19+
note: tracking was triggered
20+
--> $DIR/alloc-access-tracking.rs:LL:CC
21+
|
22+
LL | *ptr = 42; // Crucially, only a write is printed here, no read!
23+
| ^^^^^^^^^ read access to allocation with id 17
24+
|
25+
= note: BACKTRACE:
26+
= note: inside `start` at $DIR/alloc-access-tracking.rs:LL:CC
27+
1928
note: tracking was triggered
2029
--> $DIR/alloc-access-tracking.rs:LL:CC
2130
|

0 commit comments

Comments
 (0)