88
99use std:: assert_matches:: assert_matches;
1010use std:: borrow:: Cow ;
11+ use std:: cell:: Cell ;
1112use std:: collections:: VecDeque ;
1213use std:: fmt;
1314use std:: ptr;
@@ -111,6 +112,11 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
111112 /// that do not exist any more.
112113 // FIXME: this should not be public, but interning currently needs access to it
113114 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 > ,
114120}
115121
116122/// A reference to some allocation that was already bounds-checked for the given region
@@ -137,6 +143,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
137143 alloc_map : M :: MemoryMap :: default ( ) ,
138144 extra_fn_ptr_map : FxIndexMap :: default ( ) ,
139145 dead_alloc_map : FxIndexMap :: default ( ) ,
146+ validation_in_progress : Cell :: new ( false ) ,
140147 }
141148 }
142149
@@ -624,18 +631,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
624631 size,
625632 CheckInAllocMsg :: MemoryAccessTest ,
626633 |alloc_id, offset, prov| {
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) ?;
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+ }
631640 let alloc = self . get_alloc_raw ( alloc_id) ?;
632641 Ok ( ( alloc. size ( ) , alloc. align , ( alloc_id, offset, prov, alloc) ) )
633642 } ,
634643 ) ?;
635644
636645 if let Some ( ( alloc_id, offset, prov, alloc) ) = ptr_and_alloc {
637646 let range = alloc_range ( offset, size) ;
638- M :: before_memory_read ( self . tcx , & self . machine , & alloc. extra , ( alloc_id, prov) , range) ?;
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+ }
639656 Ok ( Some ( AllocRef { alloc, range, tcx : * self . tcx , alloc_id } ) )
640657 } else {
641658 Ok ( None )
@@ -909,6 +926,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
909926 }
910927 } )
911928 }
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+ }
912944}
913945
914946#[ doc( hidden) ]
@@ -1154,6 +1186,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
11541186 } ;
11551187 let src_alloc = self . get_alloc_raw ( src_alloc_id) ?;
11561188 let src_range = alloc_range ( src_offset, size) ;
1189+ assert ! ( !self . memory. validation_in_progress. get( ) , "we can't be copying during validation" ) ;
11571190 M :: before_memory_read (
11581191 tcx,
11591192 & self . machine ,
0 commit comments