@@ -40,14 +40,27 @@ pub struct MemEvents {
4040/// A single memory access. 
4141#[ allow( dead_code) ]  
4242#[ cfg_attr( target_os = "linux" ,  derive( serde:: Serialize ,  serde:: Deserialize ) ) ]  
43- #[ derive( Debug ) ]  
43+ #[ derive( Clone ,   Debug ) ]  
4444pub  enum  AccessEvent  { 
4545    /// A read occurred on this memory range. 
4646Read ( AccessRange ) , 
47- /// A read occurred on this memory range. 
47+      /// A read occurred on this memory range. 
4848Write ( AccessRange ) , 
4949} 
5050
51+ impl  AccessEvent  { 
52+     fn  get_range ( & self )  -> AccessRange  { 
53+         match  self  { 
54+             AccessEvent :: Read ( access_range)  => access_range. clone ( ) , 
55+             AccessEvent :: Write ( access_range)  => access_range. clone ( ) , 
56+         } 
57+     } 
58+ 
59+     fn  is_read ( & self )  -> bool  { 
60+         matches ! ( self ,  AccessEvent :: Read ( _) ) 
61+     } 
62+ } 
63+ 
5164/// The memory touched by a given access. 
5265#[ allow( dead_code) ]  
5366#[ cfg_attr( target_os = "linux" ,  derive( serde:: Serialize ,  serde:: Deserialize ) ) ]  
@@ -198,6 +211,78 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
198211        } 
199212        None 
200213    } 
214+ 
215+     /// Applies the `events` to Miri's internal state. The event vector must be 
216+ /// ordered sequentially by when the accesses happened, and the sizes are 
217+ /// assumed to be exact. 
218+ fn  tracing_apply_accesses ( & mut  self ,  events :  MemEvents )  -> InterpResult < ' tcx >  { 
219+         let  this = self . eval_context_mut ( ) ; 
220+ 
221+         // TODO: This could be optimised! If we first "compress" down the reads 
222+         // and writes to discard redundant reads and writes and sort them by 
223+         // address, we can take this from worst-case O(accesses * allocations) 
224+         // to O(log(accesses) * allocations). 
225+         for  evt in  events. acc_events  { 
226+             let  mut  todo = this. exposed_allocs ( ) ; 
227+             let  mut  done = rustc_data_structures:: fx:: FxHashSet :: default ( ) ; 
228+             while  let  Some ( alloc_id)  = todo. pop ( )  { 
229+                 if  done. insert ( alloc_id)  { 
230+                     continue ; 
231+                 } 
232+ 
233+                 let  info = this. get_alloc_info ( alloc_id) ; 
234+                 // If there is no data behind this pointer, skip this. 
235+                 if  !matches ! ( info. kind,  AllocKind :: LiveData )  { 
236+                     continue ; 
237+                 } 
238+ 
239+                 // Get the (size, len) pair for the current allocation... 
240+                 let  ( alloc_addr,  alloc_len)  = { 
241+                     let  alloc = this. get_alloc_raw ( alloc_id) ?; 
242+                     ( alloc. get_bytes_unchecked_raw ( ) . addr ( ) ,  alloc. len ( ) ) 
243+                 } ; 
244+ 
245+                 // ...and for the current accesses, checking if they overlap. 
246+                 let  rg = evt. get_range ( ) ; 
247+                 if  !( rg. addr  <= alloc_addr. strict_add ( alloc_len) 
248+                     && alloc_addr <= rg. addr . strict_add ( rg. size ) ) 
249+                 { 
250+                     continue ; 
251+                 } 
252+ 
253+                 // Shift the overlap range to be an offset from the allocation base addr. 
254+                 let  unshifted_overlap = std:: cmp:: max ( rg. addr ,  alloc_addr) 
255+                     ..std:: cmp:: min ( rg. addr . strict_add ( rg. size ) ,  alloc_addr. strict_add ( alloc_len) ) ; 
256+                 let  overlap = unshifted_overlap. start . strict_sub ( alloc_addr) 
257+                     ..unshifted_overlap. end . strict_sub ( alloc_addr) ; 
258+ 
259+                 if  evt. is_read ( )  { 
260+                     let  alloc = this. get_alloc_raw ( alloc_id) ?; 
261+                     let  p_map = alloc. provenance ( ) ; 
262+                     for  idx in  overlap { 
263+                         // If a provenance was read by the foreign code, expose it and add it to the todo list. 
264+                         if  let  Some ( prov)  = p_map. get ( Size :: from_bytes ( idx) ,  this)  { 
265+                             // Do this extra check since we get bytewise provenance, 
266+                             // so otherwise we risk inserting 4/8 copies of it per pointer. 
267+                             // TODO: Can freestanding bytes even have 
268+                             if  let  Some ( prov_id)  = prov. get_alloc_id ( ) 
269+                                 && !todo. contains ( & prov_id) 
270+                                 && !done. contains ( & prov_id) 
271+                             { 
272+                                 todo. push ( prov_id) ; 
273+                             } 
274+                             this. expose_provenance ( prov) ?; 
275+                         } 
276+                     } 
277+                 }  else  { 
278+                     let  ( _alloc_mut,  _m)  = this. get_alloc_raw_mut ( alloc_id) ?; 
279+                     // TODO: expose a way to write wildcards on a given range and mark it as init 
280+                 } 
281+             } 
282+         } 
283+ 
284+         interp_ok ( ( ) ) 
285+     } 
201286} 
202287
203288impl < ' tcx >  EvalContextExt < ' tcx >  for  crate :: MiriInterpCx < ' tcx >  { } 
@@ -223,6 +308,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
223308            } 
224309        } ; 
225310
311+         // Do we have ptrace? 
312+         let  tracing = trace:: Supervisor :: is_enabled ( ) ; 
313+ 
226314        // Get the function arguments, and convert them to `libffi`-compatible form. 
227315        let  mut  libffi_args = Vec :: < CArg > :: with_capacity ( args. len ( ) ) ; 
228316        for  arg in  args. iter ( )  { 
@@ -242,9 +330,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
242330                // The first time this happens, print a warning. 
243331                if  !this. machine . native_call_mem_warned . replace ( true )  { 
244332                    // Newly set, so first time we get here. 
245-                     this. emit_diagnostic ( NonHaltingDiagnostic :: NativeCallSharedMem  { 
246-                         tracing :  self :: trace:: Supervisor :: is_enabled ( ) , 
247-                     } ) ; 
333+                     this. emit_diagnostic ( NonHaltingDiagnostic :: NativeCallSharedMem  {  tracing } ) ; 
248334                } 
249335
250336                this. expose_provenance ( prov) ?; 
@@ -270,15 +356,37 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
270356            // be read by FFI. The `black_box` is defensive programming as LLVM likes 
271357            // to (incorrectly) optimize away ptr2int casts whose result is unused. 
272358            std:: hint:: black_box ( alloc. get_bytes_unchecked_raw ( ) . expose_provenance ( ) ) ; 
273-             // Expose all provenances in this allocation, since the native code can do $whatever. 
274-             for  prov in  alloc. provenance ( ) . provenances ( )  { 
275-                 this. expose_provenance ( prov) ?; 
359+ 
360+             if  !tracing { 
361+                 // Expose all provenances in this allocation, since the native code can do $whatever. 
362+                 for  prov in  alloc. provenance ( ) . provenances ( )  { 
363+                     this. expose_provenance ( prov) ?; 
364+                 } 
276365            } 
277366
278367            // Prepare for possible write from native code if mutable. 
279368            if  info. mutbl . is_mut ( )  { 
280-                 let  alloc = & mut  this. get_alloc_raw_mut ( alloc_id) ?. 0 ; 
281-                 alloc. prepare_for_native_access ( ) ; 
369+                 let  alloc = this. get_alloc_raw_mut ( alloc_id) ?. 0 ; 
370+                 if  tracing { 
371+                     let  full_range =
372+                         AllocRange  {  start :  Size :: ZERO ,  size :  Size :: from_bytes ( alloc. len ( ) )  } ; 
373+                     // Overwrite uninitialized bytes with 0, to ensure we don't leak whatever their value happens to be. 
374+                     for  chunk in  alloc. init_mask ( ) . clone ( ) . range_as_init_chunks ( full_range)  { 
375+                         if  !chunk. is_init ( )  { 
376+                             let  uninit_bytes = unsafe  { 
377+                                 let  start = chunk. range ( ) . start . bytes_usize ( ) ; 
378+                                 let  len = chunk. range ( ) . end . bytes_usize ( ) . strict_sub ( start) ; 
379+                                 let  ptr = alloc. get_bytes_unchecked_raw_mut ( ) . add ( start) ; 
380+                                 std:: slice:: from_raw_parts_mut ( ptr,  len) 
381+                             } ; 
382+                             uninit_bytes. fill ( 0 ) ; 
383+                         } 
384+                     } 
385+                 }  else  { 
386+                     // FIXME: Make this take an arg to determine whether it actually 
387+                     // writes wildcard prov & marks init, so we don't duplicate code above. 
388+                     alloc. prepare_for_native_access ( ) ; 
389+                 } 
282390                // Also expose *mutable* provenance for the interpreter-level allocation. 
283391                std:: hint:: black_box ( alloc. get_bytes_unchecked_raw_mut ( ) . expose_provenance ( ) ) ; 
284392            } 
@@ -290,10 +398,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
290398        let  ( ret,  maybe_memevents)  =
291399            this. call_native_with_args ( link_name,  dest,  code_ptr,  libffi_args) ?; 
292400
293-         if  cfg ! ( target_os = "linux" ) 
294-             && let  Some ( events)  = maybe_memevents
295-         { 
296-             trace ! ( "Registered FFI events:\n {events:#0x?}" ) ; 
401+         if  tracing { 
402+             this. tracing_apply_accesses ( maybe_memevents. unwrap ( ) ) ?; 
297403        } 
298404
299405        this. write_immediate ( * ret,  dest) ?; 
0 commit comments