@@ -198,7 +198,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
198198 let mut libffi_args = Vec :: < CArg > :: with_capacity ( args. len ( ) ) ;
199199 for arg in args. iter ( ) {
200200 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" )
202202 }
203203 let imm = this. read_immediate ( arg) ?;
204204 libffi_args. push ( imm_to_carg ( & imm, this) ?) ;
@@ -224,16 +224,42 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
224224 this. expose_provenance ( prov) ?;
225225 }
226226 }
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.
232228 let libffi_args = libffi_args
233229 . iter ( )
234230 . map ( |arg| arg. arg_downcast ( ) )
235231 . collect :: < Vec < libffi:: high:: Arg < ' _ > > > ( ) ;
236232
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+
237263 // Call the function and store output, depending on return type in the function signature.
238264 let ( ret, maybe_memevents) =
239265 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<'
321347 CArg :: USize ( v. to_scalar ( ) . to_target_usize ( cx) ?. try_into ( ) . unwrap ( ) ) ,
322348 ty:: RawPtr ( ..) => {
323349 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.
325352 CArg :: RawPtr ( std:: ptr:: with_exposed_provenance_mut ( s. bytes_usize ( ) ) )
326353 }
327354 _ => throw_unsup_format ! ( "unsupported argument type for native call: {}" , v. layout. ty) ,
0 commit comments