diff options
| author | Ralf Jung <post@ralfj.de> | 2025-07-02 14:16:28 +0200 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2025-07-02 14:25:11 +0200 |
| commit | de1278bd16fdef81a2b93f43d4ae9159da716d95 (patch) | |
| tree | ba4691722cddf571a55e74346ba33124b567b3d1 /src | |
| parent | f51c9870bab634afb9e7a262b6ca7816bb9e940d (diff) | |
| download | rust-de1278bd16fdef81a2b93f43d4ae9159da716d95.tar.gz rust-de1278bd16fdef81a2b93f43d4ae9159da716d95.zip | |
interpret: move the native call preparation logic into Miri
Diffstat (limited to 'src')
| -rw-r--r-- | src/tools/miri/src/alloc_addresses/mod.rs | 15 | ||||
| -rw-r--r-- | src/tools/miri/src/shims/native_lib/mod.rs | 41 |
2 files changed, 38 insertions, 18 deletions
diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index 1796120cf8a..3cc38fa087c 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -466,17 +466,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some((alloc_id, Size::from_bytes(rel_offset))) } - /// Prepare all exposed memory for a native call. - /// This overapproximates the modifications which external code might make to memory: - /// We set all reachable allocations as initialized, mark all reachable provenances as exposed - /// and overwrite them with `Provenance::WILDCARD`. - fn prepare_exposed_for_native_call(&mut self) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - // We need to make a deep copy of this list, but it's fine; it also serves as scratch space - // for the search within `prepare_for_native_call`. - let exposed: Vec<AllocId> = - this.machine.alloc_addresses.get_mut().exposed.iter().copied().collect(); - this.prepare_for_native_call(exposed) + /// Return a list of all exposed allocations. + fn exposed_allocs(&self) -> Vec<AllocId> { + let this = self.eval_context_ref(); + this.machine.alloc_addresses.borrow().exposed.iter().copied().collect() } } diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 9c659f65e50..9b30d8ce78b 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -198,7 +198,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut libffi_args = Vec::<CArg>::with_capacity(args.len()); for arg in args.iter() { if !matches!(arg.layout.backend_repr, BackendRepr::Scalar(_)) { - throw_unsup_format!("only scalar argument types are support for native calls") + throw_unsup_format!("only scalar argument types are supported for native calls") } let imm = this.read_immediate(arg)?; libffi_args.push(imm_to_carg(&imm, this)?); @@ -224,16 +224,42 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.expose_provenance(prov)?; } } - - // Prepare all exposed memory. - this.prepare_exposed_for_native_call()?; - - // Convert them to `libffi::high::Arg` type. + // Convert arguments to `libffi::high::Arg` type. let libffi_args = libffi_args .iter() .map(|arg| arg.arg_downcast()) .collect::<Vec<libffi::high::Arg<'_>>>(); + // Prepare all exposed memory (both previously exposed, and just newly exposed since a + // pointer was passed as argument). + this.visit_reachable_allocs(this.exposed_allocs(), |this, alloc_id, info| { + // If there is no data behind this pointer, skip this. + if !matches!(info.kind, AllocKind::LiveData) { + return interp_ok(()); + } + // It's okay to get raw access, what we do does not correspond to any actual + // AM operation, it just approximates the state to account for the native call. + let alloc = this.get_alloc_raw(alloc_id)?; + // Also expose the provenance of the interpreter-level allocation, so it can + // be read by FFI. The `black_box` is defensive programming as LLVM likes + // to (incorrectly) optimize away ptr2int casts whose result is unused. + std::hint::black_box(alloc.get_bytes_unchecked_raw().expose_provenance()); + // Expose all provenances in this allocation, since the native code can do $whatever. + for prov in alloc.provenance().provenances() { + this.expose_provenance(prov)?; + } + + // Prepare for possible write from native code if mutable. + if info.mutbl.is_mut() { + let alloc = &mut this.get_alloc_raw_mut(alloc_id)?.0; + alloc.prepare_for_native_access(); + // Also expose *mutable* provenance for the interpreter-level allocation. + std::hint::black_box(alloc.get_bytes_unchecked_raw_mut().expose_provenance()); + } + + interp_ok(()) + })?; + // Call the function and store output, depending on return type in the function signature. let (ret, maybe_memevents) = 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<' CArg::USize(v.to_scalar().to_target_usize(cx)?.try_into().unwrap()), ty::RawPtr(..) => { let s = v.to_scalar().to_pointer(cx)?.addr(); - // This relies on the `expose_provenance` in `prepare_for_native_call`. + // This relies on the `expose_provenance` in the `visit_reachable_allocs` callback + // above. CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize())) } _ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty), |
