diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 9dbd5e24ae..5a8f07263f 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -10,6 +10,7 @@ extern crate rustc_session; use std::convert::TryFrom; use std::env; +use std::num::NonZeroU64; use std::path::PathBuf; use std::rc::Rc; use std::str::FromStr; @@ -412,11 +413,16 @@ fn main() { } } arg if arg.starts_with("-Zmiri-track-alloc-id=") => { - let id: u64 = match arg.strip_prefix("-Zmiri-track-alloc-id=").unwrap().parse() + let id = match arg + .strip_prefix("-Zmiri-track-alloc-id=") + .unwrap() + .parse() + .ok() + .and_then(NonZeroU64::new) { - Ok(id) => id, - Err(err) => - panic!("-Zmiri-track-alloc-id requires a valid `u64` argument: {}", err), + Some(id) => id, + None => + panic!("-Zmiri-track-alloc-id requires a valid non-zero `u64` argument"), }; miri_config.tracked_alloc_id = Some(miri::AllocId(id)); } diff --git a/src/data_race.rs b/src/data_race.rs index cb7b1fc6db..6a64c1cb69 100644 --- a/src/data_race.rs +++ b/src/data_race.rs @@ -73,9 +73,9 @@ use rustc_middle::{mir, ty::layout::TyAndLayout}; use rustc_target::abi::Size; use crate::{ - ImmTy, Immediate, InterpResult, MPlaceTy, MemPlaceMeta, MemoryKind, MiriEvalContext, - MiriEvalContextExt, MiriMemoryKind, OpTy, Pointer, RangeMap, Scalar, ScalarMaybeUninit, Tag, - ThreadId, VClock, VTimestamp, VectorIdx, + AllocId, AllocRange, ImmTy, Immediate, InterpResult, MPlaceTy, MemPlaceMeta, MemoryKind, + MiriEvalContext, MiriEvalContextExt, MiriMemoryKind, OpTy, Pointer, RangeMap, Scalar, + ScalarMaybeUninit, Tag, ThreadId, VClock, VTimestamp, VectorIdx, }; pub type AllocExtra = VClockAlloc; @@ -561,7 +561,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { if lt { &rhs } else { &old } }; - this.allow_data_races_mut(|this| this.write_immediate_to_mplace(**new_val, place))?; + this.allow_data_races_mut(|this| this.write_immediate(**new_val, &(*place).into()))?; this.validate_atomic_rmw(&place, atomic)?; @@ -713,18 +713,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { Ok(()) } } - - fn reset_vector_clocks(&mut self, ptr: Pointer, size: Size) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - if let Some(data_race) = &mut this.memory.extra.data_race { - if data_race.multi_threaded.get() { - let alloc_meta = - this.memory.get_alloc_extra_mut(ptr.alloc_id)?.0.data_race.as_mut().unwrap(); - alloc_meta.reset_clocks(ptr.offset, size); - } - } - Ok(()) - } } /// Vector clock metadata for a logical memory allocation. @@ -769,14 +757,6 @@ impl VClockAlloc { } } - fn reset_clocks(&mut self, offset: Size, len: Size) { - let alloc_ranges = self.alloc_ranges.get_mut(); - for (_, range) in alloc_ranges.iter_mut(offset, len) { - // Reset the portion of the range - *range = MemoryCellClocks::new(0, VectorIdx::MAX_INDEX); - } - } - // Find an index, if one exists where the value // in `l` is greater than the value in `r`. fn find_gt_index(l: &VClock, r: &VClock) -> Option { @@ -820,8 +800,7 @@ impl VClockAlloc { range: &MemoryCellClocks, action: &str, is_atomic: bool, - pointer: Pointer, - len: Size, + ptr_dbg: Pointer, ) -> InterpResult<'tcx> { let (current_index, current_clocks) = global.current_thread_state(); let write_clock; @@ -863,15 +842,12 @@ impl VClockAlloc { // Throw the data-race detection. throw_ub_format!( - "Data race detected between {} on {} and {} on {}, memory({:?},offset={},size={})\ - \n(current vector clock = {:?}, conflicting timestamp = {:?})", + "Data race detected between {} on {} and {} on {} at {:?} (current vector clock = {:?}, conflicting timestamp = {:?})", action, current_thread_info, other_action, other_thread_info, - pointer.alloc_id, - pointer.offset.bytes(), - len.bytes(), + ptr_dbg, current_clocks.clock, other_clock ) @@ -884,17 +860,23 @@ impl VClockAlloc { /// atomic read operations. pub fn read<'tcx>( &self, - pointer: Pointer, - len: Size, + alloc_id: AllocId, + range: AllocRange, global: &GlobalState, ) -> InterpResult<'tcx> { if global.multi_threaded.get() { let (index, clocks) = global.current_thread_state(); let mut alloc_ranges = self.alloc_ranges.borrow_mut(); - for (_, range) in alloc_ranges.iter_mut(pointer.offset, len) { + for (offset, range) in alloc_ranges.iter_mut(range.start, range.size) { if let Err(DataRace) = range.read_race_detect(&*clocks, index) { // Report data-race. - return Self::report_data_race(global, range, "Read", false, pointer, len); + return Self::report_data_race( + global, + range, + "Read", + false, + Pointer::new(alloc_id, offset), + ); } } Ok(()) @@ -906,14 +888,14 @@ impl VClockAlloc { // Shared code for detecting data-races on unique access to a section of memory fn unique_access<'tcx>( &mut self, - pointer: Pointer, - len: Size, + alloc_id: AllocId, + range: AllocRange, write_type: WriteType, global: &mut GlobalState, ) -> InterpResult<'tcx> { if global.multi_threaded.get() { let (index, clocks) = global.current_thread_state(); - for (_, range) in self.alloc_ranges.get_mut().iter_mut(pointer.offset, len) { + for (offset, range) in self.alloc_ranges.get_mut().iter_mut(range.start, range.size) { if let Err(DataRace) = range.write_race_detect(&*clocks, index, write_type) { // Report data-race return Self::report_data_race( @@ -921,8 +903,7 @@ impl VClockAlloc { range, write_type.get_descriptor(), false, - pointer, - len, + Pointer::new(alloc_id, offset), ); } } @@ -938,11 +919,11 @@ impl VClockAlloc { /// operation pub fn write<'tcx>( &mut self, - pointer: Pointer, - len: Size, + alloc_id: AllocId, + range: AllocRange, global: &mut GlobalState, ) -> InterpResult<'tcx> { - self.unique_access(pointer, len, WriteType::Write, global) + self.unique_access(alloc_id, range, WriteType::Write, global) } /// Detect data-races for an unsynchronized deallocate operation, will not perform @@ -951,11 +932,11 @@ impl VClockAlloc { /// operation pub fn deallocate<'tcx>( &mut self, - pointer: Pointer, - len: Size, + alloc_id: AllocId, + range: AllocRange, global: &mut GlobalState, ) -> InterpResult<'tcx> { - self.unique_access(pointer, len, WriteType::Deallocate, global) + self.unique_access(alloc_id, range, WriteType::Deallocate, global) } } @@ -1002,12 +983,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { result } - /// Generic atomic operation implementation, - /// this accesses memory via get_raw instead of - /// get_raw_mut, due to issues calling get_raw_mut - /// for atomic loads from read-only memory. - /// FIXME: is this valid, or should get_raw_mut be used for - /// atomic-stores/atomic-rmw? + /// Generic atomic operation implementation fn validate_atomic_op( &self, place: &MPlaceTy<'tcx, Tag>, @@ -1023,25 +999,24 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { let this = self.eval_context_ref(); if let Some(data_race) = &this.memory.extra.data_race { if data_race.multi_threaded.get() { + let size = place.layout.size; + let (alloc_id, base_offset, ptr) = this.memory.ptr_get_alloc(place.ptr)?; // Load and log the atomic operation. // Note that atomic loads are possible even from read-only allocations, so `get_alloc_extra_mut` is not an option. - let place_ptr = place.ptr.assert_ptr(); - let size = place.layout.size; let alloc_meta = - &this.memory.get_alloc_extra(place_ptr.alloc_id)?.data_race.as_ref().unwrap(); + &this.memory.get_alloc_extra(alloc_id)?.data_race.as_ref().unwrap(); log::trace!( - "Atomic op({}) with ordering {:?} on memory({:?}, offset={}, size={})", + "Atomic op({}) with ordering {:?} on {:?} (size={})", description, &atomic, - place_ptr.alloc_id, - place_ptr.offset.bytes(), + ptr, size.bytes() ); // Perform the atomic operation. data_race.maybe_perform_sync_operation(|index, mut clocks| { - for (_, range) in - alloc_meta.alloc_ranges.borrow_mut().iter_mut(place_ptr.offset, size) + for (offset, range) in + alloc_meta.alloc_ranges.borrow_mut().iter_mut(base_offset, size) { if let Err(DataRace) = op(range, &mut *clocks, index, atomic) { mem::drop(clocks); @@ -1050,8 +1025,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { range, description, true, - place_ptr, - size, + Pointer::new(alloc_id, offset), ) .map(|_| true); } @@ -1063,12 +1037,11 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { // Log changes to atomic memory. if log::log_enabled!(log::Level::Trace) { - for (_, range) in alloc_meta.alloc_ranges.borrow().iter(place_ptr.offset, size) + for (_offset, range) in alloc_meta.alloc_ranges.borrow().iter(base_offset, size) { log::trace!( - "Updated atomic memory({:?}, offset={}, size={}) to {:#?}", - place.ptr.assert_ptr().alloc_id, - place_ptr.offset.bytes(), + "Updated atomic memory({:?}, size={}) to {:#?}", + ptr, size.bytes(), range.atomic_ops ); diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 4476ce237f..b5b75a7fc3 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -134,7 +134,7 @@ pub fn report_error<'tcx, 'mir>( let helps = match e.kind() { Unsupported(UnsupportedOpInfo::NoMirFor(..)) => vec![(None, format!("make sure to use a Miri sysroot, which you can prepare with `cargo miri setup`"))], - Unsupported(UnsupportedOpInfo::ReadBytesAsPointer | UnsupportedOpInfo::ThreadLocalStatic(_) | UnsupportedOpInfo::ReadExternStatic(_)) => + Unsupported(UnsupportedOpInfo::ThreadLocalStatic(_) | UnsupportedOpInfo::ReadExternStatic(_)) => panic!("Error should never be raised by Miri: {:?}", e.kind()), Unsupported(_) => vec![(None, format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support"))], diff --git a/src/eval.rs b/src/eval.rs index f728248c3a..ae9ff9c1f5 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -164,7 +164,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( // Third argument (`argv`): created from `config.args`. let argv = { // Put each argument in memory, collect pointers. - let mut argvs = Vec::>::new(); + let mut argvs = Vec::>::new(); for arg in config.args.iter() { // Make space for `0` terminator. let size = u64::try_from(arg.len()).unwrap().checked_add(1).unwrap(); @@ -172,7 +172,8 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( let arg_place = ecx.allocate(ecx.layout_of(arg_type)?, MiriMemoryKind::Machine.into())?; ecx.write_os_str_to_c_str(OsStr::new(arg), arg_place.ptr, size)?; - argvs.push(arg_place.ptr); + ecx.mark_immutable(&*arg_place); + argvs.push(arg_place.to_ref(&ecx)); } // Make an array with all these pointers, in the Miri memory. let argvs_layout = ecx.layout_of( @@ -181,24 +182,26 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into())?; for (idx, arg) in argvs.into_iter().enumerate() { let place = ecx.mplace_field(&argvs_place, idx)?; - ecx.write_scalar(arg, &place.into())?; + ecx.write_immediate(arg, &place.into())?; } - ecx.memory.mark_immutable(argvs_place.ptr.assert_ptr().alloc_id)?; + ecx.mark_immutable(&*argvs_place); // A pointer to that place is the 3rd argument for main. - let argv = argvs_place.ptr; + let argv = argvs_place.to_ref(&ecx); // Store `argc` and `argv` for macOS `_NSGetArg{c,v}`. { let argc_place = ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?; ecx.write_scalar(argc, &argc_place.into())?; - ecx.machine.argc = Some(argc_place.ptr); + ecx.mark_immutable(&*argc_place); + ecx.machine.argc = Some(*argc_place); let argv_place = ecx.allocate( ecx.layout_of(tcx.mk_imm_ptr(tcx.types.unit))?, MiriMemoryKind::Machine.into(), )?; - ecx.write_scalar(argv, &argv_place.into())?; - ecx.machine.argv = Some(argv_place.ptr); + ecx.write_immediate(argv, &argv_place.into())?; + ecx.mark_immutable(&*argv_place); + ecx.machine.argv = Some(*argv_place); } // Store command line as UTF-16 for Windows `GetCommandLineW`. { @@ -217,12 +220,13 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( let cmd_type = tcx.mk_array(tcx.types.u16, u64::try_from(cmd_utf16.len()).unwrap()); let cmd_place = ecx.allocate(ecx.layout_of(cmd_type)?, MiriMemoryKind::Machine.into())?; - ecx.machine.cmd_line = Some(cmd_place.ptr); + ecx.machine.cmd_line = Some(*cmd_place); // Store the UTF-16 string. We just allocated so we know the bounds are fine. for (idx, &c) in cmd_utf16.iter().enumerate() { let place = ecx.mplace_field(&cmd_place, idx)?; ecx.write_scalar(Scalar::from_u16(c), &place.into())?; } + ecx.mark_immutable(&*cmd_place); } argv }; @@ -233,7 +237,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( ecx.call_function( start_instance, Abi::Rust, - &[main_ptr.into(), argc.into(), argv.into()], + &[Scalar::from_pointer(main_ptr, &ecx).into(), argc.into(), argv], Some(&ret_place.into()), StackPopCleanup::None { cleanup: true }, )?; @@ -285,8 +289,7 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> } ecx.process_diagnostics(info); } - let return_code = - ecx.read_scalar(&ret_place.into())?.check_init()?.to_machine_isize(&ecx)?; + let return_code = ecx.read_scalar(&ret_place.into())?.to_machine_isize(&ecx)?; Ok(return_code) })(); diff --git a/src/helpers.rs b/src/helpers.rs index 057684562f..363aefa62d 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -53,18 +53,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Evaluates the scalar at the specified path. Returns Some(val) /// if the path could be resolved, and None otherwise - fn eval_path_scalar(&mut self, path: &[&str]) -> InterpResult<'tcx, ScalarMaybeUninit> { + fn eval_path_scalar(&mut self, path: &[&str]) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let instance = this.resolve_path(path); let cid = GlobalId { instance, promoted: None }; let const_val = this.eval_to_allocation(cid)?; let const_val = this.read_scalar(&const_val.into())?; - return Ok(const_val); + return Ok(const_val.check_init()?); } /// Helper function to get a `libc` constant as a `Scalar`. fn eval_libc(&mut self, name: &str) -> InterpResult<'tcx, Scalar> { - self.eval_context_mut().eval_path_scalar(&["libc", name])?.check_init() + self.eval_context_mut().eval_path_scalar(&["libc", name]) } /// Helper function to get a `libc` constant as an `i32`. @@ -75,9 +75,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Helper function to get a `windows` constant as a `Scalar`. fn eval_windows(&mut self, module: &str, name: &str) -> InterpResult<'tcx, Scalar> { - self.eval_context_mut() - .eval_path_scalar(&["std", "sys", "windows", module, name])? - .check_init() + self.eval_context_mut().eval_path_scalar(&["std", "sys", "windows", module, name]) } /// Helper function to get a `windows` constant as an `u64`. @@ -107,17 +105,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx self.eval_context_mut().write_scalar(Scalar::from_int(0, dest.layout.size), dest) } - /// Test if this immediate equals 0. - fn is_null(&self, val: Scalar) -> InterpResult<'tcx, bool> { + /// Test if this pointer equals 0. + fn ptr_is_null(&self, ptr: Pointer>) -> InterpResult<'tcx, bool> { let this = self.eval_context_ref(); let null = Scalar::null_ptr(this); - this.ptr_eq(val, null) - } - - /// Turn a Scalar into an Option - fn test_null(&self, val: Scalar) -> InterpResult<'tcx, Option>> { - let this = self.eval_context_ref(); - Ok(if this.is_null(val)? { None } else { Some(val) }) + this.ptr_eq(Scalar::from_maybe_pointer(ptr, this), null) } /// Get the `Place` for a local @@ -128,7 +120,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } /// Generate some random bytes, and write them to `dest`. - fn gen_random(&mut self, ptr: Scalar, len: u64) -> InterpResult<'tcx> { + fn gen_random(&mut self, ptr: Pointer>, len: u64) -> InterpResult<'tcx> { // Some programs pass in a null pointer and a length of 0 // to their platform's random-generation function (e.g. getrandom()) // on Linux. For compatibility with these programs, we don't perform @@ -195,13 +187,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(()) } - /// Visits the memory covered by `place`, sensitive to freezing: the 3rd parameter - /// will be true if this is frozen, false if this is in an `UnsafeCell`. + /// Visits the memory covered by `place`, sensitive to freezing: the 2nd parameter + /// of `action` will be true if this is frozen, false if this is in an `UnsafeCell`. + /// The range is relative to `place`. + /// + /// Assumes that the `place` has a proper pointer in it. fn visit_freeze_sensitive( &self, place: &MPlaceTy<'tcx, Tag>, size: Size, - mut action: impl FnMut(Pointer, Size, bool) -> InterpResult<'tcx>, + mut action: impl FnMut(AllocRange, bool) -> InterpResult<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_ref(); trace!("visit_frozen(place={:?}, size={:?})", *place, size); @@ -214,29 +209,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Store how far we proceeded into the place so far. Everything to the left of // this offset has already been handled, in the sense that the frozen parts // have had `action` called on them. - let mut end_ptr = place.ptr.assert_ptr(); + let ptr = place.ptr.into_pointer_or_addr().unwrap(); + let start_offset = ptr.into_parts().1 as Size; // we just compare offsets, the abs. value never matters + let mut cur_offset = start_offset; // Called when we detected an `UnsafeCell` at the given offset and size. - // Calls `action` and advances `end_ptr`. - let mut unsafe_cell_action = |unsafe_cell_ptr: Scalar, unsafe_cell_size: Size| { - let unsafe_cell_ptr = unsafe_cell_ptr.assert_ptr(); - debug_assert_eq!(unsafe_cell_ptr.alloc_id, end_ptr.alloc_id); - debug_assert_eq!(unsafe_cell_ptr.tag, end_ptr.tag); + // Calls `action` and advances `cur_ptr`. + let mut unsafe_cell_action = |unsafe_cell_ptr: Pointer>, + unsafe_cell_size: Size| { + let unsafe_cell_ptr = unsafe_cell_ptr.into_pointer_or_addr().unwrap(); + debug_assert_eq!(unsafe_cell_ptr.provenance, ptr.provenance); // We assume that we are given the fields in increasing offset order, // and nothing else changes. - let unsafe_cell_offset = unsafe_cell_ptr.offset; - let end_offset = end_ptr.offset; - assert!(unsafe_cell_offset >= end_offset); - let frozen_size = unsafe_cell_offset - end_offset; - // Everything between the end_ptr and this `UnsafeCell` is frozen. + let unsafe_cell_offset = unsafe_cell_ptr.into_parts().1 as Size; // we just compare offsets, the abs. value never matters + assert!(unsafe_cell_offset >= cur_offset); + let frozen_size = unsafe_cell_offset - cur_offset; + // Everything between the cur_ptr and this `UnsafeCell` is frozen. if frozen_size != Size::ZERO { - action(end_ptr, frozen_size, /*frozen*/ true)?; + action(alloc_range(cur_offset - start_offset, frozen_size), /*frozen*/ true)?; } + cur_offset += frozen_size; // This `UnsafeCell` is NOT frozen. if unsafe_cell_size != Size::ZERO { - action(unsafe_cell_ptr, unsafe_cell_size, /*frozen*/ false)?; + action( + alloc_range(cur_offset - start_offset, unsafe_cell_size), + /*frozen*/ false, + )?; } - // Update end end_ptr. - end_ptr = unsafe_cell_ptr.wrapping_offset(unsafe_cell_size, this); + cur_offset += unsafe_cell_size; // Done Ok(()) }; @@ -264,7 +263,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } // The part between the end_ptr and the end of the place is also frozen. // So pretend there is a 0-sized `UnsafeCell` at the end. - unsafe_cell_action(place.ptr.ptr_wrapping_offset(size, this), Size::ZERO)?; + unsafe_cell_action(place.ptr.wrapping_offset(size, this), Size::ZERO)?; // Done! return Ok(()); @@ -347,7 +346,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Gather the subplaces and sort them before visiting. let mut places = fields.collect::>>>()?; - places.sort_by_key(|place| place.ptr.assert_ptr().offset); + // we just compare offsets, the abs. value never matters + places.sort_by_key(|place| { + place.ptr.into_pointer_or_addr().unwrap().into_parts().1 as Size + }); self.walk_aggregate(place, places.into_iter().map(Ok)) } FieldsShape::Union { .. } | FieldsShape::Primitive => { @@ -379,9 +381,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let mut offset = Size::from_bytes(0); for &imm in imms { - this.write_immediate_to_mplace( + this.write_immediate( *imm, - &place.offset(offset, MemPlaceMeta::None, imm.layout, &*this.tcx)?, + &place.offset(offset, MemPlaceMeta::None, imm.layout, &*this.tcx)?.into(), )?; offset += imm.layout.size; } @@ -567,12 +569,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Parse a `timespec` struct and return it as a `std::time::Duration`. It returns `None` /// if the value in the `timespec` struct is invalid. Some libc functions will return /// `EINVAL` in this case. - fn read_timespec( - &mut self, - timespec_ptr_op: &OpTy<'tcx, Tag>, - ) -> InterpResult<'tcx, Option> { + fn read_timespec(&mut self, tp: &MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); - let tp = this.deref_operand(timespec_ptr_op)?; let seconds_place = this.mplace_field(&tp, 0)?; let seconds_scalar = this.read_scalar(&seconds_place.into())?; let seconds = seconds_scalar.to_machine_isize(this)?; @@ -593,14 +591,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx }) } - fn read_c_str<'a>(&'a self, sptr: Scalar) -> InterpResult<'tcx, &'a [u8]> + fn read_c_str<'a>(&'a self, ptr: Pointer>) -> InterpResult<'tcx, &'a [u8]> where 'tcx: 'a, 'mir: 'a, { let this = self.eval_context_ref(); let size1 = Size::from_bytes(1); - let ptr = this.force_ptr(sptr)?; // We need to read at least 1 byte, so we can eagerly get a ptr. // Step 1: determine the length. let mut len = Size::ZERO; @@ -620,12 +617,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.memory.read_bytes(ptr.into(), len) } - fn read_wide_str(&self, sptr: Scalar) -> InterpResult<'tcx, Vec> { + fn read_wide_str(&self, mut ptr: Pointer>) -> InterpResult<'tcx, Vec> { let this = self.eval_context_ref(); let size2 = Size::from_bytes(2); let align2 = Align::from_bytes(2).unwrap(); - let mut ptr = this.force_ptr(sptr)?; // We need to read at least 1 wchar, so we can eagerly get a ptr. let mut wchars = Vec::new(); loop { // FIXME: We are re-getting the allocation each time around the loop. @@ -709,6 +705,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?; check_arg_count(args) } + + /// Mark a machine allocation that was just created as immutable. + fn mark_immutable(&mut self, mplace: &MemPlace) { + let this = self.eval_context_mut(); + this.memory + .mark_immutable(mplace.ptr.into_pointer_or_addr().unwrap().provenance.alloc_id) + .unwrap(); + } } /// Check that the number of args is what we expect. diff --git a/src/intptrcast.rs b/src/intptrcast.rs index b5a77b08ff..665a134184 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -38,55 +38,55 @@ impl Default for GlobalState { } impl<'mir, 'tcx> GlobalState { - pub fn int_to_ptr( - int: u64, + pub fn ptr_from_addr( + addr: u64, memory: &Memory<'mir, 'tcx, Evaluator<'mir, 'tcx>>, - ) -> InterpResult<'tcx, Pointer> { + ) -> Pointer> { + trace!("Casting 0x{:x} to a pointer", addr); let global_state = memory.extra.intptrcast.borrow(); - let pos = global_state.int_to_ptr_map.binary_search_by_key(&int, |(addr, _)| *addr); - - // The int must be in-bounds after being cast to a pointer, so we error - // with `CheckInAllocMsg::InboundsTest`. - Ok(match pos { - Ok(pos) => { - let (_, alloc_id) = global_state.int_to_ptr_map[pos]; - // `int` is equal to the starting address for an allocation, the offset should be - // zero. The pointer is untagged because it was created from a cast - Pointer::new_with_tag(alloc_id, Size::from_bytes(0), Tag::Untagged) - } - Err(0) => throw_ub!(DanglingIntPointer(int, CheckInAllocMsg::InboundsTest)), + let pos = global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr); + + let alloc_id = match pos { + Ok(pos) => Some(global_state.int_to_ptr_map[pos].1), + Err(0) => None, Err(pos) => { // This is the largest of the adresses smaller than `int`, // i.e. the greatest lower bound (glb) let (glb, alloc_id) = global_state.int_to_ptr_map[pos - 1]; - // This never overflows because `int >= glb` - let offset = int - glb; - // If the offset exceeds the size of the allocation, this access is illegal - if offset <= memory.get_size_and_align(alloc_id, AllocCheck::MaybeDead)?.0.bytes() { - // This pointer is untagged because it was created from a cast - Pointer::new_with_tag(alloc_id, Size::from_bytes(offset), Tag::Untagged) + // This never overflows because `addr >= glb` + let offset = addr - glb; + // If the offset exceeds the size of the allocation, don't use this `alloc_id`. + if offset + <= memory.get_size_and_align(alloc_id, AllocCheck::MaybeDead).unwrap().0.bytes() + { + Some(alloc_id) } else { - throw_ub!(DanglingIntPointer(int, CheckInAllocMsg::InboundsTest)) + None } } - }) + }; + // Pointers created from integers are untagged. + Pointer::new( + alloc_id.map(|alloc_id| Tag { alloc_id, sb: SbTag::Untagged }), + Size::from_bytes(addr), + ) } - pub fn ptr_to_int( - ptr: Pointer, + fn alloc_base_addr( memory: &Memory<'mir, 'tcx, Evaluator<'mir, 'tcx>>, - ) -> InterpResult<'tcx, u64> { + alloc_id: AllocId, + ) -> u64 { let mut global_state = memory.extra.intptrcast.borrow_mut(); let global_state = &mut *global_state; - let id = ptr.alloc_id; - - // There is nothing wrong with a raw pointer being cast to an integer only after - // it became dangling. Hence `MaybeDead`. - let (size, align) = memory.get_size_and_align(id, AllocCheck::MaybeDead)?; - let base_addr = match global_state.base_addr.entry(id) { + match global_state.base_addr.entry(alloc_id) { Entry::Occupied(entry) => *entry.get(), Entry::Vacant(entry) => { + // There is nothing wrong with a raw pointer being cast to an integer only after + // it became dangling. Hence `MaybeDead`. + let (size, align) = + memory.get_size_and_align(alloc_id, AllocCheck::MaybeDead).unwrap(); + // This allocation does not have a base address yet, pick one. // Leave some space to the previous allocation, to give it some chance to be less aligned. let slack = { @@ -99,11 +99,12 @@ impl<'mir, 'tcx> GlobalState { let base_addr = Self::align_addr(base_addr, align.bytes()); entry.insert(base_addr); trace!( - "Assigning base address {:#x} to allocation {:?} (slack: {}, align: {})", + "Assigning base address {:#x} to allocation {:?} (size: {}, align: {}, slack: {})", base_addr, - id, - slack, + alloc_id, + size.bytes(), align.bytes(), + slack, ); // Remember next base address. If this allocation is zero-sized, leave a gap @@ -111,17 +112,37 @@ impl<'mir, 'tcx> GlobalState { global_state.next_base_addr = base_addr.checked_add(max(size.bytes(), 1)).unwrap(); // Given that `next_base_addr` increases in each allocation, pushing the // corresponding tuple keeps `int_to_ptr_map` sorted - global_state.int_to_ptr_map.push((base_addr, id)); + global_state.int_to_ptr_map.push((base_addr, alloc_id)); base_addr } - }; + } + } + + /// Convert a relative (tcx) pointer to an absolute address. + pub fn rel_ptr_to_addr( + memory: &Memory<'mir, 'tcx, Evaluator<'mir, 'tcx>>, + ptr: Pointer, + ) -> u64 { + let (alloc_id, offset) = ptr.into_parts(); // offset is relative + let base_addr = GlobalState::alloc_base_addr(memory, alloc_id); - // Sanity check that the base address is aligned. - debug_assert_eq!(base_addr % align.bytes(), 0); // Add offset with the right kind of pointer-overflowing arithmetic. let dl = memory.data_layout(); - Ok(dl.overflowing_offset(base_addr, ptr.offset.bytes()).0) + dl.overflowing_offset(base_addr, offset.bytes()).0 + } + + pub fn abs_ptr_to_rel( + memory: &Memory<'mir, 'tcx, Evaluator<'mir, 'tcx>>, + ptr: Pointer, + ) -> Size { + let (tag, addr) = ptr.into_parts(); // addr is absolute + let base_addr = GlobalState::alloc_base_addr(memory, tag.alloc_id); + + // Wrapping "addr - base_addr" + let dl = memory.data_layout(); + let neg_base_addr = (base_addr as i64).wrapping_neg(); + Size::from_bytes(dl.overflowing_signed_offset(addr.bytes(), neg_base_addr).0) } /// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple diff --git a/src/lib.rs b/src/lib.rs index 8c0a19b6df..f8d8aacce3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,13 +65,14 @@ pub use crate::eval::{ pub use crate::helpers::EvalContextExt as HelpersEvalContextExt; pub use crate::machine::{ AllocExtra, Evaluator, FrameData, MemoryExtra, MiriEvalContext, MiriEvalContextExt, - MiriMemoryKind, NUM_CPUS, PAGE_SIZE, STACK_ADDR, STACK_SIZE, + MiriMemoryKind, Tag, NUM_CPUS, PAGE_SIZE, STACK_ADDR, STACK_SIZE, }; pub use crate::mono_hash_map::MonoHashMap; pub use crate::operator::EvalContextExt as OperatorEvalContextExt; pub use crate::range_map::RangeMap; pub use crate::stacked_borrows::{ - CallId, EvalContextExt as StackedBorEvalContextExt, Item, Permission, PtrId, Stack, Stacks, Tag, + CallId, EvalContextExt as StackedBorEvalContextExt, Item, Permission, PtrId, SbTag, Stack, + Stacks, }; pub use crate::sync::{CondvarId, EvalContextExt as SyncEvalContextExt, MutexId, RwLockId}; pub use crate::thread::{ diff --git a/src/machine.rs b/src/machine.rs index 999e21796d..90e3d06aba 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -119,6 +119,35 @@ impl fmt::Display for MiriMemoryKind { } } +/// Pointer provenance (tag). +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Tag { + pub alloc_id: AllocId, + // Stacked Borrows tag. + pub sb: SbTag, +} + +impl Provenance for Tag { + const OFFSET_IS_ADDR: bool = true; + + fn fmt(ptr: &Pointer, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let (tag, addr) = ptr.into_parts(); // address is absolute + write!(f, "0x{:x}", addr.bytes())?; + // Forward `alternate` flag to `alloc_id` printing. + if f.alternate() { + write!(f, "[{:#?}]", tag.alloc_id)?; + } else { + write!(f, "[{:?}]", tag.alloc_id)?; + } + // Print Stacked Borrows tag. + write!(f, "{:?}", tag.sb) + } + + fn get_alloc_id(self) -> AllocId { + self.alloc_id + } +} + /// Extra per-allocation data #[derive(Debug, Clone)] pub struct AllocExtra { @@ -136,8 +165,8 @@ pub struct MemoryExtra { pub data_race: Option, pub intptrcast: intptrcast::MemoryExtra, - /// Mapping extern static names to their canonical allocation. - extern_statics: FxHashMap, + /// Mapping extern static names to their base pointer. + extern_statics: FxHashMap>, /// The random number generator used for resolving non-determinism. /// Needs to be queried by ptr_to_int, hence needs interior mutability. @@ -183,11 +212,10 @@ impl MemoryExtra { fn add_extern_static<'tcx, 'mir>( this: &mut MiriEvalContext<'mir, 'tcx>, name: &str, - ptr: Scalar, + ptr: Pointer>, ) { - let ptr = ptr.assert_ptr(); - assert_eq!(ptr.offset, Size::ZERO); - this.memory.extra.extern_statics.try_insert(Symbol::intern(name), ptr.alloc_id).unwrap(); + let ptr = ptr.into_pointer_or_addr().unwrap(); + this.memory.extra.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap(); } /// Sets up the "extern statics" for this machine. @@ -257,9 +285,9 @@ pub struct Evaluator<'mir, 'tcx> { /// Program arguments (`Option` because we can only initialize them after creating the ecx). /// These are *pointers* to argc/argv because macOS. /// We also need the full command line as one string because of Windows. - pub(crate) argc: Option>, - pub(crate) argv: Option>, - pub(crate) cmd_line: Option>, + pub(crate) argc: Option>, + pub(crate) argv: Option>, + pub(crate) cmd_line: Option>, /// TLS state. pub(crate) tls: TlsData<'tcx>, @@ -487,82 +515,107 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { Ok(()) } - fn thread_local_static_alloc_id( + fn thread_local_static_base_pointer( ecx: &mut InterpCx<'mir, 'tcx, Self>, def_id: DefId, - ) -> InterpResult<'tcx, AllocId> { - ecx.get_or_create_thread_local_alloc_id(def_id) + ) -> InterpResult<'tcx, Pointer> { + ecx.get_or_create_thread_local_alloc(def_id) } - fn extern_static_alloc_id( + fn extern_static_base_pointer( memory: &Memory<'mir, 'tcx, Self>, def_id: DefId, - ) -> InterpResult<'tcx, AllocId> { + ) -> InterpResult<'tcx, Pointer> { let attrs = memory.tcx.get_attrs(def_id); let link_name = match memory.tcx.sess.first_attr_value_str_by_name(&attrs, sym::link_name) { Some(name) => name, None => memory.tcx.item_name(def_id), }; - if let Some(&id) = memory.extra.extern_statics.get(&link_name) { - Ok(id) + if let Some(&ptr) = memory.extra.extern_statics.get(&link_name) { + Ok(ptr) } else { throw_unsup_format!("`extern` static {:?} is not supported by Miri", def_id) } } fn init_allocation_extra<'b>( - memory_extra: &MemoryExtra, + mem: &Memory<'mir, 'tcx, Self>, id: AllocId, alloc: Cow<'b, Allocation>, kind: Option>, - ) -> (Cow<'b, Allocation>, Self::PointerTag) { - if Some(id) == memory_extra.tracked_alloc_id { + ) -> Cow<'b, Allocation> { + if Some(id) == mem.extra.tracked_alloc_id { register_diagnostic(NonHaltingDiagnostic::CreatedAlloc(id)); } let kind = kind.expect("we set our STATIC_KIND so this cannot be None"); let alloc = alloc.into_owned(); - let (stacks, base_tag) = if let Some(stacked_borrows) = &memory_extra.stacked_borrows { - let (stacks, base_tag) = - Stacks::new_allocation(id, alloc.size(), stacked_borrows, kind); - (Some(stacks), base_tag) + let stacks = if let Some(stacked_borrows) = &mem.extra.stacked_borrows { + Some(Stacks::new_allocation(id, alloc.size(), stacked_borrows, kind)) } else { - // No stacks, no tag. - (None, Tag::Untagged) + None }; - let race_alloc = if let Some(data_race) = &memory_extra.data_race { + let race_alloc = if let Some(data_race) = &mem.extra.data_race { Some(data_race::AllocExtra::new_allocation(&data_race, alloc.size(), kind)) } else { None }; - let mut stacked_borrows = memory_extra.stacked_borrows.as_ref().map(|sb| sb.borrow_mut()); - let alloc: Allocation = alloc.with_tags_and_extra( - |alloc| { - if let Some(stacked_borrows) = &mut stacked_borrows { - // Only globals may already contain pointers at this point - assert_eq!(kind, MiriMemoryKind::Global.into()); - stacked_borrows.global_base_ptr(alloc) - } else { - Tag::Untagged - } - }, + let alloc: Allocation = alloc.convert_tag_add_extra( + &mem.tcx, AllocExtra { stacked_borrows: stacks, data_race: race_alloc }, + |ptr| Evaluator::tag_alloc_base_pointer(mem, ptr), ); - (Cow::Owned(alloc), base_tag) + Cow::Owned(alloc) + } + + fn tag_alloc_base_pointer( + mem: &Memory<'mir, 'tcx, Self>, + ptr: Pointer, + ) -> Pointer { + let absolute_addr = intptrcast::GlobalState::rel_ptr_to_addr(&mem, ptr); + let sb_tag = if let Some(stacked_borrows) = &mem.extra.stacked_borrows { + stacked_borrows.borrow_mut().base_tag(ptr.provenance) + } else { + SbTag::Untagged + }; + Pointer::new(Tag { alloc_id: ptr.provenance, sb: sb_tag }, Size::from_bytes(absolute_addr)) + } + + #[inline(always)] + fn ptr_from_addr( + mem: &Memory<'mir, 'tcx, Self>, + addr: u64, + ) -> Pointer> { + intptrcast::GlobalState::ptr_from_addr(addr, mem) + } + + /// Convert a pointer with provenance into an allocation-offset pair, + /// or a `None` with an absolute address if that conversion is not possible. + fn ptr_get_alloc( + mem: &Memory<'mir, 'tcx, Self>, + ptr: Pointer, + ) -> (AllocId, Size) { + let rel = intptrcast::GlobalState::abs_ptr_to_rel(mem, ptr); + (ptr.provenance.alloc_id, rel) } #[inline(always)] fn memory_read( memory_extra: &Self::MemoryExtra, alloc_extra: &AllocExtra, - ptr: Pointer, - size: Size, + tag: Tag, + range: AllocRange, ) -> InterpResult<'tcx> { if let Some(data_race) = &alloc_extra.data_race { - data_race.read(ptr, size, memory_extra.data_race.as_ref().unwrap())?; + data_race.read(tag.alloc_id, range, memory_extra.data_race.as_ref().unwrap())?; } if let Some(stacked_borrows) = &alloc_extra.stacked_borrows { - stacked_borrows.memory_read(ptr, size, memory_extra.stacked_borrows.as_ref().unwrap()) + stacked_borrows.memory_read( + tag.alloc_id, + tag.sb, + range, + memory_extra.stacked_borrows.as_ref().unwrap(), + ) } else { Ok(()) } @@ -572,16 +625,17 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { fn memory_written( memory_extra: &mut Self::MemoryExtra, alloc_extra: &mut AllocExtra, - ptr: Pointer, - size: Size, + tag: Tag, + range: AllocRange, ) -> InterpResult<'tcx> { if let Some(data_race) = &mut alloc_extra.data_race { - data_race.write(ptr, size, memory_extra.data_race.as_mut().unwrap())?; + data_race.write(tag.alloc_id, range, memory_extra.data_race.as_mut().unwrap())?; } if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows { stacked_borrows.memory_written( - ptr, - size, + tag.alloc_id, + tag.sb, + range, memory_extra.stacked_borrows.as_mut().unwrap(), ) } else { @@ -593,19 +647,20 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { fn memory_deallocated( memory_extra: &mut Self::MemoryExtra, alloc_extra: &mut AllocExtra, - ptr: Pointer, - size: Size, + tag: Tag, + range: AllocRange, ) -> InterpResult<'tcx> { - if Some(ptr.alloc_id) == memory_extra.tracked_alloc_id { - register_diagnostic(NonHaltingDiagnostic::FreedAlloc(ptr.alloc_id)); + if Some(tag.alloc_id) == memory_extra.tracked_alloc_id { + register_diagnostic(NonHaltingDiagnostic::FreedAlloc(tag.alloc_id)); } if let Some(data_race) = &mut alloc_extra.data_race { - data_race.deallocate(ptr, size, memory_extra.data_race.as_mut().unwrap())?; + data_race.deallocate(tag.alloc_id, range, memory_extra.data_race.as_mut().unwrap())?; } if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows { stacked_borrows.memory_deallocated( - ptr, - size, + tag.alloc_id, + tag.sb, + range, memory_extra.stacked_borrows.as_mut().unwrap(), ) } else { @@ -613,26 +668,6 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { } } - fn after_static_mem_initialized( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - ptr: Pointer, - size: Size, - ) -> InterpResult<'tcx> { - if ecx.memory.extra.data_race.is_some() { - ecx.reset_vector_clocks(ptr, size)?; - } - Ok(()) - } - - #[inline(always)] - fn tag_global_base_pointer(memory_extra: &MemoryExtra, id: AllocId) -> Self::PointerTag { - if let Some(stacked_borrows) = &memory_extra.stacked_borrows { - stacked_borrows.borrow_mut().global_base_ptr(id) - } else { - Tag::Untagged - } - } - #[inline(always)] fn retag( ecx: &mut InterpCx<'mir, 'tcx, Self>, @@ -701,20 +736,4 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { } res } - - #[inline(always)] - fn int_to_ptr( - memory: &Memory<'mir, 'tcx, Self>, - int: u64, - ) -> InterpResult<'tcx, Pointer> { - intptrcast::GlobalState::int_to_ptr(int, memory) - } - - #[inline(always)] - fn ptr_to_int( - memory: &Memory<'mir, 'tcx, Self>, - ptr: Pointer, - ) -> InterpResult<'tcx, u64> { - intptrcast::GlobalState::ptr_to_int(ptr, memory) - } } diff --git a/src/mono_hash_map.rs b/src/mono_hash_map.rs index fb0169920e..1ae2083d56 100644 --- a/src/mono_hash_map.rs +++ b/src/mono_hash_map.rs @@ -72,15 +72,31 @@ impl AllocMap for MonoHashMap { /// returns owned data, that is put into the map and returned. #[inline(always)] fn get_or(&self, k: K, vacant: impl FnOnce() -> Result) -> Result<&V, E> { - let val: *const V = match self.0.borrow_mut().entry(k) { - Entry::Occupied(entry) => &**entry.get(), - Entry::Vacant(entry) => &**entry.insert(Box::new(vacant()?)), - }; + // We cannot hold borrow_mut while calling `vacant`, since that might have to do lookups in this very map. + if let Some(v) = self.0.borrow().get(&k) { + let val: *const V = &**v; + // This is safe because `val` points into a `Box`, that we know will not move and + // will also not be dropped as long as the shared reference `self` is live. + return unsafe { Ok(&*val) }; + } + let new_val = Box::new(vacant()?); + let val: *const V = &**self.0.borrow_mut().try_insert(k, new_val).ok().unwrap(); // This is safe because `val` points into a `Box`, that we know will not move and // will also not be dropped as long as the shared reference `self` is live. unsafe { Ok(&*val) } } + /// Read-only lookup (avoid read-acquiring the RefCell). + fn get(&self, k: K) -> Option<&V> { + let val: *const V = match self.0.borrow().get(&k) { + Some(v) => &**v, + None => return None, + }; + // This is safe because `val` points into a `Box`, that we know will not move and + // will also not be dropped as long as the shared reference `self` is live. + unsafe { Some(&*val) } + } + #[inline(always)] fn get_mut_or(&mut self, k: K, vacant: impl FnOnce() -> Result) -> Result<&mut V, E> { match self.0.get_mut().entry(k) { diff --git a/src/operator.rs b/src/operator.rs index cf92aed9cc..59099469a3 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -45,9 +45,8 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> { Lt | Le | Gt | Ge => { // Just compare the integers. - // TODO: Do we really want to *always* do that, even when comparing two live in-bounds pointers? - let left = self.force_bits(left.to_scalar()?, left.layout.size)?; - let right = self.force_bits(right.to_scalar()?, right.layout.size)?; + let left = left.to_scalar()?.to_bits(left.layout.size)?; + let right = right.to_scalar()?.to_bits(right.layout.size)?; let res = match bin_op { Lt => left < right, Le => left <= right, @@ -62,11 +61,11 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> { let pointee_ty = left.layout.ty.builtin_deref(true).expect("Offset called on non-ptr type").ty; let ptr = self.ptr_offset_inbounds( - left.to_scalar()?, + self.scalar_to_ptr(left.to_scalar()?), pointee_ty, right.to_scalar()?.to_machine_isize(self)?, )?; - (ptr, false, left.layout.ty) + (Scalar::from_maybe_pointer(ptr, self), false, left.layout.ty) } _ => bug!("Invalid operator on pointers: {:?}", bin_op), @@ -76,9 +75,8 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> { fn ptr_eq(&self, left: Scalar, right: Scalar) -> InterpResult<'tcx, bool> { let size = self.pointer_size(); // Just compare the integers. - // TODO: Do we really want to *always* do that, even when comparing two live in-bounds pointers? - let left = self.force_bits(left, size)?; - let right = self.force_bits(right, size)?; + let left = left.to_bits(size)?; + let right = right.to_bits(size)?; Ok(left == right) } } diff --git a/src/shims/backtrace.rs b/src/shims/backtrace.rs index ec29fef636..1ac3a22f7e 100644 --- a/src/shims/backtrace.rs +++ b/src/shims/backtrace.rs @@ -44,9 +44,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // to reconstruct the needed frame information in `handle_miri_resolve_frame`. // Note that we never actually read or write anything from/to this pointer - // all of the data is represented by the pointer value itself. - let mut fn_ptr = this.memory.create_fn_alloc(FnVal::Instance(instance)); - fn_ptr.offset = Size::from_bytes(pos.0); - Scalar::Ptr(fn_ptr) + let fn_ptr = this.memory.create_fn_alloc(FnVal::Instance(instance)); + fn_ptr.wrapping_offset(Size::from_bytes(pos.0), this) }) .collect(); @@ -61,11 +60,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.allocate(this.layout_of(array_ty).unwrap(), MiriMemoryKind::Rust.into())?; for (i, ptr) in ptrs.into_iter().enumerate() { let place = this.mplace_index(&alloc, i as u64)?; - this.write_immediate_to_mplace(ptr.into(), &place)?; + this.write_pointer(ptr, &place.into())?; } this.write_immediate( - Immediate::new_slice(alloc.ptr.into(), len.try_into().unwrap(), this), + Immediate::new_slice( + Scalar::from_maybe_pointer(alloc.ptr, this), + len.try_into().unwrap(), + this, + ), dest, )?; Ok(()) @@ -87,21 +90,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx throw_unsup_format!("unknown `miri_resolve_frame` flags {}", flags); } - let ptr = this.force_ptr(this.read_scalar(ptr)?.check_init()?)?; + let ptr = this.read_pointer(ptr)?; + // Take apart the pointer, we need its pieces. + let (alloc_id, offset, ptr) = this.memory.ptr_get_alloc(ptr)?; - let fn_instance = if let Some(GlobalAlloc::Function(instance)) = - this.tcx.get_global_alloc(ptr.alloc_id) - { - instance - } else { - throw_ub_format!("expected function pointer, found {:?}", ptr); - }; + let fn_instance = + if let Some(GlobalAlloc::Function(instance)) = this.tcx.get_global_alloc(alloc_id) { + instance + } else { + throw_ub_format!("expected function pointer, found {:?}", ptr); + }; // Reconstruct the original function pointer, // which we pass to user code. - let mut fn_ptr = ptr; - fn_ptr.offset = Size::from_bytes(0); - let fn_ptr = Scalar::Ptr(fn_ptr); + let fn_ptr = this.memory.create_fn_alloc(FnVal::Instance(fn_instance)); let num_fields = dest.layout.layout.fields.count(); @@ -113,7 +115,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ); } - let pos = BytePos(ptr.offset.bytes().try_into().unwrap()); + let pos = BytePos(offset.bytes().try_into().unwrap()); let name = fn_instance.to_string(); let lo = tcx.sess.source_map().lookup_char_pos(pos); @@ -139,15 +141,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - this.write_immediate(name_alloc.to_ref(), &this.mplace_field(&dest, 0)?.into())?; - this.write_immediate(filename_alloc.to_ref(), &this.mplace_field(&dest, 1)?.into())?; + this.write_immediate(name_alloc.to_ref(this), &this.mplace_field(&dest, 0)?.into())?; + this.write_immediate(filename_alloc.to_ref(this), &this.mplace_field(&dest, 1)?.into())?; this.write_scalar(lineno_alloc, &this.mplace_field(&dest, 2)?.into())?; this.write_scalar(colno_alloc, &this.mplace_field(&dest, 3)?.into())?; // Support a 4-field struct for now - this is deprecated // and slated for removal. if num_fields == 5 { - this.write_scalar(fn_ptr, &this.mplace_field(&dest, 4)?.into())?; + this.write_pointer(fn_ptr, &this.mplace_field(&dest, 4)?.into())?; } Ok(()) diff --git a/src/shims/env.rs b/src/shims/env.rs index 59322b91d6..ddd2b61588 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -28,7 +28,7 @@ fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 { pub struct EnvVars<'tcx> { /// Stores pointers to the environment variables. These variables must be stored as /// null-terminated target strings (c_str or wide_str) with the `"{name}={value}"` format. - map: FxHashMap>, + map: FxHashMap>>, /// Place where the `environ` static is stored. Lazily initialized, but then never changes. pub(crate) environ: Option>, @@ -75,8 +75,8 @@ impl<'tcx> EnvVars<'tcx> { } // Deallocate environ var list. let environ = ecx.machine.env_vars.environ.unwrap(); - let old_vars_ptr = ecx.read_scalar(&environ.into())?.check_init()?; - ecx.memory.deallocate(ecx.force_ptr(old_vars_ptr)?, None, MiriMemoryKind::Env.into())?; + let old_vars_ptr = ecx.read_pointer(&environ.into())?; + ecx.memory.deallocate(old_vars_ptr, None, MiriMemoryKind::Env.into())?; Ok(()) } } @@ -85,7 +85,7 @@ fn alloc_env_var_as_c_str<'mir, 'tcx>( name: &OsStr, value: &OsStr, ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, -) -> InterpResult<'tcx, Pointer> { +) -> InterpResult<'tcx, Pointer>> { let mut name_osstring = name.to_os_string(); name_osstring.push("="); name_osstring.push(value); @@ -96,7 +96,7 @@ fn alloc_env_var_as_wide_str<'mir, 'tcx>( name: &OsStr, value: &OsStr, ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, -) -> InterpResult<'tcx, Pointer> { +) -> InterpResult<'tcx, Pointer>> { let mut name_osstring = name.to_os_string(); name_osstring.push("="); name_osstring.push(value); @@ -105,7 +105,7 @@ fn alloc_env_var_as_wide_str<'mir, 'tcx>( impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { - fn getenv(&mut self, name_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, Scalar> { + fn getenv(&mut self, name_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); let target_os = &this.tcx.sess.target.os; assert!( @@ -113,17 +113,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "`getenv` is only available for the UNIX target family" ); - let name_ptr = this.read_scalar(name_op)?.check_init()?; + let name_ptr = this.read_pointer(name_op)?; let name = this.read_os_str_from_c_str(name_ptr)?; Ok(match this.machine.env_vars.map.get(name) { Some(var_ptr) => { // The offset is used to strip the "{name}=" part of the string. - Scalar::from(var_ptr.offset( + var_ptr.offset( Size::from_bytes(u64::try_from(name.len()).unwrap().checked_add(1).unwrap()), this, - )?) + )? } - None => Scalar::null_ptr(&*this.tcx), + None => Pointer::null(), }) } @@ -139,7 +139,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); this.assert_target_os("windows", "GetEnvironmentVariableW"); - let name_ptr = this.read_scalar(name_op)?.check_init()?; + let name_ptr = this.read_pointer(name_op)?; let name = this.read_os_str_from_wide_str(name_ptr)?; Ok(match this.machine.env_vars.map.get(&name) { Some(var_ptr) => { @@ -148,11 +148,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let name_offset_bytes = u64::try_from(name.len()).unwrap() .checked_add(1).unwrap() .checked_mul(2).unwrap(); - let var_ptr = - Scalar::from(var_ptr.offset(Size::from_bytes(name_offset_bytes), this)?); + let var_ptr = var_ptr.offset(Size::from_bytes(name_offset_bytes), this)?; let var = this.read_os_str_from_wide_str(var_ptr)?; - let buf_ptr = this.read_scalar(buf_op)?.check_init()?; + let buf_ptr = this.read_pointer(buf_op)?; // `buf_size` represents the size in characters. let buf_size = u64::from(this.read_scalar(size_op)?.to_u32()?); windows_check_buffer_size(this.write_os_str_to_wide_str(&var, buf_ptr, buf_size)?) @@ -166,7 +165,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } #[allow(non_snake_case)] - fn GetEnvironmentStringsW(&mut self) -> InterpResult<'tcx, Scalar> { + fn GetEnvironmentStringsW(&mut self) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); this.assert_target_os("windows", "GetEnvironmentStringsW"); @@ -174,7 +173,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // https://docs.microsoft.com/en-us/windows/win32/procthread/environment-variables let mut env_vars = std::ffi::OsString::new(); for &item in this.machine.env_vars.map.values() { - let env_var = this.read_os_str_from_wide_str(Scalar::from(item))?; + let env_var = this.read_os_str_from_wide_str(item)?; env_vars.push(env_var); env_vars.push("\0"); } @@ -182,7 +181,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Final null terminator(block terminator) is added by `alloc_os_str_to_wide_str`. let envblock_ptr = this.alloc_os_str_as_wide_str(&env_vars, MiriMemoryKind::Env.into())?; // If the function succeeds, the return value is a pointer to the environment block of the current process. - Ok(envblock_ptr.into()) + Ok(envblock_ptr) } #[allow(non_snake_case)] @@ -193,12 +192,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); this.assert_target_os("windows", "FreeEnvironmentStringsW"); - let env_block_ptr = this.read_scalar(env_block_op)?.check_init()?; - let result = this.memory.deallocate( - this.force_ptr(env_block_ptr)?, - None, - MiriMemoryKind::Env.into(), - ); + let env_block_ptr = this.read_pointer(env_block_op)?; + let result = this.memory.deallocate(env_block_ptr, None, MiriMemoryKind::Env.into()); // If the function succeeds, the return value is nonzero. Ok(result.is_ok() as i32) } @@ -215,11 +210,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "`setenv` is only available for the UNIX target family" ); - let name_ptr = this.read_scalar(name_op)?.check_init()?; - let value_ptr = this.read_scalar(value_op)?.check_init()?; + let name_ptr = this.read_pointer(name_op)?; + let value_ptr = this.read_pointer(value_op)?; let mut new = None; - if !this.is_null(name_ptr)? { + if !this.ptr_is_null(name_ptr)? { let name = this.read_os_str_from_c_str(name_ptr)?; if !name.is_empty() && !name.to_string_lossy().contains('=') { let value = this.read_os_str_from_c_str(value_ptr)?; @@ -250,10 +245,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let mut this = self.eval_context_mut(); this.assert_target_os("windows", "SetEnvironmentVariableW"); - let name_ptr = this.read_scalar(name_op)?.check_init()?; - let value_ptr = this.read_scalar(value_op)?.check_init()?; + let name_ptr = this.read_pointer(name_op)?; + let value_ptr = this.read_pointer(value_op)?; - if this.is_null(name_ptr)? { + if this.ptr_is_null(name_ptr)? { // ERROR CODE is not clearly explained in docs.. For now, throw UB instead. throw_ub_format!("pointer to environment variable name is NULL"); } @@ -263,7 +258,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx throw_unsup_format!("environment variable name is an empty string"); } else if name.to_string_lossy().contains('=') { throw_unsup_format!("environment variable name contains '='"); - } else if this.is_null(value_ptr)? { + } else if this.ptr_is_null(value_ptr)? { // Delete environment variable `{name}` if let Some(var) = this.machine.env_vars.map.remove(&name) { this.memory.deallocate(var, None, MiriMemoryKind::Env.into())?; @@ -289,9 +284,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "`unsetenv` is only available for the UNIX target family" ); - let name_ptr = this.read_scalar(name_op)?.check_init()?; + let name_ptr = this.read_pointer(name_op)?; let mut success = None; - if !this.is_null(name_ptr)? { + if !this.ptr_is_null(name_ptr)? { let name = this.read_os_str_from_c_str(name_ptr)?.to_owned(); if !name.is_empty() && !name.to_string_lossy().contains('=') { success = Some(this.machine.env_vars.map.remove(&name)); @@ -315,7 +310,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, buf_op: &OpTy<'tcx, Tag>, size_op: &OpTy<'tcx, Tag>, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); let target_os = &this.tcx.sess.target.os; assert!( @@ -323,14 +318,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "`getcwd` is only available for the UNIX target family" ); + let buf = this.read_pointer(&buf_op)?; + let size = this.read_scalar(&size_op)?.to_machine_usize(&*this.tcx)?; + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("getcwd", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; - return Ok(Scalar::null_ptr(&*this.tcx)); + return Ok(Pointer::null()); } - let buf = this.read_scalar(&buf_op)?.check_init()?; - let size = this.read_scalar(&size_op)?.to_machine_usize(&*this.tcx)?; // If we cannot get the current directory, we return null match env::current_dir() { Ok(cwd) => { @@ -343,7 +339,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Err(e) => this.set_last_error_from_io_error(e.kind())?, } - Ok(Scalar::null_ptr(&*this.tcx)) + Ok(Pointer::null()) } #[allow(non_snake_case)] @@ -355,15 +351,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); this.assert_target_os("windows", "GetCurrentDirectoryW"); + let size = u64::from(this.read_scalar(size_op)?.to_u32()?); + let buf = this.read_pointer(buf_op)?; + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("GetCurrentDirectoryW", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; return Ok(0); } - let size = u64::from(this.read_scalar(size_op)?.to_u32()?); - let buf = this.read_scalar(buf_op)?.check_init()?; - // If we cannot get the current directory, we return 0 match env::current_dir() { Ok(cwd) => @@ -381,6 +377,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "`getcwd` is only available for the UNIX target family" ); + let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("chdir", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; @@ -388,8 +386,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(-1); } - let path = this.read_path_from_c_str(this.read_scalar(path_op)?.check_init()?)?; - match env::set_current_dir(path) { Ok(()) => Ok(0), Err(e) => { @@ -409,6 +405,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); this.assert_target_os("windows", "SetCurrentDirectoryW"); + let path = this.read_path_from_wide_str(this.read_pointer(path_op)?)?; + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("SetCurrentDirectoryW", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; @@ -416,8 +414,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(0); } - let path = this.read_path_from_wide_str(this.read_scalar(path_op)?.check_init()?)?; - match env::set_current_dir(path) { Ok(()) => Ok(1), Err(e) => { @@ -433,12 +429,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); // Deallocate the old environ list, if any. if let Some(environ) = this.machine.env_vars.environ { - let old_vars_ptr = this.read_scalar(&environ.into())?.check_init()?; - this.memory.deallocate( - this.force_ptr(old_vars_ptr)?, - None, - MiriMemoryKind::Env.into(), - )?; + let old_vars_ptr = this.read_pointer(&environ.into())?; + this.memory.deallocate(old_vars_ptr, None, MiriMemoryKind::Env.into())?; } else { // No `environ` allocated yet, let's do that. // This is memory backing an extern static, hence `ExternStatic`, not `Env`. @@ -448,10 +440,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } // Collect all the pointers to each variable in a vector. - let mut vars: Vec> = - this.machine.env_vars.map.values().map(|&ptr| ptr.into()).collect(); + let mut vars: Vec>> = + this.machine.env_vars.map.values().copied().collect(); // Add the trailing null pointer. - vars.push(Scalar::null_ptr(this)); + vars.push(Pointer::null()); // Make an array with all these pointers inside Miri. let tcx = this.tcx; let vars_layout = @@ -459,9 +451,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let vars_place = this.allocate(vars_layout, MiriMemoryKind::Env.into())?; for (idx, var) in vars.into_iter().enumerate() { let place = this.mplace_field(&vars_place, idx)?; - this.write_scalar(var, &place.into())?; + this.write_pointer(var, &place.into())?; } - this.write_scalar(vars_place.ptr, &this.machine.env_vars.environ.unwrap().into())?; + this.write_pointer(vars_place.ptr, &this.machine.env_vars.environ.unwrap().into())?; Ok(()) } diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 61dca93f0e..35c151b72f 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -72,10 +72,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx size: u64, zero_init: bool, kind: MiriMemoryKind, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); if size == 0 { - Ok(Scalar::null_ptr(this)) + Ok(Pointer::null()) } else { let align = this.min_align(size, kind); let ptr = this.memory.allocate(Size::from_bytes(size), align, kind.into())?; @@ -83,14 +83,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // We just allocated this, the access is definitely in-bounds. this.memory.write_bytes(ptr.into(), iter::repeat(0u8).take(size as usize)).unwrap(); } - Ok(Scalar::Ptr(ptr)) + Ok(ptr.into()) } } - fn free(&mut self, ptr: Scalar, kind: MiriMemoryKind) -> InterpResult<'tcx> { + fn free(&mut self, ptr: Pointer>, kind: MiriMemoryKind) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - if !this.is_null(ptr)? { - let ptr = this.force_ptr(ptr)?; + if !this.ptr_is_null(ptr)? { this.memory.deallocate(ptr, None, kind.into())?; } Ok(()) @@ -98,25 +97,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn realloc( &mut self, - old_ptr: Scalar, + old_ptr: Pointer>, new_size: u64, kind: MiriMemoryKind, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); let new_align = this.min_align(new_size, kind); - if this.is_null(old_ptr)? { + if this.ptr_is_null(old_ptr)? { if new_size == 0 { - Ok(Scalar::null_ptr(this)) + Ok(Pointer::null()) } else { let new_ptr = this.memory.allocate(Size::from_bytes(new_size), new_align, kind.into())?; - Ok(Scalar::Ptr(new_ptr)) + Ok(new_ptr.into()) } } else { - let old_ptr = this.force_ptr(old_ptr)?; if new_size == 0 { this.memory.deallocate(old_ptr, None, kind.into())?; - Ok(Scalar::null_ptr(this)) + Ok(Pointer::null()) } else { let new_ptr = this.memory.reallocate( old_ptr, @@ -125,7 +123,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx new_align, kind.into(), )?; - Ok(Scalar::Ptr(new_ptr)) + Ok(new_ptr.into()) } } } @@ -313,12 +311,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Miri-specific extern functions "miri_static_root" => { let &[ref ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?; - let ptr = this.read_scalar(ptr)?.check_init()?; - let ptr = this.force_ptr(ptr)?; - if ptr.offset != Size::ZERO { + let ptr = this.read_pointer(ptr)?; + let (alloc_id, offset, _) = this.memory.ptr_get_alloc(ptr)?; + if offset != Size::ZERO { throw_unsup_format!("pointer passed to miri_static_root must point to beginning of an allocated block"); } - this.machine.static_roots.push(ptr.alloc_id); + this.machine.static_roots.push(alloc_id); } // Obtains a Miri backtrace. See the README for details. @@ -339,7 +337,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let &[ref size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let size = this.read_scalar(size)?.to_machine_usize(this)?; let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C)?; - this.write_scalar(res, dest)?; + this.write_pointer(res, dest)?; } "calloc" => { let &[ref items, ref len] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -348,19 +346,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let size = items.checked_mul(len).ok_or_else(|| err_ub_format!("overflow during calloc size computation"))?; let res = this.malloc(size, /*zero_init:*/ true, MiriMemoryKind::C)?; - this.write_scalar(res, dest)?; + this.write_pointer(res, dest)?; } "free" => { let &[ref ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let ptr = this.read_scalar(ptr)?.check_init()?; + let ptr = this.read_pointer(ptr)?; this.free(ptr, MiriMemoryKind::C)?; } "realloc" => { let &[ref old_ptr, ref new_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let old_ptr = this.read_scalar(old_ptr)?.check_init()?; + let old_ptr = this.read_pointer(old_ptr)?; let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?; let res = this.realloc(old_ptr, new_size, MiriMemoryKind::C)?; - this.write_scalar(res, dest)?; + this.write_pointer(res, dest)?; } // Rust allocation @@ -376,7 +374,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Align::from_bytes(align).unwrap(), MiriMemoryKind::Rust.into(), )?; - this.write_scalar(ptr, dest)?; + this.write_pointer(ptr, dest)?; } "__rust_alloc_zeroed" => { let &[ref size, ref align] = this.check_shim(abi, Abi::Rust, link_name, args)?; @@ -390,15 +388,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx )?; // We just allocated this, the access is definitely in-bounds. this.memory.write_bytes(ptr.into(), iter::repeat(0u8).take(usize::try_from(size).unwrap())).unwrap(); - this.write_scalar(ptr, dest)?; + this.write_pointer(ptr, dest)?; } "__rust_dealloc" => { let &[ref ptr, ref old_size, ref align] = this.check_shim(abi, Abi::Rust, link_name, args)?; - let ptr = this.read_scalar(ptr)?.check_init()?; + let ptr = this.read_pointer(ptr)?; let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?; let align = this.read_scalar(align)?.to_machine_usize(this)?; // No need to check old_size/align; we anyway check that they match the allocation. - let ptr = this.force_ptr(ptr)?; this.memory.deallocate( ptr, Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())), @@ -407,7 +404,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } "__rust_realloc" => { let &[ref ptr, ref old_size, ref align, ref new_size] = this.check_shim(abi, Abi::Rust, link_name, args)?; - let ptr = this.force_ptr(this.read_scalar(ptr)?.check_init()?)?; + let ptr = this.read_pointer(ptr)?; let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?; let align = this.read_scalar(align)?.to_machine_usize(this)?; let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?; @@ -421,14 +418,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx align, MiriMemoryKind::Rust.into(), )?; - this.write_scalar(new_ptr, dest)?; + this.write_pointer(new_ptr, dest)?; } // C memory handling functions "memcmp" => { let &[ref left, ref right, ref n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let left = this.read_scalar(left)?.check_init()?; - let right = this.read_scalar(right)?.check_init()?; + let left = this.read_pointer(left)?; + let right = this.read_pointer(right)?; let n = Size::from_bytes(this.read_scalar(n)?.to_machine_usize(this)?); let result = { @@ -447,7 +444,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } "memrchr" => { let &[ref ptr, ref val, ref num] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let ptr = this.read_scalar(ptr)?.check_init()?; + let ptr = this.read_pointer(ptr)?; let val = this.read_scalar(val)?.to_i32()? as u8; let num = this.read_scalar(num)?.to_machine_usize(this)?; if let Some(idx) = this @@ -457,15 +454,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx .rev() .position(|&c| c == val) { - let new_ptr = ptr.ptr_offset(Size::from_bytes(num - idx as u64 - 1), this)?; - this.write_scalar(new_ptr, dest)?; + let new_ptr = ptr.offset(Size::from_bytes(num - idx as u64 - 1), this)?; + this.write_pointer(new_ptr, dest)?; } else { this.write_null(dest)?; } } "memchr" => { let &[ref ptr, ref val, ref num] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let ptr = this.read_scalar(ptr)?.check_init()?; + let ptr = this.read_pointer(ptr)?; let val = this.read_scalar(val)?.to_i32()? as u8; let num = this.read_scalar(num)?.to_machine_usize(this)?; let idx = this @@ -474,15 +471,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx .iter() .position(|&c| c == val); if let Some(idx) = idx { - let new_ptr = ptr.ptr_offset(Size::from_bytes(idx as u64), this)?; - this.write_scalar(new_ptr, dest)?; + let new_ptr = ptr.offset(Size::from_bytes(idx as u64), this)?; + this.write_pointer(new_ptr, dest)?; } else { this.write_null(dest)?; } } "strlen" => { let &[ref ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let ptr = this.read_scalar(ptr)?.check_init()?; + let ptr = this.read_pointer(ptr)?; let n = this.read_c_str(ptr)?.len(); this.write_scalar(Scalar::from_machine_usize(u64::try_from(n).unwrap(), this), dest)?; } diff --git a/src/shims/intrinsics.rs b/src/shims/intrinsics.rs index caef57df8d..5a3a782382 100644 --- a/src/shims/intrinsics.rs +++ b/src/shims/intrinsics.rs @@ -71,7 +71,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let ty = instance.substs.type_at(0); let ty_layout = this.layout_of(ty)?; let val_byte = this.read_scalar(val_byte)?.to_u8()?; - let ptr = this.read_scalar(ptr)?.check_init()?; + let ptr = this.read_pointer(ptr)?; let count = this.read_scalar(count)?.to_machine_usize(this)?; let byte_count = ty_layout.size.checked_mul(count, this).ok_or_else(|| { err_ub_format!("overflow computing total size of `write_bytes`") diff --git a/src/shims/mod.rs b/src/shims/mod.rs index bb8d0bb8db..b05fa76a93 100644 --- a/src/shims/mod.rs +++ b/src/shims/mod.rs @@ -74,8 +74,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(false); } - let req_align = - this.force_bits(this.read_scalar(align_op)?.check_init()?, this.pointer_size())?; + let req_align = this.read_scalar(align_op)?.to_machine_usize(this)?; // Stop if the alignment is not a power of two. if !req_align.is_power_of_two() { @@ -83,13 +82,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(true); // nothing left to do } - let ptr_scalar = this.read_scalar(ptr_op)?.check_init()?; - - if let Ok(ptr) = this.force_ptr(ptr_scalar) { + let ptr = this.read_pointer(ptr_op)?; + if let Ok(ptr) = ptr.into_pointer_or_addr() { // Only do anything if we can identify the allocation this goes to. - let cur_align = - this.memory.get_size_and_align(ptr.alloc_id, AllocCheck::MaybeDead)?.1.bytes(); - if u128::from(cur_align) >= req_align { + let (_, cur_align) = + this.memory.get_size_and_align(ptr.provenance.alloc_id, AllocCheck::MaybeDead)?; + if cur_align.bytes() >= req_align { // If the allocation alignment is at least the required alignment we use the // real implementation. return Ok(false); diff --git a/src/shims/os_str.rs b/src/shims/os_str.rs index ea99921c0b..83bb344982 100644 --- a/src/shims/os_str.rs +++ b/src/shims/os_str.rs @@ -50,19 +50,25 @@ impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mi pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { /// Helper function to read an OsString from a null-terminated sequence of bytes, which is what /// the Unix APIs usually handle. - fn read_os_str_from_c_str<'a>(&'a self, sptr: Scalar) -> InterpResult<'tcx, &'a OsStr> + fn read_os_str_from_c_str<'a>( + &'a self, + ptr: Pointer>, + ) -> InterpResult<'tcx, &'a OsStr> where 'tcx: 'a, 'mir: 'a, { let this = self.eval_context_ref(); - let bytes = this.read_c_str(sptr)?; + let bytes = this.read_c_str(ptr)?; bytes_to_os_str(bytes) } /// Helper function to read an OsString from a 0x0000-terminated sequence of u16, /// which is what the Windows APIs usually handle. - fn read_os_str_from_wide_str<'a>(&'a self, sptr: Scalar) -> InterpResult<'tcx, OsString> + fn read_os_str_from_wide_str<'a>( + &'a self, + ptr: Pointer>, + ) -> InterpResult<'tcx, OsString> where 'tcx: 'a, 'mir: 'a, @@ -78,7 +84,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(s.into()) } - let u16_vec = self.eval_context_ref().read_wide_str(sptr)?; + let u16_vec = self.eval_context_ref().read_wide_str(ptr)?; u16vec_to_osstring(u16_vec) } @@ -90,7 +96,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_os_str_to_c_str( &mut self, os_str: &OsStr, - sptr: Scalar, + ptr: Pointer>, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { let bytes = os_str_to_bytes(os_str)?; @@ -102,7 +108,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } self.eval_context_mut() .memory - .write_bytes(sptr, bytes.iter().copied().chain(iter::once(0u8)))?; + .write_bytes(ptr, bytes.iter().copied().chain(iter::once(0u8)))?; Ok((true, string_length)) } @@ -114,7 +120,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_os_str_to_wide_str( &mut self, os_str: &OsStr, - sptr: Scalar, + ptr: Pointer>, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { #[cfg(windows)] @@ -146,7 +152,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); let mut alloc = this .memory - .get_mut(sptr, size2 * string_length, Align::from_bytes(2).unwrap())? + .get_mut(ptr, size2 * string_length, Align::from_bytes(2).unwrap())? .unwrap(); // not a ZST, so we will get a result for (offset, wchar) in u16_vec.into_iter().chain(iter::once(0x0000)).enumerate() { let offset = u64::try_from(offset).unwrap(); @@ -161,14 +167,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, os_str: &OsStr, memkind: MemoryKind, - ) -> InterpResult<'tcx, Pointer> { + ) -> InterpResult<'tcx, Pointer>> { let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator. let this = self.eval_context_mut(); let arg_type = this.tcx.mk_array(this.tcx.types.u8, size); let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?; assert!(self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap().0); - Ok(arg_place.ptr.assert_ptr()) + Ok(arg_place.ptr) } /// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of `u16`. @@ -176,24 +182,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, os_str: &OsStr, memkind: MemoryKind, - ) -> InterpResult<'tcx, Pointer> { + ) -> InterpResult<'tcx, Pointer>> { let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0x0000` terminator. let this = self.eval_context_mut(); let arg_type = this.tcx.mk_array(this.tcx.types.u16, size); let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?; assert!(self.write_os_str_to_wide_str(os_str, arg_place.ptr, size).unwrap().0); - Ok(arg_place.ptr.assert_ptr()) + Ok(arg_place.ptr) } /// Read a null-terminated sequence of bytes, and perform path separator conversion if needed. - fn read_path_from_c_str<'a>(&'a self, sptr: Scalar) -> InterpResult<'tcx, Cow<'a, Path>> + fn read_path_from_c_str<'a>( + &'a self, + ptr: Pointer>, + ) -> InterpResult<'tcx, Cow<'a, Path>> where 'tcx: 'a, 'mir: 'a, { let this = self.eval_context_ref(); - let os_str = this.read_os_str_from_c_str(sptr)?; + let os_str = this.read_os_str_from_c_str(ptr)?; Ok(match this.convert_path_separator(Cow::Borrowed(os_str), PathConversion::TargetToHost) { Cow::Borrowed(x) => Cow::Borrowed(Path::new(x)), @@ -202,9 +211,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } /// Read a null-terminated sequence of `u16`s, and perform path separator conversion if needed. - fn read_path_from_wide_str(&self, sptr: Scalar) -> InterpResult<'tcx, PathBuf> { + fn read_path_from_wide_str(&self, ptr: Pointer>) -> InterpResult<'tcx, PathBuf> { let this = self.eval_context_ref(); - let os_str = this.read_os_str_from_wide_str(sptr)?; + let os_str = this.read_os_str_from_wide_str(ptr)?; Ok(this .convert_path_separator(Cow::Owned(os_str), PathConversion::TargetToHost) @@ -217,13 +226,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_path_to_c_str( &mut self, path: &Path, - sptr: Scalar, + ptr: Pointer>, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { let this = self.eval_context_mut(); let os_str = this .convert_path_separator(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget); - this.write_os_str_to_c_str(&os_str, sptr, size) + this.write_os_str_to_c_str(&os_str, ptr, size) } /// Write a Path to the machine memory (as a null-terminated sequence of `u16`s), @@ -231,13 +240,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_path_to_wide_str( &mut self, path: &Path, - sptr: Scalar, + ptr: Pointer>, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { let this = self.eval_context_mut(); let os_str = this .convert_path_separator(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget); - this.write_os_str_to_wide_str(&os_str, sptr, size) + this.write_os_str_to_wide_str(&os_str, ptr, size) } fn convert_path_separator<'a>( diff --git a/src/shims/panic.rs b/src/shims/panic.rs index 68b648f327..04a8e9063f 100644 --- a/src/shims/panic.rs +++ b/src/shims/panic.rs @@ -84,14 +84,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Get all the arguments. let &[ref try_fn, ref data, ref catch_fn] = check_arg_count(args)?; - let try_fn = this.read_scalar(try_fn)?.check_init()?; + let try_fn = this.read_pointer(try_fn)?; let data = this.read_scalar(data)?.check_init()?; let catch_fn = this.read_scalar(catch_fn)?.check_init()?; // Now we make a function call, and pass `data` as first and only argument. let f_instance = this.memory.get_fn(try_fn)?.as_instance()?; trace!("try_fn: {:?}", f_instance); - let ret_place = MPlaceTy::dangling(this.machine.layouts.unit, this).into(); + let ret_place = MPlaceTy::dangling(this.machine.layouts.unit).into(); this.call_function( f_instance, Abi::Rust, @@ -145,9 +145,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let payload = this.active_thread_mut().panic_payload.take().unwrap(); // Push the `catch_fn` stackframe. - let f_instance = this.memory.get_fn(catch_unwind.catch_fn)?.as_instance()?; + let f_instance = + this.memory.get_fn(this.scalar_to_ptr(catch_unwind.catch_fn))?.as_instance()?; trace!("catch_fn: {:?}", f_instance); - let ret_place = MPlaceTy::dangling(this.machine.layouts.unit, this).into(); + let ret_place = MPlaceTy::dangling(this.machine.layouts.unit).into(); this.call_function( f_instance, Abi::Rust, @@ -177,7 +178,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.call_function( panic, Abi::Rust, - &[msg.to_ref()], + &[msg.to_ref(this)], None, StackPopCleanup::Goto { ret: None, unwind }, ) diff --git a/src/shims/posix/foreign_items.rs b/src/shims/posix/foreign_items.rs index 1cfc3f0a4e..09dd7d9c7b 100644 --- a/src/shims/posix/foreign_items.rs +++ b/src/shims/posix/foreign_items.rs @@ -28,7 +28,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "getenv" => { let &[ref name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.getenv(name)?; - this.write_scalar(result, dest)?; + this.write_pointer(result, dest)?; } "unsetenv" => { let &[ref name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -44,7 +44,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "getcwd" => { let &[ref buf, ref size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.getcwd(buf, size)?; - this.write_scalar(result, dest)?; + this.write_pointer(result, dest)?; } "chdir" => { let &[ref path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -68,7 +68,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "read" => { let &[ref fd, ref buf, ref count] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; - let buf = this.read_scalar(buf)?.check_init()?; + let buf = this.read_pointer(buf)?; let count = this.read_scalar(count)?.to_machine_usize(this)?; let result = this.read(fd, buf, count)?; this.write_scalar(Scalar::from_machine_isize(result, this), dest)?; @@ -76,7 +76,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "write" => { let &[ref fd, ref buf, ref n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; - let buf = this.read_scalar(buf)?.check_init()?; + let buf = this.read_pointer(buf)?; let count = this.read_scalar(n)?.to_machine_usize(this)?; trace!("Called write({:?}, {:?}, {:?})", fd, buf, count); let result = this.write(fd, buf, count)?; @@ -160,7 +160,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Align::from_bytes(align).unwrap(), MiriMemoryKind::C.into(), )?; - this.write_scalar(ptr, &ret.into())?; + this.write_pointer(ptr, &ret.into())?; } this.write_null(dest)?; } @@ -169,11 +169,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "dlsym" => { let &[ref handle, ref symbol] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; this.read_scalar(handle)?.to_machine_usize(this)?; - let symbol = this.read_scalar(symbol)?.check_init()?; + let symbol = this.read_pointer(symbol)?; let symbol_name = this.read_c_str(symbol)?; if let Some(dlsym) = Dlsym::from_str(symbol_name, &this.tcx.sess.target.os)? { let ptr = this.memory.create_fn_alloc(FnVal::Other(dlsym)); - this.write_scalar(Scalar::from(ptr), dest)?; + this.write_pointer(ptr, dest)?; } else { this.write_null(dest)?; } @@ -208,12 +208,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "pthread_key_create" => { let &[ref key, ref dtor] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let key_place = this.deref_operand(key)?; - let dtor = this.read_scalar(dtor)?.check_init()?; + let dtor = this.read_pointer(dtor)?; // Extract the function type out of the signature (that seems easier than constructing it ourselves). - let dtor = match this.test_null(dtor)? { - Some(dtor_ptr) => Some(this.memory.get_fn(dtor_ptr)?.as_instance()?), - None => None, + let dtor = if !this.ptr_is_null(dtor)? { + Some(this.memory.get_fn(dtor)?.as_instance()?) + } else { + None }; // Figure out how large a pthread TLS key actually is. @@ -235,24 +236,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } "pthread_key_delete" => { let &[ref key] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let key = this.force_bits(this.read_scalar(key)?.check_init()?, key.layout.size)?; + let key = this.read_scalar(key)?.check_init()?.to_bits(key.layout.size)?; this.machine.tls.delete_tls_key(key)?; // Return success (0) this.write_null(dest)?; } "pthread_getspecific" => { let &[ref key] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let key = this.force_bits(this.read_scalar(key)?.check_init()?, key.layout.size)?; + let key = this.read_scalar(key)?.check_init()?.to_bits(key.layout.size)?; let active_thread = this.get_active_thread(); let ptr = this.machine.tls.load_tls(key, active_thread, this)?; this.write_scalar(ptr, dest)?; } "pthread_setspecific" => { let &[ref key, ref new_ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let key = this.force_bits(this.read_scalar(key)?.check_init()?, key.layout.size)?; + let key = this.read_scalar(key)?.check_init()?.to_bits(key.layout.size)?; let active_thread = this.get_active_thread(); - let new_ptr = this.read_scalar(new_ptr)?.check_init()?; - this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?; + let new_data = this.read_scalar(new_ptr)?; + this.machine.tls.store_tls(key, active_thread, new_data.check_init()?, &*this.tcx)?; // Return success (`0`). this.write_null(dest)?; @@ -412,9 +413,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } "pthread_atfork" => { let &[ref prepare, ref parent, ref child] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.force_bits(this.read_scalar(prepare)?.check_init()?, this.memory.pointer_size())?; - this.force_bits(this.read_scalar(parent)?.check_init()?, this.memory.pointer_size())?; - this.force_bits(this.read_scalar(child)?.check_init()?, this.memory.pointer_size())?; + this.read_pointer(prepare)?; + this.read_pointer(parent)?; + this.read_pointer(child)?; // We do not support forking, so there is nothing to do here. this.write_null(dest)?; } diff --git a/src/shims/posix/fs.rs b/src/shims/posix/fs.rs index bfc6195b3a..a14a9c907e 100644 --- a/src/shims/posix/fs.rs +++ b/src/shims/posix/fs.rs @@ -17,7 +17,6 @@ use rustc_target::abi::{Align, LayoutOf, Size}; use crate::*; use helpers::{check_arg_count, immty_from_int_checked, immty_from_uint_checked}; use shims::time::system_time_to_duration; -use stacked_borrows::Tag; #[derive(Debug)] struct FileHandle { @@ -317,7 +316,7 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, ' ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let path_scalar = this.read_scalar(path_op)?.check_init()?; + let path_scalar = this.read_pointer(path_op)?; let path = this.read_path_from_c_str(path_scalar)?.into_owned(); let metadata = match FileMetadata::from_path(this, &path, follow_symlink)? { @@ -582,7 +581,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx throw_unsup_format!("unsupported flags {:#x}", flag & !mirror); } - let path = this.read_path_from_c_str(this.read_scalar(path_op)?.check_init()?)?; + let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; let fd = options.open(&path).map(|file| { let fh = &mut this.machine.file_handler; @@ -670,7 +669,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn read(&mut self, fd: i32, buf: Scalar, count: u64) -> InterpResult<'tcx, i64> { + fn read(&mut self, fd: i32, buf: Pointer>, count: u64) -> InterpResult<'tcx, i64> { let this = self.eval_context_mut(); // Isolation check is done via `FileDescriptor` trait. @@ -718,7 +717,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn write(&mut self, fd: i32, buf: Scalar, count: u64) -> InterpResult<'tcx, i64> { + fn write(&mut self, fd: i32, buf: Pointer>, count: u64) -> InterpResult<'tcx, i64> { let this = self.eval_context_mut(); // Isolation check is done via `FileDescriptor` trait. @@ -788,7 +787,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.check_no_isolation("`unlink`")?; - let path = this.read_path_from_c_str(this.read_scalar(path_op)?.check_init()?)?; + let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; let result = remove_file(path).map(|_| 0); this.try_unwrap_io_result(result) @@ -814,8 +813,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.check_no_isolation("`symlink`")?; - let target = this.read_path_from_c_str(this.read_scalar(target_op)?.check_init()?)?; - let linkpath = this.read_path_from_c_str(this.read_scalar(linkpath_op)?.check_init()?)?; + let target = this.read_path_from_c_str(this.read_pointer(target_op)?)?; + let linkpath = this.read_path_from_c_str(this.read_pointer(linkpath_op)?)?; let result = create_link(&target, &linkpath).map(|_| 0); this.try_unwrap_io_result(result) @@ -877,11 +876,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.assert_target_os("linux", "statx"); this.check_no_isolation("`statx`")?; - let statxbuf_scalar = this.read_scalar(statxbuf_op)?.check_init()?; - let pathname_scalar = this.read_scalar(pathname_op)?.check_init()?; + let statxbuf_ptr = this.read_pointer(statxbuf_op)?; + let pathname_ptr = this.read_pointer(pathname_op)?; // If the statxbuf or pathname pointers are null, the function fails with `EFAULT`. - if this.is_null(statxbuf_scalar)? || this.is_null(pathname_scalar)? { + if this.ptr_is_null(statxbuf_ptr)? || this.ptr_is_null(pathname_ptr)? { let efault = this.eval_libc("EFAULT")?; this.set_last_error(efault)?; return Ok(-1); @@ -898,13 +897,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let statx_ty = this .resolve_path(&["libc", "unix", "linux_like", "linux", "gnu", "statx"]) .ty(*this.tcx, ty::ParamEnv::reveal_all()); - let statxbuf_ty = this.tcx.mk_mut_ptr(statx_ty); - let statxbuf_layout = this.layout_of(statxbuf_ty)?; - let statxbuf_imm = ImmTy::from_scalar(statxbuf_scalar, statxbuf_layout); - this.ref_to_mplace(&statxbuf_imm)? + let statx_layout = this.layout_of(statx_ty)?; + MPlaceTy::from_aligned_ptr(statxbuf_ptr, statx_layout) }; - let path = this.read_path_from_c_str(pathname_scalar)?.into_owned(); + let path = this.read_path_from_c_str(pathname_ptr)?.into_owned(); // See for a discussion of argument sizes. let flags = this.read_scalar(flags_op)?.to_i32()?; let empty_path_flag = flags & this.eval_libc("AT_EMPTY_PATH")?.to_i32()? != 0; @@ -1037,17 +1034,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.check_no_isolation("`rename`")?; - let oldpath_scalar = this.read_scalar(oldpath_op)?.check_init()?; - let newpath_scalar = this.read_scalar(newpath_op)?.check_init()?; + let oldpath_ptr = this.read_pointer(oldpath_op)?; + let newpath_ptr = this.read_pointer(newpath_op)?; - if this.is_null(oldpath_scalar)? || this.is_null(newpath_scalar)? { + if this.ptr_is_null(oldpath_ptr)? || this.ptr_is_null(newpath_ptr)? { let efault = this.eval_libc("EFAULT")?; this.set_last_error(efault)?; return Ok(-1); } - let oldpath = this.read_path_from_c_str(oldpath_scalar)?; - let newpath = this.read_path_from_c_str(newpath_scalar)?; + let oldpath = this.read_path_from_c_str(oldpath_ptr)?; + let newpath = this.read_path_from_c_str(newpath_ptr)?; let result = rename(oldpath, newpath).map(|_| 0); @@ -1065,12 +1062,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx #[cfg_attr(not(unix), allow(unused_variables))] let mode = if this.tcx.sess.target.os == "macos" { - u32::from(this.read_scalar(mode_op)?.check_init()?.to_u16()?) + u32::from(this.read_scalar(mode_op)?.to_u16()?) } else { this.read_scalar(mode_op)?.to_u32()? }; - let path = this.read_path_from_c_str(this.read_scalar(path_op)?.check_init()?)?; + let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; #[cfg_attr(not(unix), allow(unused_mut))] let mut builder = DirBuilder::new(); @@ -1093,7 +1090,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.check_no_isolation("`rmdir`")?; - let path = this.read_path_from_c_str(this.read_scalar(path_op)?.check_init()?)?; + let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; let result = remove_dir(path).map(|_| 0i32); @@ -1105,7 +1102,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.check_no_isolation("`opendir`")?; - let name = this.read_path_from_c_str(this.read_scalar(name_op)?.check_init()?)?; + let name = this.read_path_from_c_str(this.read_pointer(name_op)?)?; let result = read_dir(name); @@ -1449,8 +1446,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.check_no_isolation("readlink")?; - let pathname = this.read_path_from_c_str(this.read_scalar(pathname_op)?.check_init()?)?; - let buf = this.read_scalar(buf_op)?.check_init()?; + let pathname = this.read_path_from_c_str(this.read_pointer(pathname_op)?)?; + let buf = this.read_pointer(buf_op)?; let bufsize = this.read_scalar(bufsize_op)?.to_machine_usize(this)?; let result = std::fs::read_link(pathname); diff --git a/src/shims/posix/linux/foreign_items.rs b/src/shims/posix/linux/foreign_items.rs index 1d551b9fa1..0a9939fedd 100644 --- a/src/shims/posix/linux/foreign_items.rs +++ b/src/shims/posix/linux/foreign_items.rs @@ -26,7 +26,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "__errno_location" => { let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let errno_place = this.last_error_place()?; - this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?; + this.write_scalar(errno_place.to_ref(this).to_scalar()?, dest)?; } // File related shims (but also see "syscall" below for statx) @@ -231,7 +231,7 @@ fn getrandom<'tcx>( flags: &OpTy<'tcx, Tag>, dest: &PlaceTy<'tcx, Tag>, ) -> InterpResult<'tcx> { - let ptr = this.read_scalar(ptr)?.check_init()?; + let ptr = this.read_pointer(ptr)?; let len = this.read_scalar(len)?.to_machine_usize(this)?; // The only supported flags are GRND_RANDOM and GRND_NONBLOCK, diff --git a/src/shims/posix/linux/sync.rs b/src/shims/posix/linux/sync.rs index fda70d815d..17ddca879e 100644 --- a/src/shims/posix/linux/sync.rs +++ b/src/shims/posix/linux/sync.rs @@ -31,13 +31,8 @@ pub fn futex<'tcx>( let op = this.read_scalar(&args[2])?.to_i32()?; let val = this.read_scalar(&args[3])?.to_i32()?; - // The raw pointer value is used to identify the mutex. - // Not all mutex operations actually read from this address or even require this address to exist. - // This will make FUTEX_WAKE fail on an integer cast to a pointer. But FUTEX_WAIT on - // such a pointer can never work anyway, so that seems fine. - let futex_ptr = this.force_ptr(addr.to_scalar()?)?; - let thread = this.get_active_thread(); + let addr_scalar = addr.to_scalar()?; let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG")?; let futex_wait = this.eval_libc_i32("FUTEX_WAIT")?; @@ -57,14 +52,16 @@ pub fn futex<'tcx>( args.len() ); } - let timeout = &args[4]; - let timeout_time = if this.is_null(this.read_scalar(timeout)?.check_init()?)? { + + // `deref_operand` but not actually dereferencing the ptr yet (it might be NULL!). + let timeout = this.ref_to_mplace(&this.read_immediate(&args[4])?)?; + let timeout_time = if this.ptr_is_null(timeout.ptr)? { None } else { this.check_no_isolation( "`futex` syscall with `op=FUTEX_WAIT` and non-null timeout", )?; - let duration = match this.read_timespec(timeout)? { + let duration = match this.read_timespec(&timeout)? { Some(duration) => duration, None => { let einval = this.eval_libc("EINVAL")?; @@ -83,7 +80,7 @@ pub fn futex<'tcx>( // The API requires `addr` to be a 4-byte aligned pointer, and will // use the 4 bytes at the given address as an (atomic) i32. this.memory.check_ptr_access_align( - addr.to_scalar()?, + this.scalar_to_ptr(addr_scalar), Size::from_bytes(4), Align::from_bytes(4).unwrap(), CheckInAllocMsg::MemoryAccessTest, @@ -111,7 +108,7 @@ pub fn futex<'tcx>( if val == futex_val { // The value still matches, so we block the trait make it wait for FUTEX_WAKE. this.block_thread(thread); - this.futex_wait(futex_ptr, thread); + this.futex_wait(addr_scalar.to_machine_usize(this)?, thread); // Succesfully waking up from FUTEX_WAIT always returns zero. this.write_scalar(Scalar::from_machine_isize(0, this), dest)?; // Register a timeout callback if a timeout was specified. @@ -123,7 +120,7 @@ pub fn futex<'tcx>( timeout_time, Box::new(move |this| { this.unblock_thread(thread); - this.futex_remove_waiter(futex_ptr, thread); + this.futex_remove_waiter(addr_scalar.to_machine_usize(this)?, thread); let etimedout = this.eval_libc("ETIMEDOUT")?; this.set_last_error(etimedout)?; this.write_scalar(Scalar::from_machine_isize(-1, this), &dest)?; @@ -146,7 +143,7 @@ pub fn futex<'tcx>( op if op == futex_wake => { let mut n = 0; for _ in 0..val { - if let Some(thread) = this.futex_wake(futex_ptr) { + if let Some(thread) = this.futex_wake(addr_scalar.to_machine_usize(this)?) { this.unblock_thread(thread); this.unregister_timeout_callback_if_exists(thread); n += 1; diff --git a/src/shims/posix/macos/dlsym.rs b/src/shims/posix/macos/dlsym.rs index 7f39587974..d5996cc6a9 100644 --- a/src/shims/posix/macos/dlsym.rs +++ b/src/shims/posix/macos/dlsym.rs @@ -37,7 +37,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx match dlsym { Dlsym::getentropy => { let &[ref ptr, ref len] = check_arg_count(args)?; - let ptr = this.read_scalar(ptr)?.check_init()?; + let ptr = this.read_pointer(ptr)?; let len = this.read_scalar(len)?.to_machine_usize(this)?; this.gen_random(ptr, len)?; this.write_null(dest)?; diff --git a/src/shims/posix/macos/foreign_items.rs b/src/shims/posix/macos/foreign_items.rs index 47a860b96a..2f79b337ce 100644 --- a/src/shims/posix/macos/foreign_items.rs +++ b/src/shims/posix/macos/foreign_items.rs @@ -24,7 +24,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "__error" => { let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let errno_place = this.last_error_place()?; - this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?; + this.write_scalar(errno_place.to_ref(this).to_scalar()?, dest)?; } // File related shims @@ -74,7 +74,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Environment related shims "_NSGetEnviron" => { let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.write_scalar(this.machine.env_vars.environ.unwrap().ptr, dest)?; + this.write_pointer( + this.machine.env_vars.environ.expect("machine must be initialized").ptr, + dest, + )?; } // Time related shims @@ -100,18 +103,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Access to command-line arguments "_NSGetArgc" => { let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.write_scalar(this.machine.argc.expect("machine must be initialized"), dest)?; + this.write_pointer( + this.machine.argc.expect("machine must be initialized").ptr, + dest, + )?; } "_NSGetArgv" => { let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.write_scalar(this.machine.argv.expect("machine must be initialized"), dest)?; + this.write_pointer( + this.machine.argv.expect("machine must be initialized").ptr, + dest, + )?; } // Thread-local storage "_tlv_atexit" => { let &[ref dtor, ref data] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let dtor = this.read_scalar(dtor)?.check_init()?; + let dtor = this.read_pointer(dtor)?; let dtor = this.memory.get_fn(dtor)?.as_instance()?; let data = this.read_scalar(data)?.check_init()?; let active_thread = this.get_active_thread(); @@ -138,7 +147,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "pthread_setname_np" => { let &[ref name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let name = this.read_scalar(name)?.check_init()?; + let name = this.read_pointer(name)?; this.pthread_setname_np(name)?; } diff --git a/src/shims/posix/sync.rs b/src/shims/posix/sync.rs index 4725cd9fc3..782765c778 100644 --- a/src/shims/posix/sync.rs +++ b/src/shims/posix/sync.rs @@ -1,7 +1,6 @@ use std::time::SystemTime; use crate::*; -use stacked_borrows::Tag; use thread::Time; // pthread_mutexattr_t is either 4 or 8 bytes, depending on the platform. @@ -364,8 +363,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let attr = this.read_scalar(attr_op)?.check_init()?; - let kind = if this.is_null(attr)? { + let attr = this.read_pointer(attr_op)?; + let kind = if this.ptr_is_null(attr)? { this.eval_libc("PTHREAD_MUTEX_DEFAULT")? } else { mutexattr_get_kind(this, attr_op)?.check_init()? @@ -657,8 +656,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let attr = this.read_scalar(attr_op)?.check_init()?; - let clock_id = if this.is_null(attr)? { + let attr = this.read_pointer(attr_op)?; + let clock_id = if this.ptr_is_null(attr)? { this.eval_libc("CLOCK_REALTIME")? } else { condattr_get_clock_id(this, attr_op)?.check_init()? @@ -727,7 +726,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Extract the timeout. let clock_id = cond_get_clock_id(this, cond_op)?.to_i32()?; - let duration = match this.read_timespec(abstime_op)? { + let duration = match this.read_timespec(&this.deref_operand(abstime_op)?)? { Some(duration) => duration, None => { let einval = this.eval_libc("EINVAL")?; diff --git a/src/shims/posix/thread.rs b/src/shims/posix/thread.rs index ce1c817cf3..9926c36c49 100644 --- a/src/shims/posix/thread.rs +++ b/src/shims/posix/thread.rs @@ -33,7 +33,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Read the function argument that will be sent to the new thread // before the thread starts executing since reading after the // context switch will incorrectly report a data-race. - let fn_ptr = this.read_scalar(start_routine)?.check_init()?; + let fn_ptr = this.read_pointer(start_routine)?; let func_arg = this.read_immediate(arg)?; // Finally switch to new thread so that we can push the first stackframe. @@ -70,7 +70,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !this.is_null(this.read_scalar(retval)?.check_init()?)? { + if !this.ptr_is_null(this.read_pointer(retval)?)? { // FIXME: implement reading the thread function's return place. throw_unsup_format!("Miri supports pthread_join only with retval==NULL"); } @@ -110,7 +110,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let option = this.read_scalar(option)?.to_i32()?; if option == this.eval_libc_i32("PR_SET_NAME")? { - let address = this.read_scalar(arg2)?.check_init()?; + let address = this.read_pointer(arg2)?; let mut name = this.read_c_str(address)?.to_owned(); // The name should be no more than 16 bytes, including the null // byte. Since `read_c_str` returns the string without the null @@ -118,7 +118,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx name.truncate(15); this.set_active_thread_name(name); } else if option == this.eval_libc_i32("PR_GET_NAME")? { - let address = this.read_scalar(arg2)?.check_init()?; + let address = this.read_pointer(arg2)?; let mut name = this.get_active_thread_name().to_vec(); name.push(0u8); assert!(name.len() <= 16); @@ -130,7 +130,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } - fn pthread_setname_np(&mut self, name: Scalar) -> InterpResult<'tcx> { + fn pthread_setname_np(&mut self, name: Pointer>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); this.assert_target_os("macos", "pthread_setname_np"); diff --git a/src/shims/time.rs b/src/shims/time.rs index d293e4d127..1db9d85deb 100644 --- a/src/shims/time.rs +++ b/src/shims/time.rs @@ -1,7 +1,6 @@ use std::convert::TryFrom; use std::time::{Duration, Instant, SystemTime}; -use crate::stacked_borrows::Tag; use crate::*; use helpers::{immty_from_int_checked, immty_from_uint_checked}; use thread::Time; @@ -63,8 +62,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.check_no_isolation("`gettimeofday`")?; // Using tz is obsolete and should always be null - let tz = this.read_scalar(tz_op)?.check_init()?; - if !this.is_null(tz)? { + let tz = this.read_pointer(tz_op)?; + if !this.ptr_is_null(tz)? { let einval = this.eval_libc("EINVAL")?; this.set_last_error(einval)?; return Ok(-1); @@ -206,7 +205,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.check_no_isolation("`nanosleep`")?; - let duration = match this.read_timespec(req_op)? { + let duration = match this.read_timespec(&this.deref_operand(req_op)?)? { Some(duration) => duration, None => { let einval = this.eval_libc("EINVAL")?; diff --git a/src/shims/tls.rs b/src/shims/tls.rs index 239364508e..cf8b8e3e1b 100644 --- a/src/shims/tls.rs +++ b/src/shims/tls.rs @@ -109,19 +109,17 @@ impl<'tcx> TlsData<'tcx> { &mut self, key: TlsKey, thread_id: ThreadId, - new_data: Option>, + new_data: Scalar, + cx: &impl HasDataLayout, ) -> InterpResult<'tcx> { match self.keys.get_mut(&key) { Some(TlsEntry { data, .. }) => { - match new_data { - Some(scalar) => { - trace!("TLS key {} for thread {:?} stored: {:?}", key, thread_id, scalar); - data.insert(thread_id, scalar); - } - None => { - trace!("TLS key {} for thread {:?} removed", key, thread_id); - data.remove(&thread_id); - } + if new_data.to_machine_usize(cx)? != 0 { + trace!("TLS key {} for thread {:?} stored: {:?}", key, thread_id, new_data); + data.insert(thread_id, new_data); + } else { + trace!("TLS key {} for thread {:?} removed", key, thread_id); + data.remove(&thread_id); } Ok(()) } @@ -250,11 +248,12 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "thread_local_key", "p_thread_callback", ])?; - let thread_callback = this.memory.get_fn(thread_callback.check_init()?)?.as_instance()?; + let thread_callback = + this.memory.get_fn(this.scalar_to_ptr(thread_callback))?.as_instance()?; // The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`. let reason = this.eval_path_scalar(&["std", "sys", "windows", "c", "DLL_THREAD_DETACH"])?; - let ret_place = MPlaceTy::dangling(this.machine.layouts.unit, this).into(); + let ret_place = MPlaceTy::dangling(this.machine.layouts.unit).into(); this.call_function( thread_callback, Abi::System { unwind: false }, @@ -277,7 +276,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx if let Some((instance, data)) = this.machine.tls.macos_thread_dtors.remove(&thread_id) { trace!("Running macos dtor {:?} on {:?} at {:?}", instance, data, thread_id); - let ret_place = MPlaceTy::dangling(this.machine.layouts.unit, this).into(); + let ret_place = MPlaceTy::dangling(this.machine.layouts.unit).into(); this.call_function( instance, Abi::C { unwind: false }, @@ -315,9 +314,12 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.machine.tls.dtors_running.get_mut(&active_thread).unwrap().last_dtor_key = Some(key); trace!("Running TLS dtor {:?} on {:?} at {:?}", instance, ptr, active_thread); - assert!(!this.is_null(ptr).unwrap(), "data can't be NULL when dtor is called!"); + assert!( + !ptr.to_machine_usize(this).unwrap() != 0, + "data can't be NULL when dtor is called!" + ); - let ret_place = MPlaceTy::dangling(this.machine.layouts.unit, this).into(); + let ret_place = MPlaceTy::dangling(this.machine.layouts.unit).into(); this.call_function( instance, Abi::C { unwind: false }, @@ -349,6 +351,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn schedule_next_tls_dtor_for_active_thread(&mut self) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let active_thread = this.get_active_thread(); + trace!("schedule_next_tls_dtor_for_active_thread on thread {:?}", active_thread); if !this.machine.tls.set_dtors_running_for_thread(active_thread) { // This is the first time we got asked to schedule a destructor. The diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index b532457627..0eebd6aca5 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -43,7 +43,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "GetEnvironmentStringsW" => { let &[] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; let result = this.GetEnvironmentStringsW()?; - this.write_scalar(result, dest)?; + this.write_pointer(result, dest)?; } "FreeEnvironmentStringsW" => { let &[ref env_block] = @@ -78,7 +78,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; this.read_scalar(overlapped)?.to_machine_usize(this)?; // this is a poiner, that we ignore let handle = this.read_scalar(handle)?.to_machine_isize(this)?; - let buf = this.read_scalar(buf)?.check_init()?; + let buf = this.read_pointer(buf)?; let n = this.read_scalar(n)?.to_u32()?; let written_place = this.deref_operand(written_ptr)?; // Spec says to always write `0` first. @@ -116,14 +116,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let size = this.read_scalar(size)?.to_machine_usize(this)?; let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap)?; - this.write_scalar(res, dest)?; + this.write_pointer(res, dest)?; } "HeapFree" => { let &[ref handle, ref flags, ref ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; this.read_scalar(handle)?.to_machine_isize(this)?; this.read_scalar(flags)?.to_u32()?; - let ptr = this.read_scalar(ptr)?.check_init()?; + let ptr = this.read_pointer(ptr)?; this.free(ptr, MiriMemoryKind::WinHeap)?; this.write_scalar(Scalar::from_i32(1), dest)?; } @@ -132,10 +132,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; this.read_scalar(handle)?.to_machine_isize(this)?; this.read_scalar(flags)?.to_u32()?; - let ptr = this.read_scalar(ptr)?.check_init()?; + let ptr = this.read_pointer(ptr)?; let size = this.read_scalar(size)?.to_machine_usize(this)?; let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?; - this.write_scalar(res, dest)?; + this.write_pointer(res, dest)?; } // errno @@ -189,8 +189,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; let key = u128::from(this.read_scalar(key)?.to_u32()?); let active_thread = this.get_active_thread(); - let new_ptr = this.read_scalar(new_ptr)?.check_init()?; - this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?; + let new_data = this.read_scalar(new_ptr)?.check_init()?; + this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?; // Return success (`1`). this.write_scalar(Scalar::from_i32(1), dest)?; @@ -199,8 +199,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Access to command-line arguments "GetCommandLineW" => { let &[] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; - this.write_scalar( - this.machine.cmd_line.expect("machine must be initialized"), + this.write_pointer( + this.machine.cmd_line.expect("machine must be initialized").ptr, dest, )?; } @@ -267,10 +267,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let &[ref hModule, ref lpProcName] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; this.read_scalar(hModule)?.to_machine_isize(this)?; - let name = this.read_c_str(this.read_scalar(lpProcName)?.check_init()?)?; + let name = this.read_c_str(this.read_pointer(lpProcName)?)?; if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? { let ptr = this.memory.create_fn_alloc(FnVal::Other(dlsym)); - this.write_scalar(Scalar::from(ptr), dest)?; + this.write_pointer(ptr, dest)?; } else { this.write_null(dest)?; } @@ -281,7 +281,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // This is really 'RtlGenRandom'. let &[ref ptr, ref len] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; - let ptr = this.read_scalar(ptr)?.check_init()?; + let ptr = this.read_pointer(ptr)?; let len = this.read_scalar(len)?.to_u32()?; this.gen_random(ptr, len.into())?; this.write_scalar(Scalar::from_bool(true), dest)?; @@ -290,7 +290,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let &[ref algorithm, ref ptr, ref len, ref flags] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; let algorithm = this.read_scalar(algorithm)?; - let ptr = this.read_scalar(ptr)?.check_init()?; + let ptr = this.read_pointer(ptr)?; let len = this.read_scalar(len)?.to_u32()?; let flags = this.read_scalar(flags)?.to_u32()?; if flags != 2 { diff --git a/src/stacked_borrows.rs b/src/stacked_borrows.rs index 0365e9ca00..ddfbfd4266 100644 --- a/src/stacked_borrows.rs +++ b/src/stacked_borrows.rs @@ -10,7 +10,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::Mutability; use rustc_middle::mir::RetagKind; use rustc_middle::ty; -use rustc_target::abi::{Align, LayoutOf, Size}; +use rustc_target::abi::{LayoutOf, Size}; use crate::*; @@ -20,16 +20,16 @@ pub type AllocExtra = Stacks; /// Tracking pointer provenance #[derive(Copy, Clone, Hash, PartialEq, Eq)] -pub enum Tag { +pub enum SbTag { Tagged(PtrId), Untagged, } -impl fmt::Debug for Tag { +impl fmt::Debug for SbTag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Tag::Tagged(id) => write!(f, "<{}>", id), - Tag::Untagged => write!(f, ""), + SbTag::Tagged(id) => write!(f, "<{}>", id), + SbTag::Untagged => write!(f, ""), } } } @@ -54,7 +54,7 @@ pub struct Item { /// The permission this item grants. perm: Permission, /// The pointers the permission is granted to. - tag: Tag, + tag: SbTag, /// An optional protector, ensuring the item cannot get popped until `CallId` is over. protector: Option, } @@ -95,7 +95,7 @@ pub struct GlobalState { /// Table storing the "base" tag for each allocation. /// The base tag is the one used for the initial pointer. /// We need this in a separate table to handle cyclic statics. - base_ptr_ids: FxHashMap, + base_ptr_ids: FxHashMap, /// Next unused call ID (for protectors). next_call_id: CallId, /// Those call IDs corresponding to functions that are still running. @@ -197,14 +197,22 @@ impl GlobalState { self.active_calls.contains(&id) } - pub fn global_base_ptr(&mut self, id: AllocId) -> Tag { + pub fn base_tag(&mut self, id: AllocId) -> SbTag { self.base_ptr_ids.get(&id).copied().unwrap_or_else(|| { - let tag = Tag::Tagged(self.new_ptr()); + let tag = SbTag::Tagged(self.new_ptr()); trace!("New allocation {:?} has base tag {:?}", id, tag); self.base_ptr_ids.try_insert(id, tag).unwrap(); tag }) } + + pub fn base_tag_untagged(&mut self, id: AllocId) -> SbTag { + trace!("New allocation {:?} has no base tag (untagged)", id); + let tag = SbTag::Untagged; + // This must only be done on new allocations. + self.base_ptr_ids.try_insert(id, tag).unwrap(); + tag + } } /// Error reporting @@ -247,7 +255,7 @@ impl Permission { impl<'tcx> Stack { /// Find the item granting the given kind of access to the given tag, and return where /// it is on the stack. - fn find_granting(&self, access: AccessKind, tag: Tag) -> Option { + fn find_granting(&self, access: AccessKind, tag: SbTag) -> Option { self.borrows .iter() .enumerate() // we also need to know *where* in the stack @@ -288,8 +296,12 @@ impl<'tcx> Stack { } /// Check if the given item is protected. - fn check_protector(item: &Item, tag: Option, global: &GlobalState) -> InterpResult<'tcx> { - if let Tag::Tagged(id) = item.tag { + fn check_protector( + item: &Item, + tag: Option, + global: &GlobalState, + ) -> InterpResult<'tcx> { + if let SbTag::Tagged(id) = item.tag { if Some(id) == global.tracked_pointer_tag { register_diagnostic(NonHaltingDiagnostic::PoppedPointerTag(item.clone())); } @@ -314,18 +326,17 @@ impl<'tcx> Stack { fn access( &mut self, access: AccessKind, - ptr: Pointer, + tag: SbTag, + dbg_ptr: Pointer, // just for debug printing amd error messages global: &GlobalState, ) -> InterpResult<'tcx> { // Two main steps: Find granting item, remove incompatible items above. // Step 1: Find granting item. - let granting_idx = self.find_granting(access, ptr.tag).ok_or_else(|| { + let granting_idx = self.find_granting(access, tag).ok_or_else(|| { err_sb_ub(format!( - "no item granting {} to tag {:?} at {} found in borrow stack.", - access, - ptr.tag, - ptr.erase_tag(), + "no item granting {} to tag {:?} at {:?} found in borrow stack.", + access, tag, dbg_ptr, )) })?; @@ -337,7 +348,7 @@ impl<'tcx> Stack { let first_incompatible_idx = self.find_first_write_incompatible(granting_idx); for item in self.borrows.drain(first_incompatible_idx..).rev() { trace!("access: popping item {:?}", item); - Stack::check_protector(&item, Some(ptr.tag), global)?; + Stack::check_protector(&item, Some(tag), global)?; } } else { // On a read, *disable* all `Unique` above the granting item. This ensures U2 for read accesses. @@ -352,7 +363,7 @@ impl<'tcx> Stack { let item = &mut self.borrows[idx]; if item.perm == Permission::Unique { trace!("access: disabling item {:?}", item); - Stack::check_protector(item, Some(ptr.tag), global)?; + Stack::check_protector(item, Some(tag), global)?; item.perm = Permission::Disabled; } } @@ -364,12 +375,17 @@ impl<'tcx> Stack { /// Deallocate a location: Like a write access, but also there must be no /// active protectors at all because we will remove all items. - fn dealloc(&mut self, ptr: Pointer, global: &GlobalState) -> InterpResult<'tcx> { + fn dealloc( + &mut self, + tag: SbTag, + dbg_ptr: Pointer, // just for debug printing amd error messages + global: &GlobalState, + ) -> InterpResult<'tcx> { // Step 1: Find granting item. - self.find_granting(AccessKind::Write, ptr.tag).ok_or_else(|| { + self.find_granting(AccessKind::Write, tag).ok_or_else(|| { err_sb_ub(format!( - "no item granting write access for deallocation to tag {:?} at {} found in borrow stack", - ptr.tag, ptr.erase_tag(), + "no item granting write access for deallocation to tag {:?} at {:?} found in borrow stack", + tag, dbg_ptr, )) })?; @@ -387,8 +403,9 @@ impl<'tcx> Stack { /// from instead of all the way at the top of the stack. fn grant( &mut self, - derived_from: Pointer, + derived_from: SbTag, new: Item, + dbg_ptr: Pointer, global: &GlobalState, ) -> InterpResult<'tcx> { // Figure out which access `perm` corresponds to. @@ -396,10 +413,10 @@ impl<'tcx> Stack { if new.perm.grants(AccessKind::Write) { AccessKind::Write } else { AccessKind::Read }; // Now we figure out which item grants our parent (`derived_from`) this kind of access. // We use that to determine where to put the new item. - let granting_idx = self.find_granting(access, derived_from.tag) + let granting_idx = self.find_granting(access, derived_from) .ok_or_else(|| err_sb_ub(format!( - "trying to reborrow for {:?} at {}, but parent tag {:?} does not have an appropriate item in the borrow stack", - new.perm, derived_from.erase_tag(), derived_from.tag, + "trying to reborrow for {:?} at {:?}, but parent tag {:?} does not have an appropriate item in the borrow stack", + new.perm, dbg_ptr, derived_from, )))?; // Compute where to put the new item. @@ -419,7 +436,7 @@ impl<'tcx> Stack { // A "safe" reborrow for a pointer that actually expects some aliasing guarantees. // Here, creating a reference actually counts as an access. // This ensures F2b for `Unique`, by removing offending `SharedReadOnly`. - self.access(access, derived_from, global)?; + self.access(access, derived_from, dbg_ptr, global)?; // We insert "as far up as possible": We know only compatible items are remaining // on top of `derived_from`, and we want the new item at the top so that we @@ -445,7 +462,7 @@ impl<'tcx> Stack { /// Map per-stack operations to higher-level per-location-range operations. impl<'tcx> Stacks { /// Creates new stack with initial tag. - fn new(size: Size, perm: Permission, tag: Tag) -> Self { + fn new(size: Size, perm: Permission, tag: SbTag) -> Self { let item = Item { perm, tag, protector: None }; let stack = Stack { borrows: vec![item] }; @@ -455,15 +472,12 @@ impl<'tcx> Stacks { /// Call `f` on every stack in the range. fn for_each( &self, - ptr: Pointer, - size: Size, - f: impl Fn(Pointer, &mut Stack) -> InterpResult<'tcx>, + range: AllocRange, + f: impl Fn(Size, &mut Stack) -> InterpResult<'tcx>, ) -> InterpResult<'tcx> { let mut stacks = self.stacks.borrow_mut(); - for (offset, stack) in stacks.iter_mut(ptr.offset, size) { - let mut cur_ptr = ptr; - cur_ptr.offset = offset; - f(cur_ptr, stack)?; + for (offset, stack) in stacks.iter_mut(range.start, range.size) { + f(offset, stack)?; } Ok(()) } @@ -471,15 +485,12 @@ impl<'tcx> Stacks { /// Call `f` on every stack in the range. fn for_each_mut( &mut self, - ptr: Pointer, - size: Size, - f: impl Fn(Pointer, &mut Stack) -> InterpResult<'tcx>, + range: AllocRange, + f: impl Fn(Size, &mut Stack) -> InterpResult<'tcx>, ) -> InterpResult<'tcx> { let stacks = self.stacks.get_mut(); - for (offset, stack) in stacks.iter_mut(ptr.offset, size) { - let mut cur_ptr = ptr; - cur_ptr.offset = offset; - f(cur_ptr, stack)?; + for (offset, stack) in stacks.iter_mut(range.start, range.size) { + f(offset, stack)?; } Ok(()) } @@ -492,15 +503,15 @@ impl Stacks { size: Size, extra: &MemoryExtra, kind: MemoryKind, - ) -> (Self, Tag) { + ) -> Self { let mut extra = extra.borrow_mut(); - let (tag, perm) = match kind { + let (base_tag, perm) = match kind { // New unique borrow. This tag is not accessible by the program, // so it will only ever be used when using the local directly (i.e., // not through a pointer). That is, whenever we directly write to a local, this will pop // everything else off the stack, invalidating all previous pointers, // and in particular, *all* raw pointers. - MemoryKind::Stack => (Tag::Tagged(extra.new_ptr()), Permission::Unique), + MemoryKind::Stack => (extra.base_tag(id), Permission::Unique), // `Global` memory can be referenced by global pointers from `tcx`. // Thus we call `global_base_ptr` such that the global pointers get the same tag // as what we use here. @@ -515,53 +526,72 @@ impl Stacks { | MiriMemoryKind::Tls | MiriMemoryKind::Env | MiriMemoryKind::Machine, - ) => (extra.global_base_ptr(id), Permission::SharedReadWrite), + ) => (extra.base_tag(id), Permission::SharedReadWrite), // Heap allocations we only track precisely when raw pointers are tagged, for now. MemoryKind::Machine( MiriMemoryKind::Rust | MiriMemoryKind::C | MiriMemoryKind::WinHeap, ) => { let tag = - if extra.track_raw { Tag::Tagged(extra.new_ptr()) } else { Tag::Untagged }; + if extra.track_raw { extra.base_tag(id) } else { extra.base_tag_untagged(id) }; (tag, Permission::SharedReadWrite) } }; - (Stacks::new(size, perm, tag), tag) + Stacks::new(size, perm, base_tag) } #[inline(always)] pub fn memory_read<'tcx>( &self, - ptr: Pointer, - size: Size, + alloc_id: AllocId, + tag: SbTag, + range: AllocRange, extra: &MemoryExtra, ) -> InterpResult<'tcx> { - trace!("read access with tag {:?}: {:?}, size {}", ptr.tag, ptr.erase_tag(), size.bytes()); + trace!( + "read access with tag {:?}: {:?}, size {}", + tag, + Pointer::new(alloc_id, range.start), + range.size.bytes() + ); let global = &*extra.borrow(); - self.for_each(ptr, size, move |ptr, stack| stack.access(AccessKind::Read, ptr, global)) + self.for_each(range, move |offset, stack| { + stack.access(AccessKind::Read, tag, Pointer::new(alloc_id, offset), global) + }) } #[inline(always)] pub fn memory_written<'tcx>( &mut self, - ptr: Pointer, - size: Size, + alloc_id: AllocId, + tag: SbTag, + range: AllocRange, extra: &mut MemoryExtra, ) -> InterpResult<'tcx> { - trace!("write access with tag {:?}: {:?}, size {}", ptr.tag, ptr.erase_tag(), size.bytes()); + trace!( + "write access with tag {:?}: {:?}, size {}", + tag, + Pointer::new(alloc_id, range.start), + range.size.bytes() + ); let global = extra.get_mut(); - self.for_each_mut(ptr, size, move |ptr, stack| stack.access(AccessKind::Write, ptr, global)) + self.for_each_mut(range, move |offset, stack| { + stack.access(AccessKind::Write, tag, Pointer::new(alloc_id, offset), global) + }) } #[inline(always)] pub fn memory_deallocated<'tcx>( &mut self, - ptr: Pointer, - size: Size, + alloc_id: AllocId, + tag: SbTag, + range: AllocRange, extra: &mut MemoryExtra, ) -> InterpResult<'tcx> { - trace!("deallocation with tag {:?}: {:?}, size {}", ptr.tag, ptr.erase_tag(), size.bytes()); + trace!("deallocation with tag {:?}: {:?}, size {}", tag, alloc_id, range.size.bytes()); let global = extra.get_mut(); - self.for_each_mut(ptr, size, move |ptr, stack| stack.dealloc(ptr, global)) + self.for_each_mut(range, move |offset, stack| { + stack.dealloc(tag, Pointer::new(alloc_id, offset), global) + }) } } @@ -574,11 +604,12 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx place: &MPlaceTy<'tcx, Tag>, size: Size, kind: RefKind, - new_tag: Tag, + new_tag: SbTag, protect: bool, ) -> InterpResult<'tcx> { - // Nothing to do for ZSTs. + let this = self.eval_context_mut(); if size == Size::ZERO { + // Nothing to do for zero-sized accesses. trace!( "reborrow of size 0: {} reference {:?} derived from {:?} (pointee {})", kind, @@ -588,17 +619,30 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ); return Ok(()); } + let (alloc_id, base_offset, ptr) = this.memory.ptr_get_alloc(place.ptr)?; + let orig_tag = ptr.provenance.sb; + + // Ensure we bail out if the pointer goes out-of-bounds (see miri#1050). + let (allocation_size, _) = + this.memory.get_size_and_align(alloc_id, AllocCheck::Dereferenceable)?; + if base_offset + size > allocation_size { + throw_ub!(PointerOutOfBounds { + alloc_id, + offset: base_offset, + size, + allocation_size, + msg: CheckInAllocMsg::InboundsTest + }); + } - let this = self.eval_context_mut(); let protector = if protect { Some(this.frame().extra.call_id) } else { None }; - let ptr = place.ptr.assert_ptr(); trace!( "reborrow: {} reference {:?} derived from {:?} (pointee {}): {:?}, size {}", kind, new_tag, - ptr.tag, + orig_tag, place.layout.ty, - ptr.erase_tag(), + Pointer::new(alloc_id, base_offset), size.bytes() ); @@ -615,11 +659,13 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // We need a frozen-sensitive reborrow. // We have to use shared references to alloc/memory_extra here since // `visit_freeze_sensitive` needs to access the global state. - let extra = this.memory.get_alloc_extra(ptr.alloc_id)?; + let extra = this.memory.get_alloc_extra(alloc_id)?; let stacked_borrows = extra.stacked_borrows.as_ref().expect("we should have Stacked Borrows data"); let global = this.memory.extra.stacked_borrows.as_ref().unwrap().borrow(); - return this.visit_freeze_sensitive(place, size, |cur_ptr, size, frozen| { + this.visit_freeze_sensitive(place, size, |mut range, frozen| { + // Adjust range. + range.start += base_offset; // We are only ever `SharedReadOnly` inside the frozen bits. let perm = if frozen { Permission::SharedReadOnly @@ -627,21 +673,25 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Permission::SharedReadWrite }; let item = Item { perm, tag: new_tag, protector }; - stacked_borrows.for_each(cur_ptr, size, |cur_ptr, stack| { - stack.grant(cur_ptr, item, &*global) + stacked_borrows.for_each(range, |offset, stack| { + stack.grant(orig_tag, item, Pointer::new(alloc_id, offset), &*global) }) - }); + })?; + return Ok(()); } }; // Here we can avoid `borrow()` calls because we have mutable references. // Note that this asserts that the allocation is mutable -- but since we are creating a // mutable pointer, that seems reasonable. - let (alloc_extra, memory_extra) = this.memory.get_alloc_extra_mut(ptr.alloc_id)?; + let (alloc_extra, memory_extra) = this.memory.get_alloc_extra_mut(alloc_id)?; let stacked_borrows = alloc_extra.stacked_borrows.as_mut().expect("we should have Stacked Borrows data"); let global = memory_extra.stacked_borrows.as_mut().unwrap().get_mut(); let item = Item { perm, tag: new_tag, protector }; - stacked_borrows.for_each_mut(ptr, size, |ptr, stack| stack.grant(ptr, item, global)) + stacked_borrows.for_each_mut(alloc_range(base_offset, size), |offset, stack| { + stack.grant(orig_tag, item, Pointer::new(alloc_id, offset), global) + })?; + Ok(()) } /// Retags an indidual pointer, returning the retagged version. @@ -663,29 +713,26 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Some(size) => size, None => return Ok(*val), }; - // `reborrow` relies on getting a `Pointer` and everything being in-bounds, - // so let's ensure that. However, we do not care about alignment. - // We can see dangling ptrs in here e.g. after a Box's `Unique` was - // updated using "self.0 = ..." (can happen in Box::from_raw) so we cannot ICE; see miri#1050. - let place = this.mplace_access_checked(place, Some(Align::from_bytes(1).unwrap()))?; // Compute new borrow. let new_tag = { let mem_extra = this.memory.extra.stacked_borrows.as_mut().unwrap().get_mut(); match kind { // Give up tracking for raw pointers. - RefKind::Raw { .. } if !mem_extra.track_raw => Tag::Untagged, + RefKind::Raw { .. } if !mem_extra.track_raw => SbTag::Untagged, // All other pointers are properly tracked. - _ => Tag::Tagged(mem_extra.new_ptr()), + _ => SbTag::Tagged(mem_extra.new_ptr()), } }; // Reborrow. this.reborrow(&place, size, kind, new_tag, protect)?; - let new_place = place.replace_tag(new_tag); + + // Adjust pointer. + let new_place = place.map_provenance(|p| p.map(|t| Tag { sb: new_tag, ..t })); // Return new pointer. - Ok(ImmTy::from_immediate(new_place.to_ref(), val.layout)) + Ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout)) } } @@ -752,7 +799,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // We have to turn the place into a pointer to use the existing code. // (The pointer type does not matter, so we use a raw pointer.) let ptr_layout = this.layout_of(this.tcx.mk_mut_ptr(return_place.layout.ty))?; - let val = ImmTy::from_immediate(return_place.to_ref(), ptr_layout); + let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout); // Reborrow it. let val = this.retag_reference( &val, diff --git a/src/sync.rs b/src/sync.rs index b53af0aeb2..0e4e9695d8 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -152,7 +152,7 @@ pub(super) struct SynchronizationState { mutexes: IndexVec, rwlocks: IndexVec, condvars: IndexVec, - futexes: HashMap, + futexes: HashMap, } // Private extension trait for local helper methods @@ -486,18 +486,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.machine.threads.sync.condvars[id].waiters.retain(|waiter| waiter.thread != thread); } - fn futex_wait(&mut self, addr: Pointer, thread: ThreadId) { + fn futex_wait(&mut self, addr: u64, thread: ThreadId) { let this = self.eval_context_mut(); - let futex = &mut this.machine.threads.sync.futexes.entry(addr.erase_tag()).or_default(); + let futex = &mut this.machine.threads.sync.futexes.entry(addr).or_default(); let waiters = &mut futex.waiters; assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting"); waiters.push_back(FutexWaiter { thread }); } - fn futex_wake(&mut self, addr: Pointer) -> Option { + fn futex_wake(&mut self, addr: u64) -> Option { let this = self.eval_context_mut(); let current_thread = this.get_active_thread(); - let futex = &mut this.machine.threads.sync.futexes.get_mut(&addr.erase_tag())?; + let futex = &mut this.machine.threads.sync.futexes.get_mut(&addr)?; let data_race = &this.memory.extra.data_race; // Each futex-wake happens-before the end of the futex wait @@ -513,9 +513,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx res } - fn futex_remove_waiter(&mut self, addr: Pointer, thread: ThreadId) { + fn futex_remove_waiter(&mut self, addr: u64, thread: ThreadId) { let this = self.eval_context_mut(); - if let Some(futex) = this.machine.threads.sync.futexes.get_mut(&addr.erase_tag()) { + if let Some(futex) = this.machine.threads.sync.futexes.get_mut(&addr) { futex.waiters.retain(|waiter| waiter.thread != thread); } } diff --git a/src/thread.rs b/src/thread.rs index 7ee18bb7f8..de8e41224b 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -218,7 +218,7 @@ pub struct ThreadManager<'mir, 'tcx> { pub(crate) sync: SynchronizationState, /// A mapping from a thread-local static to an allocation id of a thread /// specific allocation. - thread_local_alloc_ids: RefCell>, + thread_local_alloc_ids: RefCell>>, /// A flag that indicates that we should change the active thread. yield_active_thread: bool, /// Callbacks that are called once the specified time passes. @@ -247,18 +247,18 @@ impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> { impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { /// Check if we have an allocation for the given thread local static for the /// active thread. - fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option { + fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option> { self.thread_local_alloc_ids.borrow().get(&(def_id, self.active_thread)).cloned() } - /// Set the allocation id as the allocation id of the given thread local + /// Set the pointer for the allocation of the given thread local /// static for the active thread. /// /// Panics if a thread local is initialized twice for the same thread. - fn set_thread_local_alloc_id(&self, def_id: DefId, new_alloc_id: AllocId) { + fn set_thread_local_alloc(&self, def_id: DefId, ptr: Pointer) { self.thread_local_alloc_ids .borrow_mut() - .try_insert((def_id, self.active_thread), new_alloc_id) + .try_insert((def_id, self.active_thread), ptr) .unwrap(); } @@ -435,11 +435,11 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { } /// Wakes up threads joining on the active one and deallocates thread-local statics. - /// The `AllocId` that can now be freed is returned. + /// The `AllocId` that can now be freed are returned. fn thread_terminated( &mut self, mut data_race: Option<&mut data_race::GlobalState>, - ) -> Vec { + ) -> Vec> { let mut free_tls_statics = Vec::new(); { let mut thread_local_statics = self.thread_local_alloc_ids.borrow_mut(); @@ -557,16 +557,16 @@ impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mi pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { /// Get a thread-specific allocation id for the given thread-local static. /// If needed, allocate a new one. - fn get_or_create_thread_local_alloc_id( + fn get_or_create_thread_local_alloc( &mut self, def_id: DefId, - ) -> InterpResult<'tcx, AllocId> { + ) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); let tcx = this.tcx; - if let Some(new_alloc_id) = this.machine.threads.get_thread_local_alloc_id(def_id) { + if let Some(old_alloc) = this.machine.threads.get_thread_local_alloc_id(def_id) { // We already have a thread-specific allocation id for this // thread-local static. - Ok(new_alloc_id) + Ok(old_alloc) } else { // We need to allocate a thread-specific allocation id for this // thread-local static. @@ -576,10 +576,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } let allocation = tcx.eval_static_initializer(def_id)?; // Create a fresh allocation with this content. - let new_alloc_id = - this.memory.allocate_with(allocation.clone(), MiriMemoryKind::Tls.into()).alloc_id; - this.machine.threads.set_thread_local_alloc_id(def_id, new_alloc_id); - Ok(new_alloc_id) + let new_alloc = + this.memory.allocate_with(allocation.clone(), MiriMemoryKind::Tls.into()); + this.machine.threads.set_thread_local_alloc(def_id, new_alloc); + Ok(new_alloc) } } @@ -761,10 +761,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx #[inline] fn thread_terminated(&mut self) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - for alloc_id in this.machine.threads.thread_terminated(this.memory.extra.data_race.as_mut()) - { - let ptr = this.memory.global_base_pointer(alloc_id.into())?; - this.memory.deallocate(ptr, None, MiriMemoryKind::Tls.into())?; + for ptr in this.machine.threads.thread_terminated(this.memory.extra.data_race.as_mut()) { + this.memory.deallocate(ptr.into(), None, MiriMemoryKind::Tls.into())?; } Ok(()) } diff --git a/tests/compile-fail/dangling_pointers/deref-partially-dangling.rs b/tests/compile-fail/dangling_pointers/deref-partially-dangling.rs index a6d3aae414..b7fcf4559e 100644 --- a/tests/compile-fail/dangling_pointers/deref-partially-dangling.rs +++ b/tests/compile-fail/dangling_pointers/deref-partially-dangling.rs @@ -3,6 +3,6 @@ fn main() { let x = (1, 13); let xptr = &x as *const _ as *const (i32, i32, i32); - let val = unsafe { (*xptr).1 }; //~ ERROR pointer must be in-bounds at offset 12, but is outside bounds of alloc + let val = unsafe { (*xptr).1 }; //~ ERROR pointer to 12 bytes starting at offset 0 is out-of-bounds assert_eq!(val, 13); } diff --git a/tests/compile-fail/dangling_pointers/dyn_size.rs b/tests/compile-fail/dangling_pointers/dyn_size.rs index 39a091387c..56de3970f1 100644 --- a/tests/compile-fail/dangling_pointers/dyn_size.rs +++ b/tests/compile-fail/dangling_pointers/dyn_size.rs @@ -9,5 +9,5 @@ fn main() { // That should be UB, as the reference is not fully dereferencable. let ptr: *const SliceWithHead = unsafe { std::mem::transmute((&buf, 4usize)) }; // Re-borrow that. This should be UB. - let _ptr = unsafe { &*ptr }; //~ ERROR pointer must be in-bounds at offset 5 + let _ptr = unsafe { &*ptr }; //~ ERROR pointer to 5 bytes starting at offset 0 is out-of-bounds } diff --git a/tests/compile-fail/dangling_pointers/maybe_null_pointer_deref_zst.rs b/tests/compile-fail/dangling_pointers/maybe_null_pointer_deref_zst.rs index 9db8a7dcba..357eadf91c 100644 --- a/tests/compile-fail/dangling_pointers/maybe_null_pointer_deref_zst.rs +++ b/tests/compile-fail/dangling_pointers/maybe_null_pointer_deref_zst.rs @@ -4,5 +4,5 @@ fn main() { // This pointer *could* be NULL so we cannot load from it, not even at ZST let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *const (); - let _x: () = unsafe { *ptr }; //~ ERROR outside bounds + let _x: () = unsafe { *ptr }; //~ ERROR out-of-bounds } diff --git a/tests/compile-fail/dangling_pointers/maybe_null_pointer_write_zst.rs b/tests/compile-fail/dangling_pointers/maybe_null_pointer_write_zst.rs index 79eff2507c..7f50b9827d 100644 --- a/tests/compile-fail/dangling_pointers/maybe_null_pointer_write_zst.rs +++ b/tests/compile-fail/dangling_pointers/maybe_null_pointer_write_zst.rs @@ -7,5 +7,5 @@ fn main() { // Also not assigning directly as that's array initialization, not assignment. let zst_val = [1u8; 0]; let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *mut [u8; 0]; - unsafe { *ptr = zst_val; } //~ ERROR outside bounds + unsafe { *ptr = zst_val; } //~ ERROR out-of-bounds } diff --git a/tests/compile-fail/dangling_pointers/out_of_bounds_read1.rs b/tests/compile-fail/dangling_pointers/out_of_bounds_read1.rs index 0c175af526..ef5cdeec99 100644 --- a/tests/compile-fail/dangling_pointers/out_of_bounds_read1.rs +++ b/tests/compile-fail/dangling_pointers/out_of_bounds_read1.rs @@ -1,5 +1,5 @@ fn main() { let v: Vec = vec![1, 2]; - let x = unsafe { *v.as_ptr().wrapping_offset(5) }; //~ ERROR outside bounds of alloc + let x = unsafe { *v.as_ptr().wrapping_offset(5) }; //~ ERROR out-of-bounds panic!("this should never print: {}", x); } diff --git a/tests/compile-fail/dangling_pointers/out_of_bounds_read2.rs b/tests/compile-fail/dangling_pointers/out_of_bounds_read2.rs index 0c175af526..ef5cdeec99 100644 --- a/tests/compile-fail/dangling_pointers/out_of_bounds_read2.rs +++ b/tests/compile-fail/dangling_pointers/out_of_bounds_read2.rs @@ -1,5 +1,5 @@ fn main() { let v: Vec = vec![1, 2]; - let x = unsafe { *v.as_ptr().wrapping_offset(5) }; //~ ERROR outside bounds of alloc + let x = unsafe { *v.as_ptr().wrapping_offset(5) }; //~ ERROR out-of-bounds panic!("this should never print: {}", x); } diff --git a/tests/compile-fail/intrinsics/out_of_bounds_ptr_1.rs b/tests/compile-fail/intrinsics/out_of_bounds_ptr_1.rs index 20bcc36f04..d200b3eb02 100644 --- a/tests/compile-fail/intrinsics/out_of_bounds_ptr_1.rs +++ b/tests/compile-fail/intrinsics/out_of_bounds_ptr_1.rs @@ -1,4 +1,4 @@ -// error-pattern: must be in-bounds at offset 5, but is outside bounds of alloc +// error-pattern: pointer to 5 bytes starting at offset 0 is out-of-bounds fn main() { let v = [0i8; 4]; let x = &v as *const i8; diff --git a/tests/compile-fail/intrinsics/out_of_bounds_ptr_2.rs b/tests/compile-fail/intrinsics/out_of_bounds_ptr_2.rs index 1e4759ddc4..52b385b8e3 100644 --- a/tests/compile-fail/intrinsics/out_of_bounds_ptr_2.rs +++ b/tests/compile-fail/intrinsics/out_of_bounds_ptr_2.rs @@ -2,6 +2,6 @@ fn main() { let v = [0i8; 4]; let x = &v as *const i8; - let x = unsafe { x.offset(-1) }; + let x = unsafe { x.offset(isize::MIN) }; panic!("this should never print: {:?}", x); } diff --git a/tests/compile-fail/intrinsics/ptr_offset_ptr_plus_0.rs b/tests/compile-fail/intrinsics/ptr_offset_ptr_plus_0.rs index d07ecc4dc7..8760345409 100644 --- a/tests/compile-fail/intrinsics/ptr_offset_ptr_plus_0.rs +++ b/tests/compile-fail/intrinsics/ptr_offset_ptr_plus_0.rs @@ -1,4 +1,4 @@ -// error-pattern: outside bounds of alloc +// error-pattern: pointer at offset 32 is out-of-bounds fn main() { let x = Box::into_raw(Box::new(0u32)); diff --git a/tests/compile-fail/null_pointer_deref_zst.rs b/tests/compile-fail/null_pointer_deref_zst.rs index 45925f3586..04617c58f3 100644 --- a/tests/compile-fail/null_pointer_deref_zst.rs +++ b/tests/compile-fail/null_pointer_deref_zst.rs @@ -3,6 +3,6 @@ #[allow(deref_nullptr)] fn main() { - let x: () = unsafe { *std::ptr::null() }; //~ ERROR memory access failed: 0x0 is not a valid pointer + let x: () = unsafe { *std::ptr::null() }; //~ ERROR dereferencing pointer failed: 0x0 is not a valid pointer panic!("this should never print: {:?}", x); } diff --git a/tests/compile-fail/null_pointer_write_zst.rs b/tests/compile-fail/null_pointer_write_zst.rs index 595011fcd6..46a8345b1b 100644 --- a/tests/compile-fail/null_pointer_write_zst.rs +++ b/tests/compile-fail/null_pointer_write_zst.rs @@ -1,10 +1,11 @@ // Some optimizations remove ZST accesses, thus masking this UB. // compile-flags: -Zmir-opt-level=0 +// error-pattern: memory access failed: 0x0 is not a valid pointer #[allow(deref_nullptr)] fn main() { // Not using the () type here, as writes of that type do not even have MIR generated. // Also not assigning directly as that's array initialization, not assignment. let zst_val = [1u8; 0]; - unsafe { *std::ptr::null_mut() = zst_val }; //~ ERROR memory access failed: 0x0 is not a valid pointer + unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) }; } diff --git a/tests/compile-fail/stacked_borrows/issue-miri-1050-1.rs b/tests/compile-fail/stacked_borrows/issue-miri-1050-1.rs index 24df70a817..852eb69968 100644 --- a/tests/compile-fail/stacked_borrows/issue-miri-1050-1.rs +++ b/tests/compile-fail/stacked_borrows/issue-miri-1050-1.rs @@ -1,4 +1,4 @@ -// error-pattern: pointer must be in-bounds +// error-pattern: pointer to 4 bytes starting at offset 0 is out-of-bounds fn main() { unsafe { let ptr = Box::into_raw(Box::new(0u16)); diff --git a/tests/compile-fail/stacked_borrows/static_memory_modification.rs b/tests/compile-fail/stacked_borrows/static_memory_modification.rs index 55a7f816c0..417a03bb03 100644 --- a/tests/compile-fail/stacked_borrows/static_memory_modification.rs +++ b/tests/compile-fail/stacked_borrows/static_memory_modification.rs @@ -3,6 +3,6 @@ static X: usize = 5; #[allow(mutable_transmutes)] fn main() { let _x = unsafe { - std::mem::transmute::<&usize, &mut usize>(&X) //~ ERROR writing to alloc0 which is read-only + std::mem::transmute::<&usize, &mut usize>(&X) //~ ERROR writing to alloc1 which is read-only }; } diff --git a/tests/compile-fail/zst1.rs b/tests/compile-fail/zst1.rs index e4ce7b8ecd..d400fba5d0 100644 --- a/tests/compile-fail/zst1.rs +++ b/tests/compile-fail/zst1.rs @@ -1,5 +1,5 @@ fn main() { // make sure ZST locals cannot be accessed let x = &() as *const () as *const i8; - let _val = unsafe { *x }; //~ ERROR pointer must be in-bounds + let _val = unsafe { *x }; //~ ERROR out-of-bounds } diff --git a/tests/compile-fail/zst3.rs b/tests/compile-fail/zst3.rs index 734c4b8ac4..7ecb8c7dca 100644 --- a/tests/compile-fail/zst3.rs +++ b/tests/compile-fail/zst3.rs @@ -14,5 +14,5 @@ fn main() { unsafe { *(x as *mut [u8; 0]) = zst_val; } // One byte further is OOB. let x = x.wrapping_offset(1); - unsafe { *(x as *mut [u8; 0]) = zst_val; } //~ ERROR pointer must be in-bounds + unsafe { *(x as *mut [u8; 0]) = zst_val; } //~ ERROR out-of-bounds }