Skip to content

Commit a7d5d03

Browse files
committed
interpret: move the native call preparation logic into Miri
1 parent fb4fb64 commit a7d5d03

File tree

2 files changed

+38
-18
lines changed

2 files changed

+38
-18
lines changed

src/alloc_addresses/mod.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -466,17 +466,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
466466
Some((alloc_id, Size::from_bytes(rel_offset)))
467467
}
468468

469-
/// Prepare all exposed memory for a native call.
470-
/// This overapproximates the modifications which external code might make to memory:
471-
/// We set all reachable allocations as initialized, mark all reachable provenances as exposed
472-
/// and overwrite them with `Provenance::WILDCARD`.
473-
fn prepare_exposed_for_native_call(&mut self) -> InterpResult<'tcx> {
474-
let this = self.eval_context_mut();
475-
// We need to make a deep copy of this list, but it's fine; it also serves as scratch space
476-
// for the search within `prepare_for_native_call`.
477-
let exposed: Vec<AllocId> =
478-
this.machine.alloc_addresses.get_mut().exposed.iter().copied().collect();
479-
this.prepare_for_native_call(exposed)
469+
/// Return a list of all exposed allocations.
470+
fn exposed_allocs(&self) -> Vec<AllocId> {
471+
let this = self.eval_context_ref();
472+
this.machine.alloc_addresses.borrow().exposed.iter().copied().collect()
480473
}
481474
}
482475

src/shims/native_lib/mod.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)