extend Miri to correctly pass mutable pointers through FFI
Co-authored-by: Ralf Jung <post@ralfj.de>
This commit is contained in:
parent
5926e82dd1
commit
712ceaba35
19 changed files with 476 additions and 59 deletions
|
@ -168,9 +168,9 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine {
|
|||
})
|
||||
}
|
||||
|
||||
fn expose_ptr(
|
||||
_ecx: &mut InterpCx<'tcx, Self>,
|
||||
_ptr: interpret::Pointer<Self::Provenance>,
|
||||
fn expose_provenance(
|
||||
_ecx: &InterpCx<'tcx, Self>,
|
||||
_provenance: Self::Provenance,
|
||||
) -> interpret::InterpResult<'tcx> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
|
|
@ -21,9 +21,8 @@ use crate::errors::{LongRunning, LongRunningWarn};
|
|||
use crate::fluent_generated as fluent;
|
||||
use crate::interpret::{
|
||||
self, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy,
|
||||
InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, RangeSet, Scalar, compile_time_machine,
|
||||
interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup,
|
||||
throw_unsup_format,
|
||||
InterpCx, InterpResult, MPlaceTy, OpTy, RangeSet, Scalar, compile_time_machine, interp_ok,
|
||||
throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format,
|
||||
};
|
||||
|
||||
/// When hitting this many interpreted terminators we emit a deny by default lint
|
||||
|
@ -586,7 +585,10 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn expose_ptr(_ecx: &mut InterpCx<'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx> {
|
||||
fn expose_provenance(
|
||||
_ecx: &InterpCx<'tcx, Self>,
|
||||
_provenance: Self::Provenance,
|
||||
) -> InterpResult<'tcx> {
|
||||
// This is only reachable with -Zunleash-the-miri-inside-of-you.
|
||||
throw_unsup_format!("exposing pointers is not possible at compile-time")
|
||||
}
|
||||
|
|
|
@ -238,7 +238,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let scalar = src.to_scalar();
|
||||
let ptr = scalar.to_pointer(self)?;
|
||||
match ptr.into_pointer_or_addr() {
|
||||
Ok(ptr) => M::expose_ptr(self, ptr)?,
|
||||
Ok(ptr) => M::expose_provenance(self, ptr.provenance)?,
|
||||
Err(_) => {} // Do nothing, exposing an invalid pointer (`None` provenance) is a NOP.
|
||||
};
|
||||
interp_ok(ImmTy::from_scalar(
|
||||
|
|
|
@ -327,11 +327,11 @@ pub trait Machine<'tcx>: Sized {
|
|||
addr: u64,
|
||||
) -> InterpResult<'tcx, Pointer<Option<Self::Provenance>>>;
|
||||
|
||||
/// Marks a pointer as exposed, allowing it's provenance
|
||||
/// Marks a pointer as exposed, allowing its provenance
|
||||
/// to be recovered. "Pointer-to-int cast"
|
||||
fn expose_ptr(
|
||||
ecx: &mut InterpCx<'tcx, Self>,
|
||||
ptr: Pointer<Self::Provenance>,
|
||||
fn expose_provenance(
|
||||
ecx: &InterpCx<'tcx, Self>,
|
||||
provenance: Self::Provenance,
|
||||
) -> InterpResult<'tcx>;
|
||||
|
||||
/// Convert a pointer with provenance into an allocation-offset pair and extra provenance info.
|
||||
|
|
|
@ -944,6 +944,52 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
interp_ok(())
|
||||
}
|
||||
|
||||
/// Handle the effect an FFI call might have on the state of allocations.
|
||||
/// This overapproximates the modifications which external code might make to memory:
|
||||
/// We set all reachable allocations as initialized, mark all provenances as exposed
|
||||
/// and overwrite them with `Provenance::WILDCARD`.
|
||||
pub fn prepare_for_native_call(
|
||||
&mut self,
|
||||
id: AllocId,
|
||||
initial_prov: M::Provenance,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Expose provenance of the root allocation.
|
||||
M::expose_provenance(self, initial_prov)?;
|
||||
|
||||
let mut done = FxHashSet::default();
|
||||
let mut todo = vec![id];
|
||||
while let Some(id) = todo.pop() {
|
||||
if !done.insert(id) {
|
||||
// We already saw this allocation before, don't process it again.
|
||||
continue;
|
||||
}
|
||||
let info = self.get_alloc_info(id);
|
||||
|
||||
// If there is no data behind this pointer, skip this.
|
||||
if !matches!(info.kind, AllocKind::LiveData) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Expose all provenances in this allocation, and add them to `todo`.
|
||||
let alloc = self.get_alloc_raw(id)?;
|
||||
for prov in alloc.provenance().provenances() {
|
||||
M::expose_provenance(self, prov)?;
|
||||
if let Some(id) = prov.get_alloc_id() {
|
||||
todo.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare for possible write from native code if mutable.
|
||||
if info.mutbl.is_mut() {
|
||||
self.get_alloc_raw_mut(id)?
|
||||
.0
|
||||
.prepare_for_native_write()
|
||||
.map_err(|e| e.to_interp_error(id))?;
|
||||
}
|
||||
}
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
/// Create a lazy debug printer that prints the given allocation and all allocations it points
|
||||
/// to, recursively.
|
||||
#[must_use]
|
||||
|
|
|
@ -643,6 +643,28 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialize all previously uninitialized bytes in the entire allocation, and set
|
||||
/// provenance of everything to `Wildcard`. Before calling this, make sure all
|
||||
/// provenance in this allocation is exposed!
|
||||
pub fn prepare_for_native_write(&mut self) -> AllocResult {
|
||||
let full_range = AllocRange { start: Size::ZERO, size: Size::from_bytes(self.len()) };
|
||||
// Overwrite uninitialized bytes with 0, to ensure we don't leak whatever their value happens to be.
|
||||
for chunk in self.init_mask.range_as_init_chunks(full_range) {
|
||||
if !chunk.is_init() {
|
||||
let uninit_bytes = &mut self.bytes
|
||||
[chunk.range().start.bytes_usize()..chunk.range().end.bytes_usize()];
|
||||
uninit_bytes.fill(0);
|
||||
}
|
||||
}
|
||||
// Mark everything as initialized now.
|
||||
self.mark_init(full_range, true);
|
||||
|
||||
// Set provenance of all bytes to wildcard.
|
||||
self.provenance.write_wildcards(self.len());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove all provenance in the given memory range.
|
||||
pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult {
|
||||
self.provenance.clear(range, cx)?;
|
||||
|
|
|
@ -195,6 +195,25 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Overwrites all provenance in the allocation with wildcard provenance.
|
||||
///
|
||||
/// Provided for usage in Miri and panics otherwise.
|
||||
pub fn write_wildcards(&mut self, alloc_size: usize) {
|
||||
assert!(
|
||||
Prov::OFFSET_IS_ADDR,
|
||||
"writing wildcard provenance is not supported when `OFFSET_IS_ADDR` is false"
|
||||
);
|
||||
let wildcard = Prov::WILDCARD.unwrap();
|
||||
|
||||
// Remove all pointer provenances, then write wildcards into the whole byte range.
|
||||
self.ptrs.clear();
|
||||
let last = Size::from_bytes(alloc_size);
|
||||
let bytes = self.bytes.get_or_insert_with(Box::default);
|
||||
for offset in Size::ZERO..last {
|
||||
bytes.insert(offset, wildcard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A partial, owned list of provenance to transfer into another allocation.
|
||||
|
|
|
@ -66,6 +66,9 @@ pub trait Provenance: Copy + fmt::Debug + 'static {
|
|||
/// pointer, and implement ptr-to-int transmutation by stripping provenance.
|
||||
const OFFSET_IS_ADDR: bool;
|
||||
|
||||
/// If wildcard provenance is implemented, contains the unique, general wildcard provenance variant.
|
||||
const WILDCARD: Option<Self>;
|
||||
|
||||
/// Determines how a pointer should be printed.
|
||||
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result;
|
||||
|
||||
|
@ -168,6 +171,9 @@ impl Provenance for CtfeProvenance {
|
|||
// so ptr-to-int casts are not possible (since we do not know the global physical offset).
|
||||
const OFFSET_IS_ADDR: bool = false;
|
||||
|
||||
// `CtfeProvenance` does not implement wildcard provenance.
|
||||
const WILDCARD: Option<Self> = None;
|
||||
|
||||
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Print AllocId.
|
||||
fmt::Debug::fmt(&ptr.provenance.alloc_id(), f)?; // propagates `alternate` flag
|
||||
|
@ -197,6 +203,9 @@ impl Provenance for AllocId {
|
|||
// so ptr-to-int casts are not possible (since we do not know the global physical offset).
|
||||
const OFFSET_IS_ADDR: bool = false;
|
||||
|
||||
// `AllocId` does not implement wildcard provenance.
|
||||
const WILDCARD: Option<Self> = None;
|
||||
|
||||
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Forward `alternate` flag to `alloc_id` printing.
|
||||
if f.alternate() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue