Skip to content

Commit 1536a10

Browse files
authored
Rollup merge of #143324 - RalfJung:native-call-prep, r=oli-obk
interpret: move the native call preparation logic into Miri `@nia-e` has to do a bunch of changes to this logic for her native call ptrace work, and it's getting annoying that the logic is split between Miri and rustc. So this moves the logic to Miri, keeping just the generic traversal part in rustc. It is unfortunate that this means we have to expose `get_alloc_raw`/`get_alloc_raw_mut`... I hope the function name is scary enough to reduce the risk of misuse. r? `@oli-obk`
2 parents 07fafc6 + a7d5d03 commit 1536a10

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)