@@ -34,18 +34,34 @@ pub struct MemEvents {
34
34
/// A single memory access.
35
35
#[ allow( dead_code) ]
36
36
#[ cfg_attr( target_os = "linux" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
37
- #[ derive( Debug ) ]
37
+ #[ derive( Clone , Debug ) ]
38
38
pub enum AccessEvent {
39
- /// A read may have occurred on this memory range.
40
- /// Some instructions *may* read memory without *always* doing that,
41
- /// so this can be an over-approximation.
42
- /// The range info, however, is reliable if the access did happen.
39
+ /// A read occurred on this memory range.
43
40
Read ( AccessRange ) ,
44
- /// A read may have occurred on this memory range.
41
+ /// A write may have occurred on this memory range.
45
42
/// Some instructions *may* write memory without *always* doing that,
46
43
/// so this can be an over-approximation.
47
44
/// The range info, however, is reliable if the access did happen.
48
- Write ( AccessRange ) ,
45
+ /// If the second field is true, the access definitely happened.
46
+ Write ( AccessRange , bool ) ,
47
+ }
48
+
49
+ impl AccessEvent {
50
+ fn get_range ( & self ) -> AccessRange {
51
+ match self {
52
+ AccessEvent :: Read ( access_range) => access_range. clone ( ) ,
53
+ AccessEvent :: Write ( access_range, _) => access_range. clone ( ) ,
54
+ }
55
+ }
56
+
57
+ fn is_read ( & self ) -> bool {
58
+ matches ! ( self , AccessEvent :: Read ( _) )
59
+ }
60
+
61
+ fn definitely_happened ( & self ) -> bool {
62
+ // Anything except a maybe-write is definitive.
63
+ !matches ! ( self , AccessEvent :: Write ( _, false ) )
64
+ }
49
65
}
50
66
51
67
/// The memory touched by a given access.
@@ -59,6 +75,12 @@ pub struct AccessRange {
59
75
pub size : usize ,
60
76
}
61
77
78
+ impl AccessRange {
79
+ fn end ( & self ) -> usize {
80
+ self . addr . strict_add ( self . size )
81
+ }
82
+ }
83
+
62
84
impl < ' tcx > EvalContextExtPriv < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
63
85
trait EvalContextExtPriv < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
64
86
/// Call native host function and return the output as an immediate.
@@ -196,6 +218,44 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
196
218
}
197
219
None
198
220
}
221
+
222
+ /// Applies the `events` to Miri's internal state. The event vector must be
223
+ /// ordered sequentially by when the accesses happened, and the sizes are
224
+ /// assumed to be exact.
225
+ fn tracing_apply_accesses ( & mut self , events : MemEvents ) -> InterpResult < ' tcx > {
226
+ let this = self . eval_context_mut ( ) ;
227
+
228
+ for evt in events. acc_events {
229
+ let evt_rg = evt. get_range ( ) ;
230
+ // We're assuming an access only touches 1 allocation.
231
+ let alloc_id = this
232
+ . alloc_id_from_addr ( evt_rg. addr . to_u64 ( ) , evt_rg. size . try_into ( ) . unwrap ( ) , true )
233
+ . expect ( "Foreign code did an out-of-bounds access!" ) ;
234
+
235
+ let alloc = this. get_alloc_raw ( alloc_id) ?;
236
+ let alloc_addr = alloc. get_bytes_unchecked_raw ( ) . addr ( ) ;
237
+
238
+ // Shift the overlap range to be an offset from the allocation base addr.
239
+ let overlap = evt_rg. addr . strict_sub ( alloc_addr)
240
+ ..evt_rg. end ( ) . strict_sub ( alloc_addr) ;
241
+
242
+ // Reads are infallible, writes might not be.
243
+ if evt. is_read ( ) {
244
+ let p_map = alloc. provenance ( ) ;
245
+ for idx in overlap {
246
+ // If a provenance was read by the foreign code, expose it.
247
+ if let Some ( prov) = p_map. get ( Size :: from_bytes ( idx) , this) {
248
+ this. expose_provenance ( prov) ?;
249
+ }
250
+ }
251
+ } else if evt. definitely_happened ( ) || alloc. mutability . is_mut ( ) {
252
+ //let (alloc, cx) = this.get_alloc_raw_mut(alloc_id)?;
253
+ //alloc.process_native_write(AllocRange { start: overlap.start, size: overlap.len() })
254
+ }
255
+ }
256
+
257
+ interp_ok ( ( ) )
258
+ }
199
259
}
200
260
201
261
impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
@@ -221,6 +281,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
221
281
}
222
282
} ;
223
283
284
+ // Do we have ptrace?
285
+ let tracing = trace:: Supervisor :: is_enabled ( ) ;
286
+
224
287
// Get the function arguments, and convert them to `libffi`-compatible form.
225
288
let mut libffi_args = Vec :: < CArg > :: with_capacity ( args. len ( ) ) ;
226
289
for arg in args. iter ( ) {
@@ -240,9 +303,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
240
303
// The first time this happens, print a warning.
241
304
if !this. machine . native_call_mem_warned . replace ( true ) {
242
305
// Newly set, so first time we get here.
243
- this. emit_diagnostic ( NonHaltingDiagnostic :: NativeCallSharedMem {
244
- tracing : self :: trace:: Supervisor :: is_enabled ( ) ,
245
- } ) ;
306
+ this. emit_diagnostic ( NonHaltingDiagnostic :: NativeCallSharedMem { tracing } ) ;
246
307
}
247
308
248
309
this. expose_provenance ( prov) ?;
@@ -268,15 +329,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
268
329
// be read by FFI. The `black_box` is defensive programming as LLVM likes
269
330
// to (incorrectly) optimize away ptr2int casts whose result is unused.
270
331
std:: hint:: black_box ( alloc. get_bytes_unchecked_raw ( ) . expose_provenance ( ) ) ;
271
- // Expose all provenances in this allocation, since the native code can do $whatever.
272
- for prov in alloc. provenance ( ) . provenances ( ) {
273
- this. expose_provenance ( prov) ?;
332
+
333
+ if !tracing {
334
+ // Expose all provenances in this allocation, since the native code can do $whatever.
335
+ for prov in alloc. provenance ( ) . provenances ( ) {
336
+ this. expose_provenance ( prov) ?;
337
+ }
274
338
}
275
339
276
340
// Prepare for possible write from native code if mutable.
277
341
if info. mutbl . is_mut ( ) {
278
- let alloc = & mut this. get_alloc_raw_mut ( alloc_id) ?. 0 ;
342
+ let ( alloc, _cx ) = this. get_alloc_raw_mut ( alloc_id) ?;
279
343
alloc. prepare_for_native_access ( ) ;
344
+ if tracing {
345
+ //alloc.process_native_write(&cx.tcx, None);
346
+ }
280
347
// Also expose *mutable* provenance for the interpreter-level allocation.
281
348
std:: hint:: black_box ( alloc. get_bytes_unchecked_raw_mut ( ) . expose_provenance ( ) ) ;
282
349
}
@@ -288,10 +355,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
288
355
let ( ret, maybe_memevents) =
289
356
this. call_native_with_args ( link_name, dest, code_ptr, libffi_args) ?;
290
357
291
- if cfg ! ( target_os = "linux" )
292
- && let Some ( events) = maybe_memevents
293
- {
294
- trace ! ( "Registered FFI events:\n {events:#0x?}" ) ;
358
+ if tracing {
359
+ this. tracing_apply_accesses ( maybe_memevents. unwrap ( ) ) ?;
295
360
}
296
361
297
362
this. write_immediate ( * ret, dest) ?;
0 commit comments