@@ -977,12 +977,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
977977 }
978978
979979 /// Handle the effect an FFI call might have on the state of allocations.
980- /// This overapproximates the modifications which external code might make to memory:
981- /// We set all reachable allocations as initialized, mark all reachable provenances as exposed
982- /// and overwrite them with `Provenance::WILDCARD`.
980+ /// If `paranoid` is true, overapproximates the modifications which external code might make
981+ /// to memory: We set all reachable allocations as initialized, mark all reachable provenances
982+ /// as exposed and overwrite them with `Provenance::WILDCARD`. Otherwise, it just makes sure
983+ /// that all allocations are properly set up so that we don't leak whatever was in the uninit
984+ /// bytes on FFI call.
983985 ///
984986 /// The allocations in `ids` are assumed to be already exposed.
985- pub fn prepare_for_native_call ( & mut self , ids : Vec < AllocId > ) -> InterpResult < ' tcx > {
987+ pub fn prepare_for_native_call (
988+ & mut self ,
989+ ids : Vec < AllocId > ,
990+ paranoid : bool ,
991+ ) -> InterpResult < ' tcx > {
986992 let mut done = FxHashSet :: default ( ) ;
987993 let mut todo = ids;
988994 while let Some ( id) = todo. pop ( ) {
@@ -997,25 +1003,119 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
9971003 continue ;
9981004 }
9991005
1000- // Expose all provenances in this allocation, and add them to `todo` .
1006+ // Make sure we iterate over everything recursively, preparing the extra alloc info .
10011007 let alloc = self . get_alloc_raw ( id) ?;
10021008 for prov in alloc. provenance ( ) . provenances ( ) {
1003- M :: expose_provenance ( self , prov) ?;
1009+ if paranoid {
1010+ // Expose all provenances in this allocation, and add them to `todo`.
1011+ M :: expose_provenance ( self , prov) ?;
1012+ }
10041013 if let Some ( id) = prov. get_alloc_id ( ) {
10051014 todo. push ( id) ;
10061015 }
10071016 }
1017+
10081018 // Also expose the provenance of the interpreter-level allocation, so it can
10091019 // be read by FFI. The `black_box` is defensive programming as LLVM likes
10101020 // to (incorrectly) optimize away ptr2int casts whose result is unused.
1011- std:: hint:: black_box ( alloc. get_bytes_unchecked_raw ( ) . expose_provenance ( ) ) ;
1012-
1013- // Prepare for possible write from native code if mutable.
1014- if info. mutbl . is_mut ( ) {
1015- self . get_alloc_raw_mut ( id) ?
1016- . 0
1017- . prepare_for_native_write ( )
1018- . map_err ( |e| e. to_interp_error ( id) ) ?;
1021+ if paranoid {
1022+ std:: hint:: black_box ( alloc. get_bytes_unchecked_raw ( ) . expose_provenance ( ) ) ;
1023+ // Prepare for possible write from native code if mutable.
1024+ if info. mutbl . is_mut ( ) {
1025+ self . get_alloc_raw_mut ( id) ?. 0 . prepare_for_native_write ( ) ;
1026+ }
1027+ }
1028+ }
1029+ interp_ok ( ( ) )
1030+ }
1031+
1032+ /// Updates the machine state "as if" the accesses given had been performed.
1033+ /// Used only by Miri for FFI, for taking note of events that were intercepted from foreign
1034+ /// code and properly (but still conservatively) marking their effects. Remember to call
1035+ /// `prepare_for_native_call` with `paranoid` set to false first on the same `AllocId`s, or
1036+ /// some writes may be discarded!
1037+ ///
1038+ /// The allocations in `ids` are assumed to be already exposed.
1039+ pub fn apply_accesses (
1040+ & mut self ,
1041+ mut ids : Vec < AllocId > ,
1042+ reads : Vec < std:: ops:: Range < u64 > > ,
1043+ writes : Vec < std:: ops:: Range < u64 > > ,
1044+ ) -> InterpResult < ' tcx > {
1045+ // Helper function to avoid some code duplication
1046+ fn get_start_size (
1047+ rg : std:: ops:: Range < u64 > ,
1048+ alloc_base : u64 ,
1049+ alloc_size : u64 ,
1050+ ) -> ( u64 , u64 ) {
1051+ // A bunch of range bounds nonsense that effectively simplifies to
1052+ // "get the starting point of the overlap and the length from there"
1053+ let signed_start = rg. start . cast_signed ( ) - alloc_base. cast_signed ( ) ;
1054+ let size_uncapped = if signed_start < 0 {
1055+ // We already know the ranges overlap, so this must be > 0
1056+ ( signed_start + ( rg. end - rg. start ) . cast_signed ( ) ) . try_into ( ) . unwrap ( )
1057+ } else {
1058+ rg. end - rg. start
1059+ } ;
1060+ let start: u64 = signed_start. try_into ( ) . unwrap_or ( 0 ) ;
1061+ let size = std:: cmp:: min ( size_uncapped, alloc_size - start) ;
1062+ ( start, size)
1063+ }
1064+
1065+ let mut done = FxHashSet :: default ( ) ;
1066+ while let Some ( id) = ids. pop ( ) {
1067+ if !done. insert ( id) {
1068+ continue ;
1069+ }
1070+ let info = self . get_alloc_info ( id) ;
1071+
1072+ // If there is no data behind this pointer, skip this.
1073+ if !matches ! ( info. kind, AllocKind :: LiveData ) {
1074+ continue ;
1075+ }
1076+
1077+ let alloc_base: u64 = {
1078+ // Keep the alloc here so the borrow checker is happy
1079+ let alloc = self . get_alloc_raw ( id) ?;
1080+ // No need for black_box trickery since we actually use the address
1081+ alloc. get_bytes_unchecked_raw ( ) . expose_provenance ( ) . try_into ( ) . unwrap ( )
1082+ } ;
1083+ let alloc_size = info. size . bytes ( ) ;
1084+
1085+ // Find reads which overlap with the current allocation
1086+ for rg in & reads {
1087+ let overlap = rg. start <= alloc_base + alloc_size && alloc_base <= rg. end ;
1088+ if overlap {
1089+ let ( start, size) = get_start_size ( rg. clone ( ) , alloc_base, alloc_size) ;
1090+
1091+ let alloc = self . get_alloc_raw ( id) ?;
1092+ let prov_map = alloc. provenance ( ) ;
1093+ // Only iterate on the bytes that overlap with the access
1094+ for i in start..start + size {
1095+ // We can be conservative and only expose provenances actually read
1096+ if let Some ( prov) = prov_map. get ( Size :: from_bytes ( 1 ) , self )
1097+ && rg. contains ( & ( alloc_base + i) )
1098+ {
1099+ M :: expose_provenance ( self , prov) ?;
1100+ if let Some ( id) = prov. get_alloc_id ( ) {
1101+ ids. push ( id) ;
1102+ }
1103+ }
1104+ }
1105+ }
1106+ }
1107+
1108+ // Then do the same thing for writes
1109+ for rg in & writes {
1110+ let overlap = rg. start <= alloc_base + alloc_size && alloc_base <= rg. end ;
1111+ if overlap {
1112+ let ( start, size) = get_start_size ( rg. clone ( ) , alloc_base, alloc_size) ;
1113+
1114+ let alloc_mut = self . get_alloc_raw_mut ( id) ?. 0 ;
1115+ let range =
1116+ AllocRange { start : Size :: from_bytes ( start) , size : Size :: from_bytes ( size) } ;
1117+ alloc_mut. mark_foreign_write ( range) ;
1118+ }
10191119 }
10201120 }
10211121 interp_ok ( ( ) )
0 commit comments