about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <476013+matthiaskrgr@users.noreply.github.com>2025-03-11 19:35:34 +0100
committerGitHub <noreply@github.com>2025-03-11 19:35:34 +0100
commit4feb86684dc1edd32e26170db15b964e2207adc8 (patch)
treea1d0ea90a29e636eedda79c371c7a30e2efcc277
parent954b88ed2aeeaaa2ccb261097f36379b244fc4ad (diff)
parent3846f942300c4fd8f43a8a8a1324ad5e358b9459 (diff)
downloadrust-4feb86684dc1edd32e26170db15b964e2207adc8.tar.gz
rust-4feb86684dc1edd32e26170db15b964e2207adc8.zip
Rollup merge of #138352 - RalfJung:miri-native-calls-exposed, r=oli-obk
miri native_calls: ensure we actually expose *mutable* provenance to the memory FFI can access

In native call mode, the interpreter memory itself is accessed directly by external code via pointers created from integers and passed via libffi, so we have to ensure the provenance in Miri itself (on the meta level) is sufficiently exposed. So far we only exposed the provenance for read-only accesses. This may we enough as that may actually be the same provenance as for mutable accesses, but it's hard to be sure, and anyway there's no reason to do such a gambit -- we have this function, `prepare_for_native_call`, which iterates all memory the call can access. let's just also (re-)expose Miri's own allocations there. We expose the read-only provenance for all of them and the mutable provenance for the mutable allocations.

r? ``@oli-obk``
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs4
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs5
-rw-r--r--src/tools/miri/src/alloc_addresses/mod.rs4
-rw-r--r--src/tools/miri/src/shims/native_lib.rs2
4 files changed, 12 insertions, 3 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index ce0b5a350e0..e5af0673629 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -982,6 +982,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                     todo.push(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());
 
             // Prepare for possible write from native code if mutable.
             if info.mutbl.is_mut() {
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index ba65a711815..b24f6bc7770 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -679,6 +679,11 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
         // Set provenance of all bytes to wildcard.
         self.provenance.write_wildcards(self.len());
 
+        // Also expose the provenance of the interpreter-level allocation, so it can
+        // be written 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(self.get_bytes_unchecked_raw_mut().expose_provenance());
+
         Ok(())
     }
 
diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs
index ff3a25e94bd..5d257029a46 100644
--- a/src/tools/miri/src/alloc_addresses/mod.rs
+++ b/src/tools/miri/src/alloc_addresses/mod.rs
@@ -198,8 +198,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
                 AllocKind::Dead => unreachable!(),
             };
-            // Ensure this pointer's provenance is exposed, so that it can be used by FFI code.
-            return interp_ok(base_ptr.expose_provenance().try_into().unwrap());
+            // We don't have to expose this pointer yet, we do that in `prepare_for_native_call`.
+            return interp_ok(base_ptr.addr().try_into().unwrap());
         }
         // We are not in native lib mode, so we control the addresses ourselves.
         if let Some((reuse_addr, clock)) = global_state.reuse.take_addr(
diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs
index c6fcb0355eb..0258a76c3e7 100644
--- a/src/tools/miri/src/shims/native_lib.rs
+++ b/src/tools/miri/src/shims/native_lib.rs
@@ -266,7 +266,7 @@ 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 `addr_from_alloc_id`.
+            // This relies on the `expose_provenance` in `prepare_for_native_call`.
             CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize()))
         }
         _ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty),