@@ -40,14 +40,27 @@ pub struct MemEvents {
40
40
/// A single memory access.
41
41
#[ allow( dead_code) ]
42
42
#[ cfg_attr( target_os = "linux" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
43
- #[ derive( Debug ) ]
43
+ #[ derive( Clone , Debug ) ]
44
44
pub enum AccessEvent {
45
45
/// A read occurred on this memory range.
46
46
Read ( AccessRange ) ,
47
- /// A read occurred on this memory range.
47
+ /// A read occurred on this memory range.
48
48
Write ( AccessRange ) ,
49
49
}
50
50
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
+
51
64
/// The memory touched by a given access.
52
65
#[ allow( dead_code) ]
53
66
#[ cfg_attr( target_os = "linux" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
@@ -198,6 +211,47 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
198
211
}
199
212
None
200
213
}
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
+ for evt in events. acc_events {
222
+ let evt_rg = evt. get_range ( ) ;
223
+ // We're assuming an access only touches 1 allocation.
224
+ let alloc_id = this. alloc_id_from_addr ( evt_rg. addr . to_u64 ( ) , evt_rg. size . try_into ( ) . unwrap ( ) , true ) . expect ( "Foreign code did an out-of-bounds access!" ) ;
225
+
226
+ // Get the (size, len) pair for the current allocation.
227
+ let ( alloc_addr, alloc_len) = {
228
+ let alloc = this. get_alloc_raw ( alloc_id) ?;
229
+ ( alloc. get_bytes_unchecked_raw ( ) . addr ( ) , alloc. len ( ) )
230
+ } ;
231
+
232
+ let unshifted_overlap = std:: cmp:: max ( evt_rg. addr , alloc_addr)
233
+ ..std:: cmp:: min ( evt_rg. addr . strict_add ( evt_rg. size ) , alloc_addr. strict_add ( alloc_len) ) ;
234
+ // Shift the overlap range to be an offset from the allocation base addr.
235
+ let overlap = unshifted_overlap. start . strict_sub ( alloc_addr)
236
+ ..unshifted_overlap. end . strict_sub ( alloc_addr) ;
237
+
238
+ if evt. is_read ( ) {
239
+ let alloc = this. get_alloc_raw ( alloc_id) ?;
240
+ let p_map = alloc. provenance ( ) ;
241
+ for idx in overlap {
242
+ // If a provenance was read by the foreign code, expose it.
243
+ if let Some ( prov) = p_map. get ( Size :: from_bytes ( idx) , this) {
244
+ this. expose_provenance ( prov) ?;
245
+ }
246
+ }
247
+ } else {
248
+ let ( _alloc_mut, _m) = this. get_alloc_raw_mut ( alloc_id) ?;
249
+ // TODO: expose a way to write wildcards on a given range and mark it as init
250
+ }
251
+ }
252
+
253
+ interp_ok ( ( ) )
254
+ }
201
255
}
202
256
203
257
impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
@@ -223,6 +277,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
223
277
}
224
278
} ;
225
279
280
+ // Do we have ptrace?
281
+ let tracing = trace:: Supervisor :: is_enabled ( ) ;
282
+
226
283
// Get the function arguments, and convert them to `libffi`-compatible form.
227
284
let mut libffi_args = Vec :: < CArg > :: with_capacity ( args. len ( ) ) ;
228
285
for arg in args. iter ( ) {
@@ -242,9 +299,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
242
299
// The first time this happens, print a warning.
243
300
if !this. machine . native_call_mem_warned . replace ( true ) {
244
301
// Newly set, so first time we get here.
245
- this. emit_diagnostic ( NonHaltingDiagnostic :: NativeCallSharedMem {
246
- tracing : self :: trace:: Supervisor :: is_enabled ( ) ,
247
- } ) ;
302
+ this. emit_diagnostic ( NonHaltingDiagnostic :: NativeCallSharedMem { tracing } ) ;
248
303
}
249
304
250
305
this. expose_provenance ( prov) ?;
@@ -270,15 +325,37 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
270
325
// be read by FFI. The `black_box` is defensive programming as LLVM likes
271
326
// to (incorrectly) optimize away ptr2int casts whose result is unused.
272
327
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) ?;
328
+
329
+ if !tracing {
330
+ // Expose all provenances in this allocation, since the native code can do $whatever.
331
+ for prov in alloc. provenance ( ) . provenances ( ) {
332
+ this. expose_provenance ( prov) ?;
333
+ }
276
334
}
277
335
278
336
// Prepare for possible write from native code if mutable.
279
337
if info. mutbl . is_mut ( ) {
280
- let alloc = & mut this. get_alloc_raw_mut ( alloc_id) ?. 0 ;
281
- alloc. prepare_for_native_access ( ) ;
338
+ let alloc = this. get_alloc_raw_mut ( alloc_id) ?. 0 ;
339
+ if tracing {
340
+ let full_range =
341
+ AllocRange { start : Size :: ZERO , size : Size :: from_bytes ( alloc. len ( ) ) } ;
342
+ // Overwrite uninitialized bytes with 0, to ensure we don't leak whatever their value happens to be.
343
+ for chunk in alloc. init_mask ( ) . clone ( ) . range_as_init_chunks ( full_range) {
344
+ if !chunk. is_init ( ) {
345
+ let uninit_bytes = unsafe {
346
+ let start = chunk. range ( ) . start . bytes_usize ( ) ;
347
+ let len = chunk. range ( ) . end . bytes_usize ( ) . strict_sub ( start) ;
348
+ let ptr = alloc. get_bytes_unchecked_raw_mut ( ) . add ( start) ;
349
+ std:: slice:: from_raw_parts_mut ( ptr, len)
350
+ } ;
351
+ uninit_bytes. fill ( 0 ) ;
352
+ }
353
+ }
354
+ } else {
355
+ // FIXME: Make this take an arg to determine whether it actually
356
+ // writes wildcard prov & marks init, so we don't duplicate code above.
357
+ alloc. prepare_for_native_access ( ) ;
358
+ }
282
359
// Also expose *mutable* provenance for the interpreter-level allocation.
283
360
std:: hint:: black_box ( alloc. get_bytes_unchecked_raw_mut ( ) . expose_provenance ( ) ) ;
284
361
}
@@ -290,10 +367,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
290
367
let ( ret, maybe_memevents) =
291
368
this. call_native_with_args ( link_name, dest, code_ptr, libffi_args) ?;
292
369
293
- if cfg ! ( target_os = "linux" )
294
- && let Some ( events) = maybe_memevents
295
- {
296
- trace ! ( "Registered FFI events:\n {events:#0x?}" ) ;
370
+ if tracing {
371
+ this. tracing_apply_accesses ( maybe_memevents. unwrap ( ) ) ?;
297
372
}
298
373
299
374
this. write_immediate ( * ret, dest) ?;
0 commit comments