diff --git a/src/alloc_bytes.rs b/src/alloc_bytes.rs new file mode 100644 index 0000000000..7952abdf9f --- /dev/null +++ b/src/alloc_bytes.rs @@ -0,0 +1,109 @@ +use std::alloc; +use std::alloc::Layout; +use std::borrow::Cow; +use std::slice; + +use rustc_middle::mir::interpret::AllocBytes; +use rustc_target::abi::{Align, Size}; + +/// Allocation bytes that explicitly handle the layout of the data they're storing. +/// This is necessary to interface with native code that accesses the program store in Miri. +#[derive(Debug)] +pub struct MiriAllocBytes { + /// Stored layout information about the allocation. + layout: alloc::Layout, + /// Pointer to the allocation contents. + /// Invariant: + /// * If `self.layout.size() == 0`, then `self.ptr` is some suitably aligned pointer + /// without provenance (and no actual memory was allocated). + /// * Otherwise, `self.ptr` points to memory allocated with `self.layout`. + ptr: *mut u8, +} + +impl Clone for MiriAllocBytes { + fn clone(&self) -> Self { + let bytes: Cow<'_, [u8]> = Cow::Borrowed(self); + let align = Align::from_bytes(self.layout.align().try_into().unwrap()).unwrap(); + MiriAllocBytes::from_bytes(bytes, align) + } +} + +impl Drop for MiriAllocBytes { + fn drop(&mut self) { + if self.layout.size() != 0 { + // SAFETY: Invariant, `self.ptr` points to memory allocated with `self.layout`. + unsafe { alloc::dealloc(self.ptr, self.layout) } + } + } +} + +impl std::ops::Deref for MiriAllocBytes { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + // SAFETY: `ptr` is non-null, properly aligned, and valid for reading out `self.layout.size()`-many bytes. + // Note that due to the invariant this is true even if `self.layout.size() == 0`. + unsafe { slice::from_raw_parts(self.ptr, self.layout.size()) } + } +} + +impl std::ops::DerefMut for MiriAllocBytes { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: `ptr` is non-null, properly aligned, and valid for reading out `self.layout.size()`-many bytes. + // Note that due to the invariant this is true even if `self.layout.size() == 0`. + unsafe { slice::from_raw_parts_mut(self.ptr, self.layout.size()) } + } +} + +impl MiriAllocBytes { + /// This method factors out how a `MiriAllocBytes` object is allocated, + /// specifically given an allocation function `alloc_fn`. + /// `alloc_fn` is only used if `size != 0`. + /// Returns `Err(layout)` if the allocation function returns a `ptr` that is `ptr.is_null()`. + fn alloc_with( + size: usize, + align: usize, + alloc_fn: impl FnOnce(Layout) -> *mut u8, + ) -> Result { + let layout = Layout::from_size_align(size, align).unwrap(); + let ptr = if size == 0 { + std::ptr::without_provenance_mut(align) + } else { + let ptr = alloc_fn(layout); + if ptr.is_null() { + return Err(layout); + } + ptr + }; + // SAFETY: All `MiriAllocBytes` invariants are fulfilled. + Ok(Self { ptr, layout }) + } +} + +impl AllocBytes for MiriAllocBytes { + fn from_bytes<'a>(slice: impl Into>, align: Align) -> Self { + let slice = slice.into(); + let size = slice.len(); + let align = align.bytes_usize(); + // SAFETY: `alloc_fn` will only be used if `size != 0`. + let alloc_fn = |layout| unsafe { alloc::alloc(layout) }; + let alloc_bytes = MiriAllocBytes::alloc_with(size, align, alloc_fn) + .unwrap_or_else(|layout| alloc::handle_alloc_error(layout)); + // SAFETY: `alloc_bytes.ptr` and `slice.as_ptr()` are non-null, properly aligned + // and valid for the `size`-many bytes to be copied. + unsafe { alloc_bytes.ptr.copy_from(slice.as_ptr(), size) }; + alloc_bytes + } + + fn zeroed(size: Size, align: Align) -> Option { + let size = size.bytes_usize(); + let align = align.bytes_usize(); + // SAFETY: `alloc_fn` will only be used if `size != 0`. + let alloc_fn = |layout| unsafe { alloc::alloc_zeroed(layout) }; + MiriAllocBytes::alloc_with(size, align, alloc_fn).ok() + } + + fn as_mut_ptr(&mut self) -> *mut u8 { + self.ptr + } +} diff --git a/src/diagnostics.rs b/src/diagnostics.rs index cb5ed27b6c..189c4a20bf 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -459,7 +459,7 @@ pub fn report_error<'tcx, 'mir>( pub fn report_leaks<'mir, 'tcx>( ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, - leaks: Vec<(AllocId, MemoryKind, Allocation>)>, + leaks: Vec<(AllocId, MemoryKind, Allocation, MiriAllocBytes>)>, ) { let mut any_pruned = false; for (id, kind, mut alloc) in leaks { diff --git a/src/lib.rs b/src/lib.rs index 54eb6a3bd6..e4879f2f53 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ #![feature(lint_reasons)] #![feature(trait_upcasting)] #![feature(strict_overflow_ops)] +#![feature(strict_provenance)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, @@ -74,6 +75,7 @@ extern crate rustc_target; extern crate rustc_driver; mod alloc_addresses; +mod alloc_bytes; mod borrow_tracker; mod clock; mod concurrency; @@ -107,6 +109,7 @@ pub use crate::shims::tls::TlsData; pub use crate::shims::EmulateItemResult; pub use crate::alloc_addresses::{EvalContextExt as _, ProvenanceMode}; +pub use crate::alloc_bytes::MiriAllocBytes; pub use crate::borrow_tracker::stacked_borrows::{ EvalContextExt as _, Item, Permission, Stack, Stacks, }; diff --git a/src/machine.rs b/src/machine.rs index 8854b18528..1309bfccb3 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -862,7 +862,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { type Provenance = Provenance; type ProvenanceExtra = ProvenanceExtra; - type Bytes = Box<[u8]>; + type Bytes = MiriAllocBytes; type MemoryMap = MonoHashMap)>; @@ -1088,8 +1088,9 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { id: AllocId, alloc: Cow<'b, Allocation>, kind: Option, - ) -> InterpResult<'tcx, Cow<'b, Allocation>> { - let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None"); + ) -> InterpResult<'tcx, Cow<'b, Allocation>> + { + let kind = kind.expect("we set our STATIC_KIND so this cannot be None"); if ecx.machine.tracked_alloc_ids.contains(&id) { ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc( id, @@ -1126,7 +1127,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { Some(ecx.generate_stacktrace()) }; - let alloc: Allocation = alloc.adjust_from_tcx( + let alloc: Allocation = alloc.adjust_from_tcx( &ecx.tcx, AllocExtra { borrow_tracker, diff --git a/src/provenance_gc.rs b/src/provenance_gc.rs index f23d7dfd52..ecd614bf46 100644 --- a/src/provenance_gc.rs +++ b/src/provenance_gc.rs @@ -122,7 +122,7 @@ impl VisitProvenance for OpTy<'_, Provenance> { } } -impl VisitProvenance for Allocation> { +impl VisitProvenance for Allocation, MiriAllocBytes> { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { for prov in self.provenance().provenances() { prov.visit_provenance(visit);