Skip to content

Commit 8d0e0c6

Browse files
committed
interpret/allocation: expose init + write_wildcards on a range
1 parent 2783fc4 commit 8d0e0c6

File tree

4 files changed

+42
-28
lines changed

4 files changed

+42
-28
lines changed

compiler/rustc_const_eval/src/interpret/memory.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1472,7 +1472,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
14721472
dest_alloc
14731473
.write_uninit(&tcx, dest_range)
14741474
.map_err(|e| e.to_interp_error(dest_alloc_id))?;
1475-
// We can forget about the provenance, this is all not initialized anyway.
1475+
// `write_uninit` also resets the provenance, so we are done.
14761476
return interp_ok(());
14771477
}
14781478

compiler/rustc_middle/src/mir/interpret/allocation.rs

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ pub struct Allocation<Prov: Provenance = CtfeProvenance, Extra = (), Bytes = Box
101101
/// at the given offset.
102102
provenance: ProvenanceMap<Prov>,
103103
/// Denotes which part of this allocation is initialized.
104+
///
105+
/// Invariant: the uninitialized parts have no provenance.
104106
init_mask: InitMask,
105107
/// The alignment of the allocation to detect unaligned reads.
106108
/// (`Align` guarantees that this is a power of two.)
@@ -796,24 +798,19 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
796798
Ok(())
797799
}
798800

799-
/// Initialize all previously uninitialized bytes in the entire allocation, and set
800-
/// provenance of everything to `Wildcard`. Before calling this, make sure all
801-
/// provenance in this allocation is exposed!
802-
pub fn prepare_for_native_access(&mut self) {
803-
let full_range = AllocRange { start: Size::ZERO, size: Size::from_bytes(self.len()) };
804-
// Overwrite uninitialized bytes with 0, to ensure we don't leak whatever their value happens to be.
805-
for chunk in self.init_mask.range_as_init_chunks(full_range) {
806-
if !chunk.is_init() {
807-
let uninit_bytes = &mut self.bytes
808-
[chunk.range().start.bytes_usize()..chunk.range().end.bytes_usize()];
809-
uninit_bytes.fill(0);
810-
}
811-
}
812-
// Mark everything as initialized now.
813-
self.mark_init(full_range, true);
814-
815-
// Set provenance of all bytes to wildcard.
816-
self.provenance.write_wildcards(self.len());
801+
/// Mark all bytes in the given range as initialised and reset the provenance
802+
/// to wildcards. This entirely breaks the normal mechanisms for tracking
803+
/// initialisation and is only provided for Miri operating in native-lib
804+
/// mode. UB will be missed if the underlying bytes were not actually written to.
805+
///
806+
/// If `range` is `None`, defaults to performing this on the whole allocation.
807+
pub fn process_native_write(&mut self, cx: &impl HasDataLayout, range: Option<AllocRange>) {
808+
let range = range.unwrap_or_else(|| AllocRange {
809+
start: Size::ZERO,
810+
size: Size::from_bytes(self.len()),
811+
});
812+
self.mark_init(range, true);
813+
self.provenance.write_wildcards(cx, range);
817814
}
818815

819816
/// Remove all provenance in the given memory range.

compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,21 +212,37 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
212212
Ok(())
213213
}
214214

215-
/// Overwrites all provenance in the allocation with wildcard provenance.
215+
/// Overwrites all provenance in the given range with wildcard provenance.
216+
/// Pointers partially overwritten will have their provenances preserved
217+
/// bytewise on their remaining bytes.
216218
///
217219
/// Provided for usage in Miri and panics otherwise.
218-
pub fn write_wildcards(&mut self, alloc_size: usize) {
220+
pub fn write_wildcards(&mut self, cx: &impl HasDataLayout, range: AllocRange) {
219221
assert!(
220222
Prov::OFFSET_IS_ADDR,
221223
"writing wildcard provenance is not supported when `OFFSET_IS_ADDR` is false"
222224
);
223225
let wildcard = Prov::WILDCARD.unwrap();
224226

225-
// Remove all pointer provenances, then write wildcards into the whole byte range.
226-
self.ptrs.clear();
227-
let last = Size::from_bytes(alloc_size);
228227
let bytes = self.bytes.get_or_insert_with(Box::default);
229-
for offset in Size::ZERO..last {
228+
229+
// Remove pointer provenances that overlap with the range, then readd the edge ones bytewise.
230+
let ptr_range = Self::adjusted_range_ptrs(range, cx);
231+
let ptrs = self.ptrs.range(ptr_range.clone());
232+
if let Some((offset, prov)) = ptrs.first() {
233+
for byte_ofs in *offset..range.start {
234+
bytes.insert(byte_ofs, *prov);
235+
}
236+
}
237+
if let Some((offset, prov)) = ptrs.last() {
238+
for byte_ofs in range.end()..*offset + cx.data_layout().pointer_size() {
239+
bytes.insert(byte_ofs, *prov);
240+
}
241+
}
242+
self.ptrs.remove_range(ptr_range);
243+
244+
// Overwrite bytewise provenance.
245+
for offset in range.start..range.end() {
230246
bytes.insert(offset, wildcard);
231247
}
232248
}

src/tools/miri/src/shims/native_lib/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
231231
.collect::<Vec<libffi::high::Arg<'_>>>();
232232

233233
// Prepare all exposed memory (both previously exposed, and just newly exposed since a
234-
// pointer was passed as argument).
234+
// pointer was passed as argument). Uninitialised memory is left as-is, but any data
235+
// exposed this way is garbage anyway.
235236
this.visit_reachable_allocs(this.exposed_allocs(), |this, alloc_id, info| {
236237
// If there is no data behind this pointer, skip this.
237238
if !matches!(info.kind, AllocKind::LiveData) {
@@ -251,8 +252,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
251252

252253
// Prepare for possible write from native code if mutable.
253254
if info.mutbl.is_mut() {
254-
let alloc = &mut this.get_alloc_raw_mut(alloc_id)?.0;
255-
alloc.prepare_for_native_access();
255+
let (alloc, cx) = this.get_alloc_raw_mut(alloc_id)?;
256+
alloc.process_native_write(&cx.tcx, None);
256257
// Also expose *mutable* provenance for the interpreter-level allocation.
257258
std::hint::black_box(alloc.get_bytes_unchecked_raw_mut().expose_provenance());
258259
}

0 commit comments

Comments
 (0)