Skip to content

Commit 60e7104

Browse files
nia-eRalfJung
authored andcommitted
interpret/allocation: expose init + write_wildcards on a range
1 parent 2783fc4 commit 60e7104

File tree

4 files changed

+42
-27
lines changed

4 files changed

+42
-27
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: 17 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 are 0 (in `bytes`) and 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.)
@@ -792,28 +794,25 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
792794
/// Write "uninit" to the given memory range.
793795
pub fn write_uninit(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult {
794796
self.mark_init(range, false);
797+
// Restore the invariant that the uninit parts are 0 and have no provenance.
798+
self.bytes[range.start.bytes_usize()..range.end().bytes_usize()].fill(0);
795799
self.provenance.clear(range, cx)?;
796800
Ok(())
797801
}
798802

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());
803+
/// Mark all bytes in the given range as initialised and reset the provenance
804+
/// to wildcards. This entirely breaks the normal mechanisms for tracking
805+
/// initialisation and is only provided for Miri operating in native-lib
806+
/// mode. UB will be missed if the underlying bytes were not actually written to.
807+
///
808+
/// If `range` is `None`, defaults to performing this on the whole allocation.
809+
pub fn process_native_write(&mut self, cx: &impl HasDataLayout, range: Option<AllocRange>) {
810+
let range = range.unwrap_or_else(|| AllocRange {
811+
start: Size::ZERO,
812+
size: Size::from_bytes(self.len()),
813+
});
814+
self.mark_init(range, true);
815+
self.provenance.write_wildcards(cx, range);
817816
}
818817

819818
/// 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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
251251

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

0 commit comments

Comments
 (0)