@@ -198,7 +198,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
198
198
let mut libffi_args = Vec :: < CArg > :: with_capacity ( args. len ( ) ) ;
199
199
for arg in args. iter ( ) {
200
200
if !matches ! ( arg. layout. backend_repr, BackendRepr :: Scalar ( _) ) {
201
- throw_unsup_format ! ( "only scalar argument types are support for native calls" )
201
+ throw_unsup_format ! ( "only scalar argument types are supported for native calls" )
202
202
}
203
203
let imm = this. read_immediate ( arg) ?;
204
204
libffi_args. push ( imm_to_carg ( & imm, this) ?) ;
@@ -224,16 +224,42 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
224
224
this. expose_provenance ( prov) ?;
225
225
}
226
226
}
227
-
228
- // Prepare all exposed memory.
229
- this. prepare_exposed_for_native_call ( ) ?;
230
-
231
- // Convert them to `libffi::high::Arg` type.
227
+ // Convert arguments to `libffi::high::Arg` type.
232
228
let libffi_args = libffi_args
233
229
. iter ( )
234
230
. map ( |arg| arg. arg_downcast ( ) )
235
231
. collect :: < Vec < libffi:: high:: Arg < ' _ > > > ( ) ;
236
232
233
+ // Prepare all exposed memory (both previously exposed, and just newly exposed since a
234
+ // pointer was passed as argument).
235
+ this. visit_reachable_allocs ( this. exposed_allocs ( ) , |this, alloc_id, info| {
236
+ // If there is no data behind this pointer, skip this.
237
+ if !matches ! ( info. kind, AllocKind :: LiveData ) {
238
+ return interp_ok ( ( ) ) ;
239
+ }
240
+ // It's okay to get raw access, what we do does not correspond to any actual
241
+ // AM operation, it just approximates the state to account for the native call.
242
+ let alloc = this. get_alloc_raw ( alloc_id) ?;
243
+ // Also expose the provenance of the interpreter-level allocation, so it can
244
+ // be read by FFI. The `black_box` is defensive programming as LLVM likes
245
+ // to (incorrectly) optimize away ptr2int casts whose result is unused.
246
+ std:: hint:: black_box ( alloc. get_bytes_unchecked_raw ( ) . expose_provenance ( ) ) ;
247
+ // Expose all provenances in this allocation, since the native code can do $whatever.
248
+ for prov in alloc. provenance ( ) . provenances ( ) {
249
+ this. expose_provenance ( prov) ?;
250
+ }
251
+
252
+ // Prepare for possible write from native code if mutable.
253
+ if info. mutbl . is_mut ( ) {
254
+ let alloc = & mut this. get_alloc_raw_mut ( alloc_id) ?. 0 ;
255
+ alloc. prepare_for_native_access ( ) ;
256
+ // Also expose *mutable* provenance for the interpreter-level allocation.
257
+ std:: hint:: black_box ( alloc. get_bytes_unchecked_raw_mut ( ) . expose_provenance ( ) ) ;
258
+ }
259
+
260
+ interp_ok ( ( ) )
261
+ } ) ?;
262
+
237
263
// Call the function and store output, depending on return type in the function signature.
238
264
let ( ret, maybe_memevents) =
239
265
this. call_native_with_args ( link_name, dest, code_ptr, libffi_args) ?;
@@ -321,7 +347,8 @@ fn imm_to_carg<'tcx>(v: &ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'
321
347
CArg :: USize ( v. to_scalar ( ) . to_target_usize ( cx) ?. try_into ( ) . unwrap ( ) ) ,
322
348
ty:: RawPtr ( ..) => {
323
349
let s = v. to_scalar ( ) . to_pointer ( cx) ?. addr ( ) ;
324
- // This relies on the `expose_provenance` in `prepare_for_native_call`.
350
+ // This relies on the `expose_provenance` in the `visit_reachable_allocs` callback
351
+ // above.
325
352
CArg :: RawPtr ( std:: ptr:: with_exposed_provenance_mut ( s. bytes_usize ( ) ) )
326
353
}
327
354
_ => throw_unsup_format ! ( "unsupported argument type for native call: {}" , v. layout. ty) ,
0 commit comments