From f65a69985b8e48efb568da0603282a21341b326e Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Wed, 9 Nov 2022 00:42:28 -0800 Subject: [PATCH 01/32] deref shim arguments with actual ty instead of declared ty --- src/concurrency/init_once.rs | 6 +- src/concurrency/sync.rs | 19 ++++- src/helpers.rs | 35 ++++++-- src/shims/foreign_items.rs | 6 +- src/shims/time.rs | 22 +++--- src/shims/unix/foreign_items.rs | 6 +- src/shims/unix/fs.rs | 17 +--- src/shims/unix/linux/fd.rs | 2 +- src/shims/unix/linux/foreign_items.rs | 2 +- src/shims/unix/linux/sync.rs | 6 +- src/shims/unix/sync.rs | 110 +++++++++++++++++++++----- src/shims/unix/thread.rs | 2 +- src/shims/windows/foreign_items.rs | 56 +++---------- src/shims/windows/sync.rs | 52 ++++++++---- 14 files changed, 215 insertions(+), 126 deletions(-) diff --git a/src/concurrency/init_once.rs b/src/concurrency/init_once.rs index 1f57e8b2b0..71582c75ea 100644 --- a/src/concurrency/init_once.rs +++ b/src/concurrency/init_once.rs @@ -2,6 +2,7 @@ use std::collections::VecDeque; use std::num::NonZeroU32; use rustc_index::Idx; +use rustc_middle::ty::layout::TyAndLayout; use super::sync::EvalContextExtPriv as _; use super::thread::MachineCallback; @@ -94,10 +95,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn init_once_get_or_create_id( &mut self, lock_op: &OpTy<'tcx, Provenance>, + lock_layout: TyAndLayout<'tcx>, offset: u64, ) -> InterpResult<'tcx, InitOnceId> { let this = self.eval_context_mut(); - this.init_once_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset)) + this.init_once_get_or_create(|ecx, next_id| { + ecx.get_or_create_id(next_id, lock_op, lock_layout, offset) + }) } /// Provides the closure with the next InitOnceId. Creates that InitOnce if the closure returns None, diff --git a/src/concurrency/sync.rs b/src/concurrency/sync.rs index f37a2fd2cd..ec70b7042d 100644 --- a/src/concurrency/sync.rs +++ b/src/concurrency/sync.rs @@ -6,6 +6,7 @@ use log::trace; use rustc_data_structures::fx::FxHashMap; use rustc_index::{Idx, IndexVec}; +use rustc_middle::ty::layout::TyAndLayout; use super::init_once::InitOnce; use super::vector_clock::VClock; @@ -200,11 +201,12 @@ pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>: &mut self, next_id: Id, lock_op: &OpTy<'tcx, Provenance>, + lock_layout: TyAndLayout<'tcx>, offset: u64, ) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); let value_place = - this.deref_operand_and_offset(lock_op, offset, this.machine.layouts.u32)?; + this.deref_operand_and_offset(lock_op, offset, lock_layout, this.machine.layouts.u32)?; // Since we are lazy, this update has to be atomic. let (old, success) = this @@ -278,28 +280,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn mutex_get_or_create_id( &mut self, lock_op: &OpTy<'tcx, Provenance>, + lock_layout: TyAndLayout<'tcx>, offset: u64, ) -> InterpResult<'tcx, MutexId> { let this = self.eval_context_mut(); - this.mutex_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset)) + this.mutex_get_or_create(|ecx, next_id| { + ecx.get_or_create_id(next_id, lock_op, lock_layout, offset) + }) } fn rwlock_get_or_create_id( &mut self, lock_op: &OpTy<'tcx, Provenance>, + lock_layout: TyAndLayout<'tcx>, offset: u64, ) -> InterpResult<'tcx, RwLockId> { let this = self.eval_context_mut(); - this.rwlock_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset)) + this.rwlock_get_or_create(|ecx, next_id| { + ecx.get_or_create_id(next_id, lock_op, lock_layout, offset) + }) } fn condvar_get_or_create_id( &mut self, lock_op: &OpTy<'tcx, Provenance>, + lock_layout: TyAndLayout<'tcx>, offset: u64, ) -> InterpResult<'tcx, CondvarId> { let this = self.eval_context_mut(); - this.condvar_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset)) + this.condvar_get_or_create(|ecx, next_id| { + ecx.get_or_create_id(next_id, lock_op, lock_layout, offset) + }) } #[inline] diff --git a/src/helpers.rs b/src/helpers.rs index befdddfa8c..ae5a975bb8 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -730,20 +730,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } + /// Dereference a pointer operand to a place using `layout` instead of the pointer's declared type + fn deref_operand_as( + &self, + op: &OpTy<'tcx, Provenance>, + layout: TyAndLayout<'tcx>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { + let this = self.eval_context_ref(); + let ptr = this.read_pointer(op)?; + + let mplace = MPlaceTy::from_aligned_ptr(ptr, layout); + + this.check_mplace(mplace)?; + + Ok(mplace) + } + /// Calculates the MPlaceTy given the offset and layout of an access on an operand fn deref_operand_and_offset( &self, op: &OpTy<'tcx, Provenance>, offset: u64, - layout: TyAndLayout<'tcx>, + base_layout: TyAndLayout<'tcx>, + value_layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { let this = self.eval_context_ref(); - let op_place = this.deref_operand(op)?; // FIXME: we still deref with the original type! + let op_place = this.deref_operand_as(op, base_layout)?; let offset = Size::from_bytes(offset); // Ensure that the access is within bounds. - assert!(op_place.layout.size >= offset + layout.size); - let value_place = op_place.offset(offset, layout, this)?; + assert!(base_layout.size >= offset + value_layout.size); + let value_place = op_place.offset(offset, value_layout, this)?; Ok(value_place) } @@ -751,10 +768,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &self, op: &OpTy<'tcx, Provenance>, offset: u64, - layout: TyAndLayout<'tcx>, + base_layout: TyAndLayout<'tcx>, + value_layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); - let value_place = this.deref_operand_and_offset(op, offset, layout)?; + let value_place = this.deref_operand_and_offset(op, offset, base_layout, value_layout)?; this.read_scalar(&value_place.into()) } @@ -763,10 +781,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { op: &OpTy<'tcx, Provenance>, offset: u64, value: impl Into>, - layout: TyAndLayout<'tcx>, + base_layout: TyAndLayout<'tcx>, + value_layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); - let value_place = this.deref_operand_and_offset(op, offset, layout)?; + let value_place = this.deref_operand_and_offset(op, offset, base_layout, value_layout)?; this.write_scalar(value, &value_place.into()) } diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 7436499457..d52582ce90 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -409,14 +409,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // &mut self, // arg1: &OpTy<'tcx, Provenance>, // arg2: &OpTy<'tcx, Provenance>, - // arg3: &OpTy<'tcx, Provenance>) + // arg3: &OpTy<'tcx, Provenance>, + // arg4: &OpTy<'tcx, Provenance>) // -> InterpResult<'tcx, Scalar> { // let this = self.eval_context_mut(); // // // First thing: load all the arguments. Details depend on the shim. // let arg1 = this.read_scalar(arg1)?.to_u32()?; // let arg2 = this.read_pointer(arg2)?; // when you need to work with the pointer directly - // let arg3 = this.deref_operand(arg3)?; // when you want to load/store through the pointer at its declared type + // let arg3 = this.deref_operand(arg3)?; // when you want to load/store through the pointer + // let arg4 = this.deref_operand_as(arg4, this.libc_ty_layout("some_libc_struct")?) // // // ... // diff --git a/src/shims/time.rs b/src/shims/time.rs index 756dec1bec..c761c45a5a 100644 --- a/src/shims/time.rs +++ b/src/shims/time.rs @@ -25,6 +25,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.assert_target_os_is_unix("clock_gettime"); let clk_id = this.read_scalar(clk_id_op)?.to_i32()?; + let tp = this.deref_operand_as(tp_op, this.libc_ty_layout("timespec"))?; let absolute_clocks; let mut relative_clocks; @@ -76,7 +77,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let tv_sec = duration.as_secs(); let tv_nsec = duration.subsec_nanos(); - this.write_int_fields(&[tv_sec.into(), tv_nsec.into()], &this.deref_operand(tp_op)?)?; + this.write_int_fields(&[tv_sec.into(), tv_nsec.into()], &tp)?; Ok(Scalar::from_i32(0)) } @@ -91,6 +92,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.assert_target_os_is_unix("gettimeofday"); this.check_no_isolation("`gettimeofday`")?; + let tv = this.deref_operand_as(tv_op, this.libc_ty_layout("timeval"))?; + // Using tz is obsolete and should always be null let tz = this.read_pointer(tz_op)?; if !this.ptr_is_null(tz)? { @@ -103,7 +106,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let tv_sec = duration.as_secs(); let tv_usec = duration.subsec_micros(); - this.write_int_fields(&[tv_sec.into(), tv_usec.into()], &this.deref_operand(tv_op)?)?; + this.write_int_fields(&[tv_sec.into(), tv_usec.into()], &tv)?; Ok(0) } @@ -118,6 +121,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.assert_target_os("windows", "GetSystemTimeAsFileTime"); this.check_no_isolation("`GetSystemTimeAsFileTime`")?; + let filetime = this.deref_operand_as(LPFILETIME_op, this.windows_ty_layout("FILETIME"))?; + let NANOS_PER_SEC = this.eval_windows_u64("time", "NANOS_PER_SEC"); let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC"); let INTERVALS_TO_UNIX_EPOCH = this.eval_windows_u64("time", "INTERVALS_TO_UNIX_EPOCH"); @@ -131,10 +136,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let dwLowDateTime = u32::try_from(duration_ticks & 0x00000000FFFFFFFF).unwrap(); let dwHighDateTime = u32::try_from((duration_ticks & 0xFFFFFFFF00000000) >> 32).unwrap(); - this.write_int_fields( - &[dwLowDateTime.into(), dwHighDateTime.into()], - &this.deref_operand(LPFILETIME_op)?, - )?; + this.write_int_fields(&[dwLowDateTime.into(), dwHighDateTime.into()], &filetime)?; Ok(()) } @@ -177,7 +179,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // and thus 10^9 counts per second. this.write_scalar( Scalar::from_i64(1_000_000_000), - &this.deref_operand(lpFrequency_op)?.into(), + &this.deref_operand_as(lpFrequency_op, this.machine.layouts.u64)?.into(), )?; Ok(Scalar::from_i32(-1)) // Return non-zero on success } @@ -204,7 +206,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.assert_target_os("macos", "mach_timebase_info"); - let info = this.deref_operand(info_op)?; + let info = this.deref_operand_as(info_op, this.libc_ty_layout("mach_timebase_info"))?; // Since our emulated ticks in `mach_absolute_time` *are* nanoseconds, // no scaling needs to happen. @@ -223,7 +225,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.assert_target_os_is_unix("nanosleep"); - let duration = match this.read_timespec(&this.deref_operand(req_op)?)? { + let req = this.deref_operand_as(req_op, this.libc_ty_layout("timespec"))?; + + let duration = match this.read_timespec(&req)? { Some(duration) => duration, None => { let einval = this.eval_libc("EINVAL"); diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index c371e85c31..d2b9430872 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -259,7 +259,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Thread-local storage "pthread_key_create" => { let [key, dtor] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let key_place = this.deref_operand(key)?; + let key_place = this.deref_operand_as(key, this.libc_ty_layout("pthread_key_t"))?; let dtor = this.read_pointer(dtor)?; // Extract the function type out of the signature (that seems easier than constructing it ourselves). @@ -520,7 +520,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Hence we can mostly ignore the input `attr_place`. let [attr_place, addr_place, size_place] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let _attr_place = this.deref_operand(attr_place)?; + let _attr_place = this.deref_operand_as(attr_place, this.libc_ty_layout("pthread_attr_t"))?; let addr_place = this.deref_operand(addr_place)?; let size_place = this.deref_operand(size_place)?; @@ -563,7 +563,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.check_no_isolation("`getpwuid_r`")?; let uid = this.read_scalar(uid)?.to_u32()?; - let pwd = this.deref_operand(pwd)?; + let pwd = this.deref_operand_as(pwd, this.libc_ty_layout("passwd"))?; let buf = this.read_pointer(buf)?; let buflen = this.read_target_usize(buflen)?; let result = this.deref_operand(result)?; diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index d1b09cd7b5..749700aeb7 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -345,7 +345,8 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx let (created_sec, created_nsec) = metadata.created.unwrap_or((0, 0)); let (modified_sec, modified_nsec) = metadata.modified.unwrap_or((0, 0)); - let buf = this.deref_operand(buf_op)?; + let buf = this.deref_operand_as(buf_op, this.libc_ty_layout("stat"))?; + this.write_int_fields_named( &[ ("st_dev", 0), @@ -1014,15 +1015,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { return Ok(-1); } - // Under normal circumstances, we would use `deref_operand(statxbuf_op)` to produce a - // proper `MemPlace` and then write the results of this function to it. However, the - // `syscall` function is untyped. This means that all the `statx` parameters are provided - // as `isize`s instead of having the proper types. Thus, we have to recover the layout of - // `statxbuf_op` by using the `libc::statx` struct type. - let statxbuf = { - let statx_layout = this.libc_ty_layout("statx"); - MPlaceTy::from_aligned_ptr(statxbuf_ptr, statx_layout) - }; + let statxbuf = this.deref_operand_as(statxbuf_op, this.libc_ty_layout("statx"))?; let path = this.read_path_from_c_str(pathname_ptr)?.into_owned(); // See for a discussion of argument sizes. @@ -1428,7 +1421,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // pub d_name: [c_char; 1024], // } - let entry_place = this.deref_operand(entry_op)?; + let entry_place = this.deref_operand_as(entry_op, this.libc_ty_layout("dirent"))?; let name_place = this.mplace_field(&entry_place, 5)?; let file_name = dir_entry.file_name(); // not a Path as there are no separators! @@ -1444,8 +1437,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ); } - let entry_place = this.deref_operand(entry_op)?; - // If the host is a Unix system, fill in the inode number with its real value. // If not, use 0 as a fallback value. #[cfg(unix)] diff --git a/src/shims/unix/linux/fd.rs b/src/shims/unix/linux/fd.rs index 3c263e4df9..dc395d39ce 100644 --- a/src/shims/unix/linux/fd.rs +++ b/src/shims/unix/linux/fd.rs @@ -71,7 +71,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let epoll_ctl_del = this.eval_libc_i32("EPOLL_CTL_DEL"); if op == epoll_ctl_add || op == epoll_ctl_mod { - let event = this.deref_operand(event)?; + let event = this.deref_operand_as(event, this.libc_ty_layout("epoll_event"))?; let events = this.mplace_field(&event, 0)?; let events = this.read_scalar(&events.into())?.to_u32()?; diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index 4cb7ee8efc..5985d29fab 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -191,7 +191,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; this.read_scalar(pid)?.to_i32()?; this.read_target_usize(cpusetsize)?; - this.deref_operand(mask)?; + this.deref_operand_as(mask, this.libc_ty_layout("cpu_set_t"))?; // FIXME: we just return an error; `num_cpus` then falls back to `sysconf`. let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; diff --git a/src/shims/unix/linux/sync.rs b/src/shims/unix/linux/sync.rs index 6889c43004..e26e29362a 100644 --- a/src/shims/unix/linux/sync.rs +++ b/src/shims/unix/linux/sync.rs @@ -85,9 +85,8 @@ pub fn futex<'tcx>( return Ok(()); } - // `deref_operand` but not actually dereferencing the ptr yet (it might be NULL!). - let timeout = this.ref_to_mplace(&this.read_immediate(&args[3])?)?; - let timeout_time = if this.ptr_is_null(timeout.ptr)? { + let timeout_op = &args[3]; + let timeout_time = if this.ptr_is_null(this.read_pointer(timeout_op)?)? { None } else { let realtime = op & futex_realtime == futex_realtime; @@ -96,6 +95,7 @@ pub fn futex<'tcx>( "`futex` syscall with `op=FUTEX_WAIT` and non-null timeout with `FUTEX_CLOCK_REALTIME`", )?; } + let timeout = this.deref_operand_as(timeout_op, this.libc_ty_layout("timespec"))?; let duration = match this.read_timespec(&timeout)? { Some(duration) => duration, None => { diff --git a/src/shims/unix/sync.rs b/src/shims/unix/sync.rs index 05feeac45b..94b831da5c 100644 --- a/src/shims/unix/sync.rs +++ b/src/shims/unix/sync.rs @@ -36,7 +36,13 @@ fn mutexattr_get_kind<'mir, 'tcx: 'mir>( ecx: &MiriInterpCx<'mir, 'tcx>, attr_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { - ecx.read_scalar_at_offset(attr_op, 0, ecx.machine.layouts.i32)?.to_i32() + ecx.read_scalar_at_offset( + attr_op, + 0, + ecx.libc_ty_layout("pthread_mutexattr_t"), + ecx.machine.layouts.i32, + )? + .to_i32() } fn mutexattr_set_kind<'mir, 'tcx: 'mir>( @@ -44,7 +50,13 @@ fn mutexattr_set_kind<'mir, 'tcx: 'mir>( attr_op: &OpTy<'tcx, Provenance>, kind: i32, ) -> InterpResult<'tcx, ()> { - ecx.write_scalar_at_offset(attr_op, 0, Scalar::from_i32(kind), ecx.machine.layouts.i32) + ecx.write_scalar_at_offset( + attr_op, + 0, + Scalar::from_i32(kind), + ecx.libc_ty_layout("pthread_mutexattr_t"), + ecx.machine.layouts.i32, + ) } // pthread_mutex_t is between 24 and 48 bytes, depending on the platform. @@ -60,14 +72,20 @@ fn mutex_get_id<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, mutex_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, MutexId> { - ecx.mutex_get_or_create_id(mutex_op, 4) + ecx.mutex_get_or_create_id(mutex_op, ecx.libc_ty_layout("pthread_mutex_t"), 4) } fn mutex_reset_id<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, mutex_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, ()> { - ecx.write_scalar_at_offset(mutex_op, 4, Scalar::from_i32(0), ecx.machine.layouts.u32) + ecx.write_scalar_at_offset( + mutex_op, + 4, + Scalar::from_i32(0), + ecx.libc_ty_layout("pthread_mutex_t"), + ecx.machine.layouts.u32, + ) } fn mutex_get_kind<'mir, 'tcx: 'mir>( @@ -75,7 +93,13 @@ fn mutex_get_kind<'mir, 'tcx: 'mir>( mutex_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 }; - ecx.read_scalar_at_offset(mutex_op, offset, ecx.machine.layouts.i32)?.to_i32() + ecx.read_scalar_at_offset( + mutex_op, + offset, + ecx.libc_ty_layout("pthread_mutex_t"), + ecx.machine.layouts.i32, + )? + .to_i32() } fn mutex_set_kind<'mir, 'tcx: 'mir>( @@ -84,7 +108,13 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>( kind: i32, ) -> InterpResult<'tcx, ()> { let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 }; - ecx.write_scalar_at_offset(mutex_op, offset, Scalar::from_i32(kind), ecx.machine.layouts.i32) + ecx.write_scalar_at_offset( + mutex_op, + offset, + Scalar::from_i32(kind), + ecx.libc_ty_layout("pthread_mutex_t"), + ecx.machine.layouts.i32, + ) } // pthread_rwlock_t is between 32 and 56 bytes, depending on the platform. @@ -98,7 +128,7 @@ fn rwlock_get_id<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, rwlock_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, RwLockId> { - ecx.rwlock_get_or_create_id(rwlock_op, 4) + ecx.rwlock_get_or_create_id(rwlock_op, ecx.libc_ty_layout("pthread_rwlock_t"), 4) } // pthread_condattr_t @@ -111,7 +141,13 @@ fn condattr_get_clock_id<'mir, 'tcx: 'mir>( ecx: &MiriInterpCx<'mir, 'tcx>, attr_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { - ecx.read_scalar_at_offset(attr_op, 0, ecx.machine.layouts.i32)?.to_i32() + ecx.read_scalar_at_offset( + attr_op, + 0, + ecx.libc_ty_layout("pthread_condattr_t"), + ecx.machine.layouts.i32, + )? + .to_i32() } fn condattr_set_clock_id<'mir, 'tcx: 'mir>( @@ -119,7 +155,13 @@ fn condattr_set_clock_id<'mir, 'tcx: 'mir>( attr_op: &OpTy<'tcx, Provenance>, clock_id: i32, ) -> InterpResult<'tcx, ()> { - ecx.write_scalar_at_offset(attr_op, 0, Scalar::from_i32(clock_id), ecx.machine.layouts.i32) + ecx.write_scalar_at_offset( + attr_op, + 0, + Scalar::from_i32(clock_id), + ecx.libc_ty_layout("pthread_condattr_t"), + ecx.machine.layouts.i32, + ) } // pthread_cond_t @@ -135,21 +177,33 @@ fn cond_get_id<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, cond_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, CondvarId> { - ecx.condvar_get_or_create_id(cond_op, 4) + ecx.condvar_get_or_create_id(cond_op, ecx.libc_ty_layout("pthread_cond_t"), 4) } fn cond_reset_id<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, cond_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, ()> { - ecx.write_scalar_at_offset(cond_op, 4, Scalar::from_i32(0), ecx.machine.layouts.u32) + ecx.write_scalar_at_offset( + cond_op, + 4, + Scalar::from_i32(0), + ecx.libc_ty_layout("pthread_cond_t"), + ecx.machine.layouts.u32, + ) } fn cond_get_clock_id<'mir, 'tcx: 'mir>( ecx: &MiriInterpCx<'mir, 'tcx>, cond_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { - ecx.read_scalar_at_offset(cond_op, 8, ecx.machine.layouts.i32)?.to_i32() + ecx.read_scalar_at_offset( + cond_op, + 8, + ecx.libc_ty_layout("pthread_cond_t"), + ecx.machine.layouts.i32, + )? + .to_i32() } fn cond_set_clock_id<'mir, 'tcx: 'mir>( @@ -157,7 +211,13 @@ fn cond_set_clock_id<'mir, 'tcx: 'mir>( cond_op: &OpTy<'tcx, Provenance>, clock_id: i32, ) -> InterpResult<'tcx, ()> { - ecx.write_scalar_at_offset(cond_op, 8, Scalar::from_i32(clock_id), ecx.machine.layouts.i32) + ecx.write_scalar_at_offset( + cond_op, + 8, + Scalar::from_i32(clock_id), + ecx.libc_ty_layout("pthread_cond_t"), + ecx.machine.layouts.i32, + ) } /// Try to reacquire the mutex associated with the condition variable after we @@ -285,7 +345,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // However, the way libstd uses the pthread APIs works in our favor here, so we can get away with this. // This can always be revisited to have some external state to catch double-destroys // but not complain about the above code. See https://github.com/rust-lang/miri/pull/1933 - this.write_uninit(&this.deref_operand(attr_op)?.into())?; + this.write_uninit( + &this.deref_operand_as(attr_op, this.libc_ty_layout("pthread_mutexattr_t"))?.into(), + )?; Ok(0) } @@ -437,7 +499,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { mutex_get_id(this, mutex_op)?; // This might lead to false positives, see comment in pthread_mutexattr_destroy - this.write_uninit(&this.deref_operand(mutex_op)?.into())?; + this.write_uninit( + &this.deref_operand_as(mutex_op, this.libc_ty_layout("pthread_mutex_t"))?.into(), + )?; // FIXME: delete interpreter state associated with this mutex. Ok(0) @@ -560,7 +624,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { rwlock_get_id(this, rwlock_op)?; // This might lead to false positives, see comment in pthread_mutexattr_destroy - this.write_uninit(&this.deref_operand(rwlock_op)?.into())?; + this.write_uninit( + &this.deref_operand_as(rwlock_op, this.libc_ty_layout("pthread_rwlock_t"))?.into(), + )?; // FIXME: delete interpreter state associated with this rwlock. Ok(0) @@ -624,7 +690,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { condattr_get_clock_id(this, attr_op)?; // This might lead to false positives, see comment in pthread_mutexattr_destroy - this.write_uninit(&this.deref_operand(attr_op)?.into())?; + this.write_uninit( + &this.deref_operand_as(attr_op, this.libc_ty_layout("pthread_condattr_t"))?.into(), + )?; Ok(0) } @@ -715,7 +783,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Extract the timeout. let clock_id = cond_get_clock_id(this, cond_op)?; - let duration = match this.read_timespec(&this.deref_operand(abstime_op)?)? { + let duration = match this + .read_timespec(&this.deref_operand_as(abstime_op, this.libc_ty_layout("timespec"))?)? + { Some(duration) => duration, None => { let einval = this.eval_libc("EINVAL"); @@ -797,7 +867,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { cond_get_clock_id(this, cond_op)?; // This might lead to false positives, see comment in pthread_mutexattr_destroy - this.write_uninit(&this.deref_operand(cond_op)?.into())?; + this.write_uninit( + &this.deref_operand_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?.into(), + )?; // FIXME: delete interpreter state associated with this condvar. Ok(0) diff --git a/src/shims/unix/thread.rs b/src/shims/unix/thread.rs index 6165cfd282..30ed110ca8 100644 --- a/src/shims/unix/thread.rs +++ b/src/shims/unix/thread.rs @@ -13,7 +13,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let thread_info_place = this.deref_operand(thread)?; + let thread_info_place = this.deref_operand_as(thread, this.libc_ty_layout("pthread_t"))?; let start_routine = this.read_pointer(start_routine)?; diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index b8bb7f6245..2888424c21 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -10,8 +10,6 @@ use shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle}; use shims::windows::sync::EvalContextExt as _; use shims::windows::thread::EvalContextExt as _; -use smallvec::SmallVec; - impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn emulate_foreign_item_by_name( @@ -92,7 +90,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let buf = this.read_pointer(buf)?; let n = this.read_scalar(n)?.to_u32()?; let byte_offset = this.read_target_usize(byte_offset)?; // is actually a pointer - let io_status_block = this.deref_operand(io_status_block)?; + let io_status_block = this + .deref_operand_as(io_status_block, this.windows_ty_layout("IO_STATUS_BLOCK"))?; if byte_offset != 0 { throw_unsup_format!( @@ -187,54 +186,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Also called from `page_size` crate. let [system_info] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; - let system_info = this.deref_operand(system_info)?; + let system_info = + this.deref_operand_as(system_info, this.windows_ty_layout("SYSTEM_INFO"))?; // Initialize with `0`. this.write_bytes_ptr( system_info.ptr, iter::repeat(0u8).take(system_info.layout.size.bytes_usize()), )?; // Set selected fields. - let word_layout = this.machine.layouts.u16; - let dword_layout = this.machine.layouts.u32; - let usize_layout = this.machine.layouts.usize; - - // Using `mplace_field` is error-prone, see: https://github.com/rust-lang/miri/issues/2136. - // Pointer fields have different sizes on different targets. - // To avoid all these issue we calculate the offsets ourselves. - let field_sizes = [ - word_layout.size, // 0, wProcessorArchitecture : WORD - word_layout.size, // 1, wReserved : WORD - dword_layout.size, // 2, dwPageSize : DWORD - usize_layout.size, // 3, lpMinimumApplicationAddress : LPVOID - usize_layout.size, // 4, lpMaximumApplicationAddress : LPVOID - usize_layout.size, // 5, dwActiveProcessorMask : DWORD_PTR - dword_layout.size, // 6, dwNumberOfProcessors : DWORD - dword_layout.size, // 7, dwProcessorType : DWORD - dword_layout.size, // 8, dwAllocationGranularity : DWORD - word_layout.size, // 9, wProcessorLevel : WORD - word_layout.size, // 10, wProcessorRevision : WORD - ]; - let field_offsets: SmallVec<[Size; 11]> = field_sizes - .iter() - .copied() - .scan(Size::ZERO, |a, x| { - let res = Some(*a); - *a = a.checked_add(x, this).unwrap(); - res - }) - .collect(); - - // Set page size. - let page_size = system_info.offset(field_offsets[2], dword_layout, &this.tcx)?; - this.write_scalar( - Scalar::from_int(this.machine.page_size, dword_layout.size), - &page_size.into(), - )?; - // Set number of processors. - let num_cpus = system_info.offset(field_offsets[6], dword_layout, &this.tcx)?; - this.write_scalar( - Scalar::from_int(this.machine.num_cpus, dword_layout.size), - &num_cpus.into(), + this.write_int_fields_named( + &[ + ("dwPageSize", this.machine.page_size.into()), + ("dwNumberOfProcessors", this.machine.num_cpus.into()), + ], + &system_info, )?; } @@ -426,6 +391,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [console, buffer_info] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; this.read_target_isize(console)?; + // FIXME: this should use deref_operand_as, but CONSOLE_SCREEN_BUFFER_INFO is not in std this.deref_operand(buffer_info)?; // Indicate an error. // FIXME: we should set last_error, but to what? diff --git a/src/shims/windows/sync.rs b/src/shims/windows/sync.rs index b9cc3e15be..a2db9441cf 100644 --- a/src/shims/windows/sync.rs +++ b/src/shims/windows/sync.rs @@ -7,10 +7,6 @@ use crate::concurrency::sync::{CondvarLock, RwLockMode}; use crate::concurrency::thread::MachineCallback; use crate::*; -const SRWLOCK_ID_OFFSET: u64 = 0; -const INIT_ONCE_ID_OFFSET: u64 = 0; -const CONDVAR_ID_OFFSET: u64 = 0; - impl<'mir, 'tcx> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Try to reacquire the lock associated with the condition variable after we @@ -41,6 +37,30 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } + + fn srwlock_get_id( + &mut self, + rwlock_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, RwLockId> { + let this = self.eval_context_mut(); + this.rwlock_get_or_create_id(rwlock_op, this.windows_ty_layout("SRWLOCK"), 0) + } + + fn init_once_get_id( + &mut self, + init_once_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, InitOnceId> { + let this = self.eval_context_mut(); + this.init_once_get_or_create_id(init_once_op, this.windows_ty_layout("INIT_ONCE"), 0) + } + + fn condvar_get_id( + &mut self, + condvar_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, CondvarId> { + let this = self.eval_context_mut(); + this.condvar_get_or_create_id(condvar_op, this.windows_ty_layout("CONDITION_VARIABLE"), 0) + } } impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} @@ -48,7 +68,7 @@ impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn AcquireSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(lock_op, SRWLOCK_ID_OFFSET)?; + let id = this.srwlock_get_id(lock_op)?; let active_thread = this.get_active_thread(); if this.rwlock_is_locked(id) { @@ -72,7 +92,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { lock_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(lock_op, SRWLOCK_ID_OFFSET)?; + let id = this.srwlock_get_id(lock_op)?; let active_thread = this.get_active_thread(); if this.rwlock_is_locked(id) { @@ -86,7 +106,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn ReleaseSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(lock_op, SRWLOCK_ID_OFFSET)?; + let id = this.srwlock_get_id(lock_op)?; let active_thread = this.get_active_thread(); if !this.rwlock_writer_unlock(id, active_thread) { @@ -101,7 +121,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn AcquireSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(lock_op, SRWLOCK_ID_OFFSET)?; + let id = this.srwlock_get_id(lock_op)?; let active_thread = this.get_active_thread(); if this.rwlock_is_write_locked(id) { @@ -118,7 +138,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { lock_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(lock_op, SRWLOCK_ID_OFFSET)?; + let id = this.srwlock_get_id(lock_op)?; let active_thread = this.get_active_thread(); if this.rwlock_is_write_locked(id) { @@ -131,7 +151,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn ReleaseSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(lock_op, SRWLOCK_ID_OFFSET)?; + let id = this.srwlock_get_id(lock_op)?; let active_thread = this.get_active_thread(); if !this.rwlock_reader_unlock(id, active_thread) { @@ -154,7 +174,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let active_thread = this.get_active_thread(); - let id = this.init_once_get_or_create_id(init_once_op, INIT_ONCE_ID_OFFSET)?; + let id = this.init_once_get_id(init_once_op)?; let flags = this.read_scalar(flags_op)?.to_u32()?; let pending_place = this.deref_operand(pending_op)?.into(); let context = this.read_pointer(context_op)?; @@ -229,7 +249,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = this.init_once_get_or_create_id(init_once_op, INIT_ONCE_ID_OFFSET)?; + let id = this.init_once_get_id(init_once_op)?; let flags = this.read_scalar(flags_op)?.to_u32()?; let context = this.read_pointer(context_op)?; @@ -372,8 +392,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let condvar_id = this.condvar_get_or_create_id(condvar_op, CONDVAR_ID_OFFSET)?; - let lock_id = this.rwlock_get_or_create_id(lock_op, SRWLOCK_ID_OFFSET)?; + let condvar_id = this.condvar_get_id(condvar_op)?; + let lock_id = this.srwlock_get_id(lock_op)?; let timeout_ms = this.read_scalar(timeout_op)?.to_u32()?; let flags = this.read_scalar(flags_op)?.to_u32()?; @@ -456,7 +476,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn WakeConditionVariable(&mut self, condvar_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let condvar_id = this.condvar_get_or_create_id(condvar_op, CONDVAR_ID_OFFSET)?; + let condvar_id = this.condvar_get_id(condvar_op)?; if let Some((thread, lock)) = this.condvar_signal(condvar_id) { if let CondvarLock::RwLock { id, mode } = lock { @@ -475,7 +495,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { condvar_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let condvar_id = this.condvar_get_or_create_id(condvar_op, CONDVAR_ID_OFFSET)?; + let condvar_id = this.condvar_get_id(condvar_op)?; while let Some((thread, lock)) = this.condvar_signal(condvar_id) { if let CondvarLock::RwLock { id, mode } = lock { From c6eec529ea552db6499ea68db54c5e39f66edb5f Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Fri, 2 Jun 2023 16:24:41 -0700 Subject: [PATCH 02/32] add deref_pointer_as --- src/helpers.rs | 14 ++++++++++++++ src/shims/unix/linux/sync.rs | 8 +++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index ae5a975bb8..c19d691ede 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -746,6 +746,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(mplace) } + fn deref_pointer_as( + &self, + val: &ImmTy<'tcx, Provenance>, + layout: TyAndLayout<'tcx>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { + let this = self.eval_context_ref(); + let mut mplace = this.ref_to_mplace(val)?; + + mplace.layout = layout; + mplace.align = layout.align.abi; + + Ok(mplace) + } + /// Calculates the MPlaceTy given the offset and layout of an access on an operand fn deref_operand_and_offset( &self, diff --git a/src/shims/unix/linux/sync.rs b/src/shims/unix/linux/sync.rs index e26e29362a..873b84e305 100644 --- a/src/shims/unix/linux/sync.rs +++ b/src/shims/unix/linux/sync.rs @@ -85,8 +85,11 @@ pub fn futex<'tcx>( return Ok(()); } - let timeout_op = &args[3]; - let timeout_time = if this.ptr_is_null(this.read_pointer(timeout_op)?)? { + let timeout = this.deref_pointer_as( + &this.read_immediate(&args[3])?, + this.libc_ty_layout("timespec"), + )?; + let timeout_time = if this.ptr_is_null(timeout.ptr)? { None } else { let realtime = op & futex_realtime == futex_realtime; @@ -95,7 +98,6 @@ pub fn futex<'tcx>( "`futex` syscall with `op=FUTEX_WAIT` and non-null timeout with `FUTEX_CLOCK_REALTIME`", )?; } - let timeout = this.deref_operand_as(timeout_op, this.libc_ty_layout("timespec"))?; let duration = match this.read_timespec(&timeout)? { Some(duration) => duration, None => { From d537ab9180dedb8f5975b7e88130971ef7fe2a3b Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Fri, 2 Jun 2023 17:30:05 -0700 Subject: [PATCH 03/32] explain windows sync layouts --- src/shims/windows/sync.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/shims/windows/sync.rs b/src/shims/windows/sync.rs index a2db9441cf..172312e331 100644 --- a/src/shims/windows/sync.rs +++ b/src/shims/windows/sync.rs @@ -38,6 +38,9 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } + // Windows sync primitives are pointer sized. + // We only use the first 4 bytes for the id. + fn srwlock_get_id( &mut self, rwlock_op: &OpTy<'tcx, Provenance>, From 05640c48b11508a33b471d5e00f0f8072d8dba58 Mon Sep 17 00:00:00 2001 From: Neven Villani Date: Wed, 10 May 2023 15:29:31 +0200 Subject: [PATCH 04/32] Select more TB fail tests - reorganize tests/ structure: {stacked,tree,both}_borrows - UnsafeCell transmutation (the one that should fail, i.e. transmute & -> UnsafeCell then try to write) - select TB pass tests from existing SB fail tests (and a version that fails TB) - many fail tests now shared * extra test for TB that parent write invalidates child reads * buggy_* tests now shared * tests for deep retagging (pass_invalid_shr_*) now shared * extra TB test that shared references are read-only * aliasing_mut{1,2,3,4} adapted to fail both * extra TB test that write to raw parent invalidates shared children * mut_exclusive_violation2 now shared * issue-miri-1050-2 revisions fix - deduplications --- .../alias_through_mutation.rs | 7 +- .../alias_through_mutation.stack.stderr} | 0 .../alias_through_mutation.tree.stderr | 32 ++++++++ .../aliasing_mut1.rs | 8 +- .../aliasing_mut1.stack.stderr} | 8 +- .../both_borrows/aliasing_mut1.tree.stderr | 31 +++++++ .../aliasing_mut2.rs | 8 +- .../aliasing_mut2.stack.stderr} | 8 +- .../both_borrows/aliasing_mut2.tree.stderr | 31 +++++++ .../aliasing_mut3.rs | 8 +- .../aliasing_mut3.stack.stderr} | 10 +-- .../both_borrows/aliasing_mut3.tree.stderr | 31 +++++++ .../aliasing_mut4.rs | 9 ++- .../aliasing_mut4.stack.stderr} | 8 +- .../both_borrows/aliasing_mut4.tree.stderr | 39 +++++++++ .../box_noalias_violation.rs | 7 +- .../box_noalias_violation.stack.stderr} | 0 .../box_noalias_violation.tree.stderr | 38 +++++++++ .../buggy_as_mut_slice.rs | 8 +- .../buggy_as_mut_slice.stack.stderr} | 0 .../buggy_as_mut_slice.tree.stderr | 32 ++++++++ .../buggy_split_at_mut.rs | 5 +- .../buggy_split_at_mut.stack.stderr} | 0 .../buggy_split_at_mut.tree.stderr | 32 ++++++++ tests/fail/both_borrows/illegal_write1.rs | 14 ++++ .../illegal_write1.stack.stderr} | 0 .../both_borrows/illegal_write1.tree.stderr | 20 +++++ .../illegal_write5.rs | 6 +- .../illegal_write5.stack.stderr} | 0 .../both_borrows/illegal_write5.tree.stderr | 32 ++++++++ tests/fail/both_borrows/illegal_write6.rs | 17 ++++ .../illegal_write6.stack.stderr} | 0 .../both_borrows/illegal_write6.tree.stderr | 38 +++++++++ .../invalidate_against_protector2.rs | 7 +- ...nvalidate_against_protector2.stack.stderr} | 0 .../invalidate_against_protector2.tree.stderr | 32 ++++++++ .../invalidate_against_protector3.rs | 7 +- ...nvalidate_against_protector3.stack.stderr} | 0 .../invalidate_against_protector3.tree.stderr | 32 ++++++++ .../issue-miri-1050-1.rs | 2 + .../issue-miri-1050-1.stack.stderr} | 0 .../issue-miri-1050-1.tree.stderr | 21 +++++ .../issue-miri-1050-2.rs | 2 + .../issue-miri-1050-2.stack.stderr} | 0 .../issue-miri-1050-2.tree.stderr | 21 +++++ .../load_invalid_shr.rs | 6 +- .../load_invalid_shr.stack.stderr} | 0 .../both_borrows/load_invalid_shr.tree.stderr | 32 ++++++++ .../mut_exclusive_violation1.rs | 7 +- .../mut_exclusive_violation1.stack.stderr} | 0 .../mut_exclusive_violation1.tree.stderr | 42 ++++++++++ .../both_borrows/mut_exclusive_violation2.rs | 16 ++++ .../mut_exclusive_violation2.stack.stderr} | 4 +- .../mut_exclusive_violation2.tree.stderr | 32 ++++++++ .../newtype_pair_retagging.rs | 6 +- .../newtype_pair_retagging.stack.stderr} | 0 .../newtype_pair_retagging.tree.stderr | 55 +++++++++++++ .../newtype_retagging.rs | 7 +- .../newtype_retagging.stack.stderr} | 0 .../newtype_retagging.tree.stderr | 55 +++++++++++++ tests/fail/both_borrows/outdated_local.rs | 14 ++++ .../outdated_local.stack.stderr} | 0 .../both_borrows/outdated_local.tree.stderr | 26 ++++++ .../pass_invalid_shr.rs | 7 +- .../pass_invalid_shr.stack.stderr} | 0 .../both_borrows/pass_invalid_shr.tree.stderr | 26 ++++++ .../both_borrows/pass_invalid_shr_option.rs | 15 ++++ .../pass_invalid_shr_option.stack.stderr | 28 +++++++ .../pass_invalid_shr_option.tree.stderr | 32 ++++++++ .../both_borrows/pass_invalid_shr_tuple.rs | 16 ++++ .../pass_invalid_shr_tuple.stack.stderr | 28 +++++++ .../pass_invalid_shr_tuple.tree.stderr | 32 ++++++++ .../return_invalid_shr.rs | 7 +- .../return_invalid_shr.stack.stderr} | 0 .../return_invalid_shr.tree.stderr | 31 +++++++ .../return_invalid_shr_option.rs | 7 +- .../return_invalid_shr_option.stack.stderr} | 0 .../return_invalid_shr_option.tree.stderr | 37 +++++++++ .../return_invalid_shr_tuple.rs | 7 +- .../return_invalid_shr_tuple.stack.stderr} | 0 .../return_invalid_shr_tuple.tree.stderr | 37 +++++++++ .../both_borrows/shr_frozen_violation1.rs | 22 +++++ .../shr_frozen_violation1.stack.stderr} | 0 .../shr_frozen_violation1.tree.stderr | 36 +++++++++ .../both_borrows/shr_frozen_violation2.rs | 15 ++++ .../shr_frozen_violation2.stack.stderr | 28 +++++++ .../shr_frozen_violation2.tree.stderr | 26 ++++++ tests/fail/stacked_borrows/illegal_write1.rs | 9 --- tests/fail/stacked_borrows/illegal_write6.rs | 12 --- .../mut_exclusive_violation2.rs | 12 --- tests/fail/stacked_borrows/outdated_local.rs | 9 --- .../stacked_borrows/shr_frozen_violation1.rs | 17 ---- .../alternate-read-write.rs | 0 .../alternate-read-write.stderr | 0 .../error-range.rs | 0 .../error-range.stderr | 0 .../fail/tree_borrows/fnentry_invalidation.rs | 29 +++++++ .../tree_borrows/fnentry_invalidation.stderr | 32 ++++++++ .../fragile-data-race.rs | 0 .../fragile-data-race.stderr | 0 .../outside-range.rs | 0 .../outside-range.stderr | 0 .../parent_read_freezes_raw_mut.rs} | 4 +- .../parent_read_freezes_raw_mut.stderr} | 10 +-- tests/fail/tree_borrows/pass_invalid_mut.rs | 20 +++++ .../fail/tree_borrows/pass_invalid_mut.stderr | 43 ++++++++++ .../reserved/cell-protected-write.rs | 0 .../reserved/cell-protected-write.stderr | 0 .../reserved/int-protected-write.rs | 0 .../reserved/int-protected-write.stderr | 0 .../retag-data-race.rs | 0 .../retag-data-race.stderr | 0 tests/fail/tree_borrows/return_invalid_mut.rs | 22 +++++ .../tree_borrows/return_invalid_mut.stderr | 38 +++++++++ .../strongly-protected.rs | 0 .../strongly-protected.stderr | 0 .../write-during-2phase.rs | 0 .../write-during-2phase.stderr | 0 tests/fail/tree_borrows/write_to_shr.stderr | 26 ++++++ .../pass/tree-borrows/transmute-unsafecell.rs | 13 --- .../2phase-interiormut.rs | 0 .../cell-alternate-writes.rs | 0 .../cell-alternate-writes.stderr | 0 .../copy-nonoverlapping.rs | 0 .../end-of-protector.rs | 0 .../end-of-protector.stderr | 0 .../formatting.rs | 0 .../formatting.stderr | 0 .../reborrow-is-read.rs | 0 .../reborrow-is-read.stderr | 0 .../reserved.rs | 0 .../reserved.stderr | 0 tests/pass/tree_borrows/sb_fails.rs | 81 +++++++++++++++++++ .../pass/tree_borrows/transmute-unsafecell.rs | 32 ++++++++ .../tree-borrows.rs | 0 135 files changed, 1641 insertions(+), 118 deletions(-) rename tests/fail/{stacked_borrows => both_borrows}/alias_through_mutation.rs (63%) rename tests/fail/{stacked_borrows/alias_through_mutation.stderr => both_borrows/alias_through_mutation.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/alias_through_mutation.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/aliasing_mut1.rs (68%) rename tests/fail/{stacked_borrows/aliasing_mut1.stderr => both_borrows/aliasing_mut1.stack.stderr} (79%) create mode 100644 tests/fail/both_borrows/aliasing_mut1.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/aliasing_mut2.rs (62%) rename tests/fail/{stacked_borrows/aliasing_mut2.stderr => both_borrows/aliasing_mut2.stack.stderr} (79%) create mode 100644 tests/fail/both_borrows/aliasing_mut2.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/aliasing_mut3.rs (61%) rename tests/fail/{stacked_borrows/aliasing_mut3.stderr => both_borrows/aliasing_mut3.stack.stderr} (76%) create mode 100644 tests/fail/both_borrows/aliasing_mut3.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/aliasing_mut4.rs (65%) rename tests/fail/{stacked_borrows/aliasing_mut4.stderr => both_borrows/aliasing_mut4.stack.stderr} (78%) create mode 100644 tests/fail/both_borrows/aliasing_mut4.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/box_noalias_violation.rs (61%) rename tests/fail/{stacked_borrows/box_noalias_violation.stderr => both_borrows/box_noalias_violation.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/box_noalias_violation.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/buggy_as_mut_slice.rs (52%) rename tests/fail/{stacked_borrows/buggy_as_mut_slice.stderr => both_borrows/buggy_as_mut_slice.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/buggy_split_at_mut.rs (74%) rename tests/fail/{stacked_borrows/buggy_split_at_mut.stderr => both_borrows/buggy_split_at_mut.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/buggy_split_at_mut.tree.stderr create mode 100644 tests/fail/both_borrows/illegal_write1.rs rename tests/fail/{stacked_borrows/illegal_write1.stderr => both_borrows/illegal_write1.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/illegal_write1.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/illegal_write5.rs (66%) rename tests/fail/{stacked_borrows/illegal_write5.stderr => both_borrows/illegal_write5.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/illegal_write5.tree.stderr create mode 100644 tests/fail/both_borrows/illegal_write6.rs rename tests/fail/{stacked_borrows/illegal_write6.stderr => both_borrows/illegal_write6.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/illegal_write6.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/invalidate_against_protector2.rs (66%) rename tests/fail/{stacked_borrows/invalidate_against_protector2.stderr => both_borrows/invalidate_against_protector2.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/invalidate_against_protector2.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/invalidate_against_protector3.rs (68%) rename tests/fail/{stacked_borrows/invalidate_against_protector3.stderr => both_borrows/invalidate_against_protector3.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/invalidate_against_protector3.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/issue-miri-1050-1.rs (75%) rename tests/fail/{stacked_borrows/issue-miri-1050-1.stderr => both_borrows/issue-miri-1050-1.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/issue-miri-1050-1.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/issue-miri-1050-2.rs (73%) rename tests/fail/{stacked_borrows/issue-miri-1050-2.stderr => both_borrows/issue-miri-1050-2.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/issue-miri-1050-2.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/load_invalid_shr.rs (60%) rename tests/fail/{stacked_borrows/load_invalid_shr.stderr => both_borrows/load_invalid_shr.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/load_invalid_shr.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/mut_exclusive_violation1.rs (70%) rename tests/fail/{stacked_borrows/mut_exclusive_violation1.stderr => both_borrows/mut_exclusive_violation1.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr create mode 100644 tests/fail/both_borrows/mut_exclusive_violation2.rs rename tests/fail/{stacked_borrows/mut_exclusive_violation2.stderr => both_borrows/mut_exclusive_violation2.stack.stderr} (94%) create mode 100644 tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/newtype_pair_retagging.rs (69%) rename tests/fail/{stacked_borrows/newtype_pair_retagging.stderr => both_borrows/newtype_pair_retagging.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/newtype_pair_retagging.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/newtype_retagging.rs (67%) rename tests/fail/{stacked_borrows/newtype_retagging.stderr => both_borrows/newtype_retagging.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/newtype_retagging.tree.stderr create mode 100644 tests/fail/both_borrows/outdated_local.rs rename tests/fail/{stacked_borrows/outdated_local.stderr => both_borrows/outdated_local.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/outdated_local.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/pass_invalid_shr.rs (52%) rename tests/fail/{stacked_borrows/pass_invalid_shr.stderr => both_borrows/pass_invalid_shr.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/pass_invalid_shr.tree.stderr create mode 100644 tests/fail/both_borrows/pass_invalid_shr_option.rs create mode 100644 tests/fail/both_borrows/pass_invalid_shr_option.stack.stderr create mode 100644 tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr create mode 100644 tests/fail/both_borrows/pass_invalid_shr_tuple.rs create mode 100644 tests/fail/both_borrows/pass_invalid_shr_tuple.stack.stderr create mode 100644 tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/return_invalid_shr.rs (55%) rename tests/fail/{stacked_borrows/return_invalid_shr.stderr => both_borrows/return_invalid_shr.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/return_invalid_shr.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/return_invalid_shr_option.rs (63%) rename tests/fail/{stacked_borrows/return_invalid_shr_option.stderr => both_borrows/return_invalid_shr_option.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/return_invalid_shr_option.tree.stderr rename tests/fail/{stacked_borrows => both_borrows}/return_invalid_shr_tuple.rs (58%) rename tests/fail/{stacked_borrows/return_invalid_shr_tuple.stderr => both_borrows/return_invalid_shr_tuple.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr create mode 100644 tests/fail/both_borrows/shr_frozen_violation1.rs rename tests/fail/{stacked_borrows/shr_frozen_violation1.stderr => both_borrows/shr_frozen_violation1.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/shr_frozen_violation1.tree.stderr create mode 100644 tests/fail/both_borrows/shr_frozen_violation2.rs create mode 100644 tests/fail/both_borrows/shr_frozen_violation2.stack.stderr create mode 100644 tests/fail/both_borrows/shr_frozen_violation2.tree.stderr delete mode 100644 tests/fail/stacked_borrows/illegal_write1.rs delete mode 100644 tests/fail/stacked_borrows/illegal_write6.rs delete mode 100644 tests/fail/stacked_borrows/mut_exclusive_violation2.rs delete mode 100644 tests/fail/stacked_borrows/outdated_local.rs delete mode 100644 tests/fail/stacked_borrows/shr_frozen_violation1.rs rename tests/fail/{tree-borrows => tree_borrows}/alternate-read-write.rs (100%) rename tests/fail/{tree-borrows => tree_borrows}/alternate-read-write.stderr (100%) rename tests/fail/{tree-borrows => tree_borrows}/error-range.rs (100%) rename tests/fail/{tree-borrows => tree_borrows}/error-range.stderr (100%) create mode 100644 tests/fail/tree_borrows/fnentry_invalidation.rs create mode 100644 tests/fail/tree_borrows/fnentry_invalidation.stderr rename tests/fail/{tree-borrows => tree_borrows}/fragile-data-race.rs (100%) rename tests/fail/{tree-borrows => tree_borrows}/fragile-data-race.stderr (100%) rename tests/fail/{tree-borrows => tree_borrows}/outside-range.rs (100%) rename tests/fail/{tree-borrows => tree_borrows}/outside-range.stderr (100%) rename tests/fail/{tree-borrows/read-to-local.rs => tree_borrows/parent_read_freezes_raw_mut.rs} (69%) rename tests/fail/{tree-borrows/read-to-local.stderr => tree_borrows/parent_read_freezes_raw_mut.stderr} (83%) create mode 100644 tests/fail/tree_borrows/pass_invalid_mut.rs create mode 100644 tests/fail/tree_borrows/pass_invalid_mut.stderr rename tests/fail/{tree-borrows => tree_borrows}/reserved/cell-protected-write.rs (100%) rename tests/fail/{tree-borrows => tree_borrows}/reserved/cell-protected-write.stderr (100%) rename tests/fail/{tree-borrows => tree_borrows}/reserved/int-protected-write.rs (100%) rename tests/fail/{tree-borrows => tree_borrows}/reserved/int-protected-write.stderr (100%) rename tests/fail/{tree-borrows => tree_borrows}/retag-data-race.rs (100%) rename tests/fail/{tree-borrows => tree_borrows}/retag-data-race.stderr (100%) create mode 100644 tests/fail/tree_borrows/return_invalid_mut.rs create mode 100644 tests/fail/tree_borrows/return_invalid_mut.stderr rename tests/fail/{tree-borrows => tree_borrows}/strongly-protected.rs (100%) rename tests/fail/{tree-borrows => tree_borrows}/strongly-protected.stderr (100%) rename tests/fail/{tree-borrows => tree_borrows}/write-during-2phase.rs (100%) rename tests/fail/{tree-borrows => tree_borrows}/write-during-2phase.stderr (100%) create mode 100644 tests/fail/tree_borrows/write_to_shr.stderr delete mode 100644 tests/pass/tree-borrows/transmute-unsafecell.rs rename tests/pass/{tree-borrows => tree_borrows}/2phase-interiormut.rs (100%) rename tests/pass/{tree-borrows => tree_borrows}/cell-alternate-writes.rs (100%) rename tests/pass/{tree-borrows => tree_borrows}/cell-alternate-writes.stderr (100%) rename tests/pass/{tree-borrows => tree_borrows}/copy-nonoverlapping.rs (100%) rename tests/pass/{tree-borrows => tree_borrows}/end-of-protector.rs (100%) rename tests/pass/{tree-borrows => tree_borrows}/end-of-protector.stderr (100%) rename tests/pass/{tree-borrows => tree_borrows}/formatting.rs (100%) rename tests/pass/{tree-borrows => tree_borrows}/formatting.stderr (100%) rename tests/pass/{tree-borrows => tree_borrows}/reborrow-is-read.rs (100%) rename tests/pass/{tree-borrows => tree_borrows}/reborrow-is-read.stderr (100%) rename tests/pass/{tree-borrows => tree_borrows}/reserved.rs (100%) rename tests/pass/{tree-borrows => tree_borrows}/reserved.stderr (100%) create mode 100644 tests/pass/tree_borrows/sb_fails.rs create mode 100644 tests/pass/tree_borrows/transmute-unsafecell.rs rename tests/pass/{tree-borrows => tree_borrows}/tree-borrows.rs (100%) diff --git a/tests/fail/stacked_borrows/alias_through_mutation.rs b/tests/fail/both_borrows/alias_through_mutation.rs similarity index 63% rename from tests/fail/stacked_borrows/alias_through_mutation.rs rename to tests/fail/both_borrows/alias_through_mutation.rs index 73095bb2fc..f40bf327ca 100644 --- a/tests/fail/stacked_borrows/alias_through_mutation.rs +++ b/tests/fail/both_borrows/alias_through_mutation.rs @@ -1,3 +1,6 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + // This makes a ref that was passed to us via &mut alias with things it should not alias with fn retarget(x: &mut &u32, target: &mut u32) { unsafe { @@ -11,5 +14,7 @@ fn main() { retarget(&mut target_alias, target); // now `target_alias` points to the same thing as `target` *target = 13; - let _val = *target_alias; //~ ERROR: /read access .* tag does not exist in the borrow stack/ + let _val = *target_alias; + //~[stack]^ ERROR: /read access .* tag does not exist in the borrow stack/ + //~[tree]| ERROR: /read access through .* is forbidden/ } diff --git a/tests/fail/stacked_borrows/alias_through_mutation.stderr b/tests/fail/both_borrows/alias_through_mutation.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/alias_through_mutation.stderr rename to tests/fail/both_borrows/alias_through_mutation.stack.stderr diff --git a/tests/fail/both_borrows/alias_through_mutation.tree.stderr b/tests/fail/both_borrows/alias_through_mutation.tree.stderr new file mode 100644 index 0000000000..a7c78b3140 --- /dev/null +++ b/tests/fail/both_borrows/alias_through_mutation.tree.stderr @@ -0,0 +1,32 @@ +error: Undefined Behavior: read access through is forbidden + --> $DIR/alias_through_mutation.rs:LL:CC + | +LL | let _val = *target_alias; + | ^^^^^^^^^^^^^ read access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Disabled which forbids child read accesses +help: the accessed tag was created here + --> $DIR/alias_through_mutation.rs:LL:CC + | +LL | *x = &mut *(target as *mut _); + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: the conflicting tag was created here, in the initial state Reserved + --> $DIR/alias_through_mutation.rs:LL:CC + | +LL | retarget(&mut target_alias, target); + | ^^^^^^ +help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] + --> $DIR/alias_through_mutation.rs:LL:CC + | +LL | *target = 13; + | ^^^^^^^^^^^^ + = help: this corresponds to a loss of read and write permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/alias_through_mutation.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/aliasing_mut1.rs b/tests/fail/both_borrows/aliasing_mut1.rs similarity index 68% rename from tests/fail/stacked_borrows/aliasing_mut1.rs rename to tests/fail/both_borrows/aliasing_mut1.rs index 14a27d8e9d..a9efe17fdd 100644 --- a/tests/fail/stacked_borrows/aliasing_mut1.rs +++ b/tests/fail/both_borrows/aliasing_mut1.rs @@ -1,6 +1,12 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows use std::mem; -pub fn safe(_x: &mut i32, _y: &mut i32) {} //~ ERROR: protect +pub fn safe(x: &mut i32, y: &mut i32) { + //~[stack]^ ERROR: protect + *x = 1; //~[tree] ERROR: /write access through .* is forbidden/ + *y = 2; +} fn main() { let mut x = 0; diff --git a/tests/fail/stacked_borrows/aliasing_mut1.stderr b/tests/fail/both_borrows/aliasing_mut1.stack.stderr similarity index 79% rename from tests/fail/stacked_borrows/aliasing_mut1.stderr rename to tests/fail/both_borrows/aliasing_mut1.stack.stderr index 3ce39968cb..678211a700 100644 --- a/tests/fail/stacked_borrows/aliasing_mut1.stderr +++ b/tests/fail/both_borrows/aliasing_mut1.stack.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: not granting access to tag because that would remove [Unique for ] which is strongly protected because it is an argument of call ID --> $DIR/aliasing_mut1.rs:LL:CC | -LL | pub fn safe(_x: &mut i32, _y: &mut i32) {} - | ^^ not granting access to tag because that would remove [Unique for ] which is strongly protected because it is an argument of call ID +LL | pub fn safe(x: &mut i32, y: &mut i32) { + | ^ not granting access to tag because that would remove [Unique for ] which is strongly protected because it is an argument of call ID | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information @@ -14,8 +14,8 @@ LL | let xraw: *mut i32 = unsafe { mem::transmute(&mut x) }; help: is this argument --> $DIR/aliasing_mut1.rs:LL:CC | -LL | pub fn safe(_x: &mut i32, _y: &mut i32) {} - | ^^ +LL | pub fn safe(x: &mut i32, y: &mut i32) { + | ^ = note: BACKTRACE (of the first span): = note: inside `safe` at $DIR/aliasing_mut1.rs:LL:CC note: inside `main` diff --git a/tests/fail/both_borrows/aliasing_mut1.tree.stderr b/tests/fail/both_borrows/aliasing_mut1.tree.stderr new file mode 100644 index 0000000000..358bb8761e --- /dev/null +++ b/tests/fail/both_borrows/aliasing_mut1.tree.stderr @@ -0,0 +1,31 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/aliasing_mut1.rs:LL:CC + | +LL | *x = 1; + | ^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag has state Frozen which forbids child write accesses +help: the accessed tag was created here, in the initial state Reserved + --> $DIR/aliasing_mut1.rs:LL:CC + | +LL | pub fn safe(x: &mut i32, y: &mut i32) { + | ^ +help: the accessed tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4] + --> $DIR/aliasing_mut1.rs:LL:CC + | +LL | pub fn safe(x: &mut i32, y: &mut i32) { + | ^ + = help: this corresponds to a loss of write permissions + = note: BACKTRACE (of the first span): + = note: inside `safe` at $DIR/aliasing_mut1.rs:LL:CC +note: inside `main` + --> $DIR/aliasing_mut1.rs:LL:CC + | +LL | safe_raw(xraw, xraw); + | ^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/aliasing_mut2.rs b/tests/fail/both_borrows/aliasing_mut2.rs similarity index 62% rename from tests/fail/stacked_borrows/aliasing_mut2.rs rename to tests/fail/both_borrows/aliasing_mut2.rs index 84d901f83b..74ea2b2862 100644 --- a/tests/fail/stacked_borrows/aliasing_mut2.rs +++ b/tests/fail/both_borrows/aliasing_mut2.rs @@ -1,6 +1,12 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows use std::mem; -pub fn safe(_x: &i32, _y: &mut i32) {} //~ ERROR: protect +pub fn safe(x: &i32, y: &mut i32) { + //~[stack]^ ERROR: protect + let _v = *x; + *y = 2; //~[tree] ERROR: /write access through .* is forbidden/ +} fn main() { let mut x = 0; diff --git a/tests/fail/stacked_borrows/aliasing_mut2.stderr b/tests/fail/both_borrows/aliasing_mut2.stack.stderr similarity index 79% rename from tests/fail/stacked_borrows/aliasing_mut2.stderr rename to tests/fail/both_borrows/aliasing_mut2.stack.stderr index df4b6cf025..90134ce367 100644 --- a/tests/fail/stacked_borrows/aliasing_mut2.stderr +++ b/tests/fail/both_borrows/aliasing_mut2.stack.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: not granting access to tag because that would remove [SharedReadOnly for ] which is strongly protected because it is an argument of call ID --> $DIR/aliasing_mut2.rs:LL:CC | -LL | pub fn safe(_x: &i32, _y: &mut i32) {} - | ^^ not granting access to tag because that would remove [SharedReadOnly for ] which is strongly protected because it is an argument of call ID +LL | pub fn safe(x: &i32, y: &mut i32) { + | ^ not granting access to tag because that would remove [SharedReadOnly for ] which is strongly protected because it is an argument of call ID | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information @@ -14,8 +14,8 @@ LL | let xref = &mut x; help: is this argument --> $DIR/aliasing_mut2.rs:LL:CC | -LL | pub fn safe(_x: &i32, _y: &mut i32) {} - | ^^ +LL | pub fn safe(x: &i32, y: &mut i32) { + | ^ = note: BACKTRACE (of the first span): = note: inside `safe` at $DIR/aliasing_mut2.rs:LL:CC note: inside `main` diff --git a/tests/fail/both_borrows/aliasing_mut2.tree.stderr b/tests/fail/both_borrows/aliasing_mut2.tree.stderr new file mode 100644 index 0000000000..f485114d4e --- /dev/null +++ b/tests/fail/both_borrows/aliasing_mut2.tree.stderr @@ -0,0 +1,31 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/aliasing_mut2.rs:LL:CC + | +LL | *y = 2; + | ^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag has state Frozen which forbids child write accesses +help: the accessed tag was created here, in the initial state Reserved + --> $DIR/aliasing_mut2.rs:LL:CC + | +LL | pub fn safe(x: &i32, y: &mut i32) { + | ^ +help: the accessed tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4] + --> $DIR/aliasing_mut2.rs:LL:CC + | +LL | let _v = *x; + | ^^ + = help: this corresponds to a loss of write permissions + = note: BACKTRACE (of the first span): + = note: inside `safe` at $DIR/aliasing_mut2.rs:LL:CC +note: inside `main` + --> $DIR/aliasing_mut2.rs:LL:CC + | +LL | safe_raw(xshr, xraw); + | ^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/aliasing_mut3.rs b/tests/fail/both_borrows/aliasing_mut3.rs similarity index 61% rename from tests/fail/stacked_borrows/aliasing_mut3.rs rename to tests/fail/both_borrows/aliasing_mut3.rs index f1ba06b6e4..8cb60faf2d 100644 --- a/tests/fail/stacked_borrows/aliasing_mut3.rs +++ b/tests/fail/both_borrows/aliasing_mut3.rs @@ -1,6 +1,12 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows use std::mem; -pub fn safe(_x: &mut i32, _y: &i32) {} //~ ERROR: borrow stack +pub fn safe(x: &mut i32, y: &i32) { + //~[stack]^ ERROR: borrow stack + *x = 1; //~[tree] ERROR: /write access through .* is forbidden/ + let _v = *y; +} fn main() { let mut x = 0; diff --git a/tests/fail/stacked_borrows/aliasing_mut3.stderr b/tests/fail/both_borrows/aliasing_mut3.stack.stderr similarity index 76% rename from tests/fail/stacked_borrows/aliasing_mut3.stderr rename to tests/fail/both_borrows/aliasing_mut3.stack.stderr index ae54d0248d..a457bd9a6a 100644 --- a/tests/fail/stacked_borrows/aliasing_mut3.stderr +++ b/tests/fail/both_borrows/aliasing_mut3.stack.stderr @@ -1,11 +1,11 @@ error: Undefined Behavior: trying to retag from for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location --> $DIR/aliasing_mut3.rs:LL:CC | -LL | pub fn safe(_x: &mut i32, _y: &i32) {} - | ^^ - | | - | trying to retag from for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location - | this error occurs as part of function-entry retag at ALLOC[0x0..0x4] +LL | pub fn safe(x: &mut i32, y: &i32) { + | ^ + | | + | trying to retag from for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | this error occurs as part of function-entry retag at ALLOC[0x0..0x4] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information diff --git a/tests/fail/both_borrows/aliasing_mut3.tree.stderr b/tests/fail/both_borrows/aliasing_mut3.tree.stderr new file mode 100644 index 0000000000..f1d89074ad --- /dev/null +++ b/tests/fail/both_borrows/aliasing_mut3.tree.stderr @@ -0,0 +1,31 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/aliasing_mut3.rs:LL:CC + | +LL | *x = 1; + | ^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag has state Frozen which forbids child write accesses +help: the accessed tag was created here, in the initial state Reserved + --> $DIR/aliasing_mut3.rs:LL:CC + | +LL | pub fn safe(x: &mut i32, y: &i32) { + | ^ +help: the accessed tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4] + --> $DIR/aliasing_mut3.rs:LL:CC + | +LL | pub fn safe(x: &mut i32, y: &i32) { + | ^ + = help: this corresponds to a loss of write permissions + = note: BACKTRACE (of the first span): + = note: inside `safe` at $DIR/aliasing_mut3.rs:LL:CC +note: inside `main` + --> $DIR/aliasing_mut3.rs:LL:CC + | +LL | safe_raw(xraw, xshr); + | ^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/aliasing_mut4.rs b/tests/fail/both_borrows/aliasing_mut4.rs similarity index 65% rename from tests/fail/stacked_borrows/aliasing_mut4.rs rename to tests/fail/both_borrows/aliasing_mut4.rs index 52081b5622..e188a1f0c3 100644 --- a/tests/fail/stacked_borrows/aliasing_mut4.rs +++ b/tests/fail/both_borrows/aliasing_mut4.rs @@ -1,8 +1,15 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows +//@[tree]error-in-other-file: /write access through .* is forbidden/ use std::cell::Cell; use std::mem; // Make sure &mut UnsafeCell also is exclusive -pub fn safe(_x: &i32, _y: &mut Cell) {} //~ ERROR: protect +pub fn safe(x: &i32, y: &mut Cell) { + //~[stack]^ ERROR: protect + y.set(1); + let _ = *x; +} fn main() { let mut x = 0; diff --git a/tests/fail/stacked_borrows/aliasing_mut4.stderr b/tests/fail/both_borrows/aliasing_mut4.stack.stderr similarity index 78% rename from tests/fail/stacked_borrows/aliasing_mut4.stderr rename to tests/fail/both_borrows/aliasing_mut4.stack.stderr index ddf197bc63..b53ae9bd55 100644 --- a/tests/fail/stacked_borrows/aliasing_mut4.stderr +++ b/tests/fail/both_borrows/aliasing_mut4.stack.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: not granting access to tag because that would remove [SharedReadOnly for ] which is strongly protected because it is an argument of call ID --> $DIR/aliasing_mut4.rs:LL:CC | -LL | pub fn safe(_x: &i32, _y: &mut Cell) {} - | ^^ not granting access to tag because that would remove [SharedReadOnly for ] which is strongly protected because it is an argument of call ID +LL | pub fn safe(x: &i32, y: &mut Cell) { + | ^ not granting access to tag because that would remove [SharedReadOnly for ] which is strongly protected because it is an argument of call ID | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information @@ -14,8 +14,8 @@ LL | let xref = &mut x; help: is this argument --> $DIR/aliasing_mut4.rs:LL:CC | -LL | pub fn safe(_x: &i32, _y: &mut Cell) {} - | ^^ +LL | pub fn safe(x: &i32, y: &mut Cell) { + | ^ = note: BACKTRACE (of the first span): = note: inside `safe` at $DIR/aliasing_mut4.rs:LL:CC note: inside `main` diff --git a/tests/fail/both_borrows/aliasing_mut4.tree.stderr b/tests/fail/both_borrows/aliasing_mut4.tree.stderr new file mode 100644 index 0000000000..f620d8cd51 --- /dev/null +++ b/tests/fail/both_borrows/aliasing_mut4.tree.stderr @@ -0,0 +1,39 @@ +error: Undefined Behavior: write access through is forbidden + --> RUSTLIB/core/src/mem/mod.rs:LL:CC + | +LL | ptr::write(dest, src); + | ^^^^^^^^^^^^^^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) + = help: the access would cause the protected tag to transition from Frozen to Disabled + = help: this is a loss of read permissions, which is not allowed for protected tags +help: the accessed tag was created here + --> $DIR/aliasing_mut4.rs:LL:CC + | +LL | y.set(1); + | ^^^^^^^^ +help: the protected tag was created here, in the initial state Frozen + --> $DIR/aliasing_mut4.rs:LL:CC + | +LL | pub fn safe(x: &i32, y: &mut Cell) { + | ^ + = note: BACKTRACE (of the first span): + = note: inside `std::mem::replace::` at RUSTLIB/core/src/mem/mod.rs:LL:CC + = note: inside `std::cell::Cell::::replace` at RUSTLIB/core/src/cell.rs:LL:CC + = note: inside `std::cell::Cell::::set` at RUSTLIB/core/src/cell.rs:LL:CC +note: inside `safe` + --> $DIR/aliasing_mut4.rs:LL:CC + | +LL | y.set(1); + | ^^^^^^^^ +note: inside `main` + --> $DIR/aliasing_mut4.rs:LL:CC + | +LL | safe_raw(xshr, xraw as *mut _); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/box_noalias_violation.rs b/tests/fail/both_borrows/box_noalias_violation.rs similarity index 61% rename from tests/fail/stacked_borrows/box_noalias_violation.rs rename to tests/fail/both_borrows/box_noalias_violation.rs index 2067149b6c..1d20b22b3a 100644 --- a/tests/fail/stacked_borrows/box_noalias_violation.rs +++ b/tests/fail/both_borrows/box_noalias_violation.rs @@ -1,8 +1,13 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + unsafe fn test(mut x: Box, y: *const i32) -> i32 { // We will call this in a way that x and y alias. *x = 5; std::mem::forget(x); - *y //~ERROR: weakly protected + *y + //~[stack]^ ERROR: weakly protected + //~[tree]| ERROR: /read access through .* is forbidden/ } fn main() { diff --git a/tests/fail/stacked_borrows/box_noalias_violation.stderr b/tests/fail/both_borrows/box_noalias_violation.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/box_noalias_violation.stderr rename to tests/fail/both_borrows/box_noalias_violation.stack.stderr diff --git a/tests/fail/both_borrows/box_noalias_violation.tree.stderr b/tests/fail/both_borrows/box_noalias_violation.tree.stderr new file mode 100644 index 0000000000..3c842b4463 --- /dev/null +++ b/tests/fail/both_borrows/box_noalias_violation.tree.stderr @@ -0,0 +1,38 @@ +error: Undefined Behavior: read access through is forbidden + --> $DIR/box_noalias_violation.rs:LL:CC + | +LL | *y + | ^^ read access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) + = help: the access would cause the protected tag to transition from Active to Frozen + = help: this is a loss of write permissions, which is not allowed for protected tags +help: the accessed tag was created here + --> $DIR/box_noalias_violation.rs:LL:CC + | +LL | let ptr = &mut v as *mut i32; + | ^^^^^^ +help: the protected tag was created here, in the initial state Reserved + --> $DIR/box_noalias_violation.rs:LL:CC + | +LL | unsafe fn test(mut x: Box, y: *const i32) -> i32 { + | ^^^^^ +help: the protected tag later transitioned to Active due to a child write access at offsets [0x0..0x4] + --> $DIR/box_noalias_violation.rs:LL:CC + | +LL | *x = 5; + | ^^^^^^ + = help: this corresponds to the first write to a 2-phase borrowed mutable reference + = note: BACKTRACE (of the first span): + = note: inside `test` at $DIR/box_noalias_violation.rs:LL:CC +note: inside `main` + --> $DIR/box_noalias_violation.rs:LL:CC + | +LL | test(Box::from_raw(ptr), ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/buggy_as_mut_slice.rs b/tests/fail/both_borrows/buggy_as_mut_slice.rs similarity index 52% rename from tests/fail/stacked_borrows/buggy_as_mut_slice.rs rename to tests/fail/both_borrows/buggy_as_mut_slice.rs index 8615a9a58a..6d535a14c0 100644 --- a/tests/fail/stacked_borrows/buggy_as_mut_slice.rs +++ b/tests/fail/both_borrows/buggy_as_mut_slice.rs @@ -1,3 +1,5 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows mod safe { use std::slice::from_raw_parts_mut; @@ -9,7 +11,9 @@ mod safe { fn main() { let v = vec![0, 1, 2]; let v1 = safe::as_mut_slice(&v); - let _v2 = safe::as_mut_slice(&v); + let v2 = safe::as_mut_slice(&v); v1[1] = 5; - //~^ ERROR: /write access .* tag does not exist in the borrow stack/ + //~[stack]^ ERROR: /write access .* tag does not exist in the borrow stack/ + v2[1] = 7; + //~[tree]^ ERROR: /write access through .* is forbidden/ } diff --git a/tests/fail/stacked_borrows/buggy_as_mut_slice.stderr b/tests/fail/both_borrows/buggy_as_mut_slice.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/buggy_as_mut_slice.stderr rename to tests/fail/both_borrows/buggy_as_mut_slice.stack.stderr diff --git a/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr b/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr new file mode 100644 index 0000000000..d1d643f3fb --- /dev/null +++ b/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr @@ -0,0 +1,32 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/buggy_as_mut_slice.rs:LL:CC + | +LL | v2[1] = 7; + | ^^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Disabled which forbids child write accesses +help: the accessed tag was created here + --> $DIR/buggy_as_mut_slice.rs:LL:CC + | +LL | let v2 = safe::as_mut_slice(&v); + | ^^^^^^^^^^^^^^^^^^^^^^ +help: the conflicting tag was created here, in the initial state Reserved + --> $DIR/buggy_as_mut_slice.rs:LL:CC + | +LL | unsafe { from_raw_parts_mut(self_.as_ptr() as *mut T, self_.len()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8] + --> $DIR/buggy_as_mut_slice.rs:LL:CC + | +LL | v1[1] = 5; + | ^^^^^^^^^ + = help: this corresponds to a loss of read and write permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/buggy_as_mut_slice.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/buggy_split_at_mut.rs b/tests/fail/both_borrows/buggy_split_at_mut.rs similarity index 74% rename from tests/fail/stacked_borrows/buggy_split_at_mut.rs rename to tests/fail/both_borrows/buggy_split_at_mut.rs index 8a1ea86d63..4dc6eaf7cd 100644 --- a/tests/fail/stacked_borrows/buggy_split_at_mut.rs +++ b/tests/fail/both_borrows/buggy_split_at_mut.rs @@ -1,3 +1,5 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows mod safe { use std::slice::from_raw_parts_mut; @@ -19,7 +21,8 @@ mod safe { fn main() { let mut array = [1, 2, 3, 4]; let (a, b) = safe::split_at_mut(&mut array, 0); - //~^ ERROR: /retag .* tag does not exist in the borrow stack/ + //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ a[1] = 5; b[1] = 6; + //~[tree]^ ERROR: /write access through .* is forbidden/ } diff --git a/tests/fail/stacked_borrows/buggy_split_at_mut.stderr b/tests/fail/both_borrows/buggy_split_at_mut.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/buggy_split_at_mut.stderr rename to tests/fail/both_borrows/buggy_split_at_mut.stack.stderr diff --git a/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr b/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr new file mode 100644 index 0000000000..3c619fe316 --- /dev/null +++ b/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr @@ -0,0 +1,32 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/buggy_split_at_mut.rs:LL:CC + | +LL | b[1] = 6; + | ^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Disabled which forbids child write accesses +help: the accessed tag was created here + --> $DIR/buggy_split_at_mut.rs:LL:CC + | +LL | let (a, b) = safe::split_at_mut(&mut array, 0); + | ^ +help: the conflicting tag was created here, in the initial state Reserved + --> $DIR/buggy_split_at_mut.rs:LL:CC + | +LL | from_raw_parts_mut(ptr.offset(mid as isize), len - mid), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8] + --> $DIR/buggy_split_at_mut.rs:LL:CC + | +LL | a[1] = 5; + | ^^^^^^^^ + = help: this corresponds to a loss of read and write permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/buggy_split_at_mut.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/both_borrows/illegal_write1.rs b/tests/fail/both_borrows/illegal_write1.rs new file mode 100644 index 0000000000..d3991949cc --- /dev/null +++ b/tests/fail/both_borrows/illegal_write1.rs @@ -0,0 +1,14 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +fn main() { + let target = Box::new(42); // has an implicit raw + let xref = &*target; + { + let x: *mut u32 = xref as *const _ as *mut _; + unsafe { *x = 42 }; + //~[stack]^ ERROR: /write access .* tag only grants SharedReadOnly permission/ + //~[tree]| ERROR: /write access through .* is forbidden/ + } + let _x = *xref; +} diff --git a/tests/fail/stacked_borrows/illegal_write1.stderr b/tests/fail/both_borrows/illegal_write1.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/illegal_write1.stderr rename to tests/fail/both_borrows/illegal_write1.stack.stderr diff --git a/tests/fail/both_borrows/illegal_write1.tree.stderr b/tests/fail/both_borrows/illegal_write1.tree.stderr new file mode 100644 index 0000000000..749fee28df --- /dev/null +++ b/tests/fail/both_borrows/illegal_write1.tree.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/illegal_write1.rs:LL:CC + | +LL | unsafe { *x = 42 }; + | ^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag has state Frozen which forbids child write accesses +help: the accessed tag was created here, in the initial state Frozen + --> $DIR/illegal_write1.rs:LL:CC + | +LL | let xref = &*target; + | ^^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/illegal_write1.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/illegal_write5.rs b/tests/fail/both_borrows/illegal_write5.rs similarity index 66% rename from tests/fail/stacked_borrows/illegal_write5.rs rename to tests/fail/both_borrows/illegal_write5.rs index 1c655dc0a0..5de63c0f06 100644 --- a/tests/fail/stacked_borrows/illegal_write5.rs +++ b/tests/fail/both_borrows/illegal_write5.rs @@ -1,3 +1,6 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + // A callee may not write to the destination of our `&mut` without us noticing. fn main() { @@ -8,7 +11,8 @@ fn main() { callee(xraw); // ... though any use of raw value will invalidate our ref. let _val = *xref; - //~^ ERROR: /read access .* tag does not exist in the borrow stack/ + //~[stack]^ ERROR: /read access .* tag does not exist in the borrow stack/ + //~[tree]| ERROR: /read access through .* is forbidden/ } fn callee(xraw: *mut i32) { diff --git a/tests/fail/stacked_borrows/illegal_write5.stderr b/tests/fail/both_borrows/illegal_write5.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/illegal_write5.stderr rename to tests/fail/both_borrows/illegal_write5.stack.stderr diff --git a/tests/fail/both_borrows/illegal_write5.tree.stderr b/tests/fail/both_borrows/illegal_write5.tree.stderr new file mode 100644 index 0000000000..b94dc77c11 --- /dev/null +++ b/tests/fail/both_borrows/illegal_write5.tree.stderr @@ -0,0 +1,32 @@ +error: Undefined Behavior: read access through is forbidden + --> $DIR/illegal_write5.rs:LL:CC + | +LL | let _val = *xref; + | ^^^^^ read access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Disabled which forbids child read accesses +help: the accessed tag was created here + --> $DIR/illegal_write5.rs:LL:CC + | +LL | let xref = unsafe { &mut *xraw }; + | ^^^^^^^^^^ +help: the conflicting tag was created here, in the initial state Reserved + --> $DIR/illegal_write5.rs:LL:CC + | +LL | let xref = unsafe { &mut *xraw }; + | ^^^^^^^^^^ +help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] + --> $DIR/illegal_write5.rs:LL:CC + | +LL | unsafe { *xraw = 15 }; + | ^^^^^^^^^^ + = help: this corresponds to a loss of read and write permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/illegal_write5.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/both_borrows/illegal_write6.rs b/tests/fail/both_borrows/illegal_write6.rs new file mode 100644 index 0000000000..5c2dfc48e5 --- /dev/null +++ b/tests/fail/both_borrows/illegal_write6.rs @@ -0,0 +1,17 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +fn main() { + let x = &mut 0u32; + let p = x as *mut u32; + foo(x, p); +} + +fn foo(a: &mut u32, y: *mut u32) -> u32 { + *a = 1; + let _b = &*a; + unsafe { *y = 2 }; + //~[stack]^ ERROR: /not granting access .* because that would remove .* which is strongly protected/ + //~[tree]| ERROR: /write access through .* is forbidden/ + return *a; +} diff --git a/tests/fail/stacked_borrows/illegal_write6.stderr b/tests/fail/both_borrows/illegal_write6.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/illegal_write6.stderr rename to tests/fail/both_borrows/illegal_write6.stack.stderr diff --git a/tests/fail/both_borrows/illegal_write6.tree.stderr b/tests/fail/both_borrows/illegal_write6.tree.stderr new file mode 100644 index 0000000000..e808d4a0ae --- /dev/null +++ b/tests/fail/both_borrows/illegal_write6.tree.stderr @@ -0,0 +1,38 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/illegal_write6.rs:LL:CC + | +LL | unsafe { *y = 2 }; + | ^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) + = help: the access would cause the protected tag to transition from Active to Disabled + = help: this is a loss of read and write permissions, which is not allowed for protected tags +help: the accessed tag was created here + --> $DIR/illegal_write6.rs:LL:CC + | +LL | let x = &mut 0u32; + | ^^^^^^^^^ +help: the protected tag was created here, in the initial state Reserved + --> $DIR/illegal_write6.rs:LL:CC + | +LL | fn foo(a: &mut u32, y: *mut u32) -> u32 { + | ^ +help: the protected tag later transitioned to Active due to a child write access at offsets [0x0..0x4] + --> $DIR/illegal_write6.rs:LL:CC + | +LL | *a = 1; + | ^^^^^^ + = help: this corresponds to the first write to a 2-phase borrowed mutable reference + = note: BACKTRACE (of the first span): + = note: inside `foo` at $DIR/illegal_write6.rs:LL:CC +note: inside `main` + --> $DIR/illegal_write6.rs:LL:CC + | +LL | foo(x, p); + | ^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/invalidate_against_protector2.rs b/tests/fail/both_borrows/invalidate_against_protector2.rs similarity index 66% rename from tests/fail/stacked_borrows/invalidate_against_protector2.rs rename to tests/fail/both_borrows/invalidate_against_protector2.rs index f4e767302f..0cec2d7c39 100644 --- a/tests/fail/stacked_borrows/invalidate_against_protector2.rs +++ b/tests/fail/both_borrows/invalidate_against_protector2.rs @@ -1,8 +1,13 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + fn inner(x: *mut i32, _y: &i32) { // If `x` and `y` alias, retagging is fine with this... but we really // shouldn't be allowed to write to `x` at all because `y` was assumed to be // immutable for the duration of this call. - unsafe { *x = 0 }; //~ ERROR: protect + unsafe { *x = 0 }; + //~[stack]^ ERROR: protect + //~[tree]| ERROR: /write access through .* is forbidden/ } fn main() { diff --git a/tests/fail/stacked_borrows/invalidate_against_protector2.stderr b/tests/fail/both_borrows/invalidate_against_protector2.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/invalidate_against_protector2.stderr rename to tests/fail/both_borrows/invalidate_against_protector2.stack.stderr diff --git a/tests/fail/both_borrows/invalidate_against_protector2.tree.stderr b/tests/fail/both_borrows/invalidate_against_protector2.tree.stderr new file mode 100644 index 0000000000..b6744fcfa0 --- /dev/null +++ b/tests/fail/both_borrows/invalidate_against_protector2.tree.stderr @@ -0,0 +1,32 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/invalidate_against_protector2.rs:LL:CC + | +LL | unsafe { *x = 0 }; + | ^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) + = help: the access would cause the protected tag to transition from Frozen to Disabled + = help: this is a loss of read permissions, which is not allowed for protected tags +help: the accessed tag was created here + --> $DIR/invalidate_against_protector2.rs:LL:CC + | +LL | let xraw = &mut x as *mut _; + | ^^^^^^ +help: the protected tag was created here, in the initial state Frozen + --> $DIR/invalidate_against_protector2.rs:LL:CC + | +LL | fn inner(x: *mut i32, _y: &i32) { + | ^^ + = note: BACKTRACE (of the first span): + = note: inside `inner` at $DIR/invalidate_against_protector2.rs:LL:CC +note: inside `main` + --> $DIR/invalidate_against_protector2.rs:LL:CC + | +LL | inner(xraw, xref); + | ^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/invalidate_against_protector3.rs b/tests/fail/both_borrows/invalidate_against_protector3.rs similarity index 68% rename from tests/fail/stacked_borrows/invalidate_against_protector3.rs rename to tests/fail/both_borrows/invalidate_against_protector3.rs index 634eb97217..170bcf9590 100644 --- a/tests/fail/stacked_borrows/invalidate_against_protector3.rs +++ b/tests/fail/both_borrows/invalidate_against_protector3.rs @@ -1,10 +1,15 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + use std::alloc::{alloc, Layout}; fn inner(x: *mut i32, _y: &i32) { // If `x` and `y` alias, retagging is fine with this... but we really // shouldn't be allowed to write to `x` at all because `y` was assumed to be // immutable for the duration of this call. - unsafe { *x = 0 }; //~ ERROR: protect + unsafe { *x = 0 }; + //~[stack]^ ERROR: protect + //~[tree]| ERROR: /write access through .* is forbidden/ } fn main() { diff --git a/tests/fail/stacked_borrows/invalidate_against_protector3.stderr b/tests/fail/both_borrows/invalidate_against_protector3.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/invalidate_against_protector3.stderr rename to tests/fail/both_borrows/invalidate_against_protector3.stack.stderr diff --git a/tests/fail/both_borrows/invalidate_against_protector3.tree.stderr b/tests/fail/both_borrows/invalidate_against_protector3.tree.stderr new file mode 100644 index 0000000000..cfbe0463e2 --- /dev/null +++ b/tests/fail/both_borrows/invalidate_against_protector3.tree.stderr @@ -0,0 +1,32 @@ +error: Undefined Behavior: write access through (root of the allocation) is forbidden + --> $DIR/invalidate_against_protector3.rs:LL:CC + | +LL | unsafe { *x = 0 }; + | ^^^^^^ write access through (root of the allocation) is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag (root of the allocation) is foreign to the protected tag (i.e., it is not a child) + = help: the access would cause the protected tag to transition from Frozen to Disabled + = help: this is a loss of read permissions, which is not allowed for protected tags +help: the accessed tag was created here + --> $DIR/invalidate_against_protector3.rs:LL:CC + | +LL | let ptr = alloc(Layout::for_value(&0i32)) as *mut i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the protected tag was created here, in the initial state Frozen + --> $DIR/invalidate_against_protector3.rs:LL:CC + | +LL | fn inner(x: *mut i32, _y: &i32) { + | ^^ + = note: BACKTRACE (of the first span): + = note: inside `inner` at $DIR/invalidate_against_protector3.rs:LL:CC +note: inside `main` + --> $DIR/invalidate_against_protector3.rs:LL:CC + | +LL | inner(ptr, &*ptr); + | ^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/issue-miri-1050-1.rs b/tests/fail/both_borrows/issue-miri-1050-1.rs similarity index 75% rename from tests/fail/stacked_borrows/issue-miri-1050-1.rs rename to tests/fail/both_borrows/issue-miri-1050-1.rs index 075efe4941..424aace239 100644 --- a/tests/fail/stacked_borrows/issue-miri-1050-1.rs +++ b/tests/fail/both_borrows/issue-miri-1050-1.rs @@ -1,3 +1,5 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows //@error-in-other-file: pointer to 4 bytes starting at offset 0 is out-of-bounds fn main() { diff --git a/tests/fail/stacked_borrows/issue-miri-1050-1.stderr b/tests/fail/both_borrows/issue-miri-1050-1.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/issue-miri-1050-1.stderr rename to tests/fail/both_borrows/issue-miri-1050-1.stack.stderr diff --git a/tests/fail/both_borrows/issue-miri-1050-1.tree.stderr b/tests/fail/both_borrows/issue-miri-1050-1.tree.stderr new file mode 100644 index 0000000000..c69a3af293 --- /dev/null +++ b/tests/fail/both_borrows/issue-miri-1050-1.tree.stderr @@ -0,0 +1,21 @@ +error: Undefined Behavior: out-of-bounds pointer use: ALLOC has size 2, so pointer to 4 bytes starting at offset 0 is out-of-bounds + --> RUSTLIB/alloc/src/boxed.rs:LL:CC + | +LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: ALLOC has size 2, so pointer to 4 bytes starting at offset 0 is out-of-bounds + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `std::boxed::Box::::from_raw_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC + = note: inside `std::boxed::Box::::from_raw` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `main` + --> $DIR/issue-miri-1050-1.rs:LL:CC + | +LL | drop(Box::from_raw(ptr as *mut u32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/issue-miri-1050-2.rs b/tests/fail/both_borrows/issue-miri-1050-2.rs similarity index 73% rename from tests/fail/stacked_borrows/issue-miri-1050-2.rs rename to tests/fail/both_borrows/issue-miri-1050-2.rs index 1b43daa925..5c947e6414 100644 --- a/tests/fail/stacked_borrows/issue-miri-1050-2.rs +++ b/tests/fail/both_borrows/issue-miri-1050-2.rs @@ -1,3 +1,5 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows //@error-in-other-file: is a dangling pointer use std::ptr::NonNull; diff --git a/tests/fail/stacked_borrows/issue-miri-1050-2.stderr b/tests/fail/both_borrows/issue-miri-1050-2.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/issue-miri-1050-2.stderr rename to tests/fail/both_borrows/issue-miri-1050-2.stack.stderr diff --git a/tests/fail/both_borrows/issue-miri-1050-2.tree.stderr b/tests/fail/both_borrows/issue-miri-1050-2.tree.stderr new file mode 100644 index 0000000000..23d7fdcd03 --- /dev/null +++ b/tests/fail/both_borrows/issue-miri-1050-2.tree.stderr @@ -0,0 +1,21 @@ +error: Undefined Behavior: out-of-bounds pointer use: 0x4[noalloc] is a dangling pointer (it has no provenance) + --> RUSTLIB/alloc/src/boxed.rs:LL:CC + | +LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: 0x4[noalloc] is a dangling pointer (it has no provenance) + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `std::boxed::Box::::from_raw_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC + = note: inside `std::boxed::Box::::from_raw` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `main` + --> $DIR/issue-miri-1050-2.rs:LL:CC + | +LL | drop(Box::from_raw(ptr.as_ptr())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/load_invalid_shr.rs b/tests/fail/both_borrows/load_invalid_shr.rs similarity index 60% rename from tests/fail/stacked_borrows/load_invalid_shr.rs rename to tests/fail/both_borrows/load_invalid_shr.rs index 8f94cc07a2..9dc2bdac85 100644 --- a/tests/fail/stacked_borrows/load_invalid_shr.rs +++ b/tests/fail/both_borrows/load_invalid_shr.rs @@ -1,3 +1,5 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows // Make sure we catch this even without validation //@compile-flags: -Zmiri-disable-validation @@ -8,5 +10,7 @@ fn main() { let xref = unsafe { &*xraw }; let xref_in_mem = Box::new(xref); unsafe { *xraw = 42 }; // unfreeze - let _val = *xref_in_mem; //~ ERROR: /retag .* tag does not exist in the borrow stack/ + let _val = *xref_in_mem; + //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ + //~[tree]| ERROR: /read access through .* is forbidden/ } diff --git a/tests/fail/stacked_borrows/load_invalid_shr.stderr b/tests/fail/both_borrows/load_invalid_shr.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/load_invalid_shr.stderr rename to tests/fail/both_borrows/load_invalid_shr.stack.stderr diff --git a/tests/fail/both_borrows/load_invalid_shr.tree.stderr b/tests/fail/both_borrows/load_invalid_shr.tree.stderr new file mode 100644 index 0000000000..f68887f059 --- /dev/null +++ b/tests/fail/both_borrows/load_invalid_shr.tree.stderr @@ -0,0 +1,32 @@ +error: Undefined Behavior: read access through is forbidden + --> $DIR/load_invalid_shr.rs:LL:CC + | +LL | let _val = *xref_in_mem; + | ^^^^^^^^^^^^ read access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Disabled which forbids child read accesses +help: the accessed tag was created here + --> $DIR/load_invalid_shr.rs:LL:CC + | +LL | let xref_in_mem = Box::new(xref); + | ^^^^^^^^^^^^^^ +help: the conflicting tag was created here, in the initial state Frozen + --> $DIR/load_invalid_shr.rs:LL:CC + | +LL | let xref = unsafe { &*xraw }; + | ^^^^^^ +help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] + --> $DIR/load_invalid_shr.rs:LL:CC + | +LL | unsafe { *xraw = 42 }; // unfreeze + | ^^^^^^^^^^ + = help: this corresponds to a loss of read permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/load_invalid_shr.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/mut_exclusive_violation1.rs b/tests/fail/both_borrows/mut_exclusive_violation1.rs similarity index 70% rename from tests/fail/stacked_borrows/mut_exclusive_violation1.rs rename to tests/fail/both_borrows/mut_exclusive_violation1.rs index f6fcdf1acd..1bca637ae3 100644 --- a/tests/fail/stacked_borrows/mut_exclusive_violation1.rs +++ b/tests/fail/both_borrows/mut_exclusive_violation1.rs @@ -1,3 +1,6 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + fn demo_mut_advanced_unique(our: &mut i32) -> i32 { unknown_code_1(&*our); @@ -24,7 +27,9 @@ fn unknown_code_1(x: &i32) { fn unknown_code_2() { unsafe { - *LEAK = 7; //~ ERROR: /write access .* tag does not exist in the borrow stack/ + *LEAK = 7; + //~[stack]^ ERROR: /write access .* tag does not exist in the borrow stack/ + //~[tree]| ERROR: /write access through .* is forbidden/ } } diff --git a/tests/fail/stacked_borrows/mut_exclusive_violation1.stderr b/tests/fail/both_borrows/mut_exclusive_violation1.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/mut_exclusive_violation1.stderr rename to tests/fail/both_borrows/mut_exclusive_violation1.stack.stderr diff --git a/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr b/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr new file mode 100644 index 0000000000..d80cfbea54 --- /dev/null +++ b/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr @@ -0,0 +1,42 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/mut_exclusive_violation1.rs:LL:CC + | +LL | *LEAK = 7; + | ^^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Disabled which forbids child write accesses +help: the accessed tag was created here + --> $DIR/mut_exclusive_violation1.rs:LL:CC + | +LL | fn unknown_code_1(x: &i32) { + | ^ +help: the conflicting tag was created here, in the initial state Frozen + --> $DIR/mut_exclusive_violation1.rs:LL:CC + | +LL | unknown_code_1(&*our); + | ^^^^^ +help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] + --> $DIR/mut_exclusive_violation1.rs:LL:CC + | +LL | *our = 5; + | ^^^^^^^^ + = help: this corresponds to a loss of read permissions + = note: BACKTRACE (of the first span): + = note: inside `unknown_code_2` at $DIR/mut_exclusive_violation1.rs:LL:CC +note: inside `demo_mut_advanced_unique` + --> $DIR/mut_exclusive_violation1.rs:LL:CC + | +LL | unknown_code_2(); + | ^^^^^^^^^^^^^^^^ +note: inside `main` + --> $DIR/mut_exclusive_violation1.rs:LL:CC + | +LL | demo_mut_advanced_unique(&mut 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/both_borrows/mut_exclusive_violation2.rs b/tests/fail/both_borrows/mut_exclusive_violation2.rs new file mode 100644 index 0000000000..43f8b9d950 --- /dev/null +++ b/tests/fail/both_borrows/mut_exclusive_violation2.rs @@ -0,0 +1,16 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows +use std::ptr::NonNull; + +fn main() { + unsafe { + let x = &mut 0; + let mut ptr1 = NonNull::from(x); + let mut ptr2 = ptr1.clone(); + let raw1 = ptr1.as_mut(); + let raw2 = ptr2.as_mut(); + let _val = *raw1; //~[stack] ERROR: /read access .* tag does not exist in the borrow stack/ + *raw2 = 2; + *raw1 = 3; //~[tree] ERROR: /write access through .* is forbidden/ + } +} diff --git a/tests/fail/stacked_borrows/mut_exclusive_violation2.stderr b/tests/fail/both_borrows/mut_exclusive_violation2.stack.stderr similarity index 94% rename from tests/fail/stacked_borrows/mut_exclusive_violation2.stderr rename to tests/fail/both_borrows/mut_exclusive_violation2.stack.stderr index 30ce698761..258189f887 100644 --- a/tests/fail/stacked_borrows/mut_exclusive_violation2.stderr +++ b/tests/fail/both_borrows/mut_exclusive_violation2.stack.stderr @@ -17,8 +17,8 @@ LL | let raw1 = ptr1.as_mut(); help: was later invalidated at offsets [0x0..0x4] by a Unique retag --> $DIR/mut_exclusive_violation2.rs:LL:CC | -LL | let _raw2 = ptr2.as_mut(); - | ^^^^^^^^^^^^^ +LL | let raw2 = ptr2.as_mut(); + | ^^^^^^^^^^^^^ = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/mut_exclusive_violation2.rs:LL:CC diff --git a/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr b/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr new file mode 100644 index 0000000000..94ca9ffb54 --- /dev/null +++ b/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr @@ -0,0 +1,32 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/mut_exclusive_violation2.rs:LL:CC + | +LL | *raw1 = 3; + | ^^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Disabled which forbids child write accesses +help: the accessed tag was created here + --> $DIR/mut_exclusive_violation2.rs:LL:CC + | +LL | let raw1 = ptr1.as_mut(); + | ^^^^^^^^^^^^^ +help: the conflicting tag was created here, in the initial state Reserved + --> $DIR/mut_exclusive_violation2.rs:LL:CC + | +LL | let raw1 = ptr1.as_mut(); + | ^^^^^^^^^^^^^ +help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] + --> $DIR/mut_exclusive_violation2.rs:LL:CC + | +LL | *raw2 = 2; + | ^^^^^^^^^ + = help: this corresponds to a loss of read and write permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/mut_exclusive_violation2.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/newtype_pair_retagging.rs b/tests/fail/both_borrows/newtype_pair_retagging.rs similarity index 69% rename from tests/fail/stacked_borrows/newtype_pair_retagging.rs rename to tests/fail/both_borrows/newtype_pair_retagging.rs index 1ae6740924..013d722135 100644 --- a/tests/fail/stacked_borrows/newtype_pair_retagging.rs +++ b/tests/fail/both_borrows/newtype_pair_retagging.rs @@ -1,4 +1,8 @@ -//@error-in-other-file: which is strongly protected +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +//@[stack]error-in-other-file: which is strongly protected +//@[tree]error-in-other-file: /write access through .* is forbidden/ struct Newtype<'a>(&'a mut i32, i32); fn dealloc_while_running(_n: Newtype<'_>, dealloc: impl FnOnce()) { diff --git a/tests/fail/stacked_borrows/newtype_pair_retagging.stderr b/tests/fail/both_borrows/newtype_pair_retagging.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/newtype_pair_retagging.stderr rename to tests/fail/both_borrows/newtype_pair_retagging.stack.stderr diff --git a/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr b/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr new file mode 100644 index 0000000000..11ac000da9 --- /dev/null +++ b/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr @@ -0,0 +1,55 @@ +error: Undefined Behavior: write access through is forbidden + --> RUSTLIB/alloc/src/alloc.rs:LL:CC + | +LL | unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) + = help: the access would cause the protected tag to transition from Frozen to Disabled + = help: this is a loss of read permissions, which is not allowed for protected tags +help: the accessed tag was created here + --> $DIR/newtype_pair_retagging.rs:LL:CC + | +LL | || drop(Box::from_raw(ptr)), + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: the protected tag was created here, in the initial state Reserved + --> $DIR/newtype_pair_retagging.rs:LL:CC + | +LL | fn dealloc_while_running(_n: Newtype<'_>, dealloc: impl FnOnce()) { + | ^^ +help: the protected tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4] + --> $DIR/newtype_pair_retagging.rs:LL:CC + | +LL | || drop(Box::from_raw(ptr)), + | ^^^^^^^^^^^^^^^^^^ + = help: this corresponds to a loss of write permissions + = note: BACKTRACE (of the first span): + = note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `::deallocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `alloc::alloc::box_free::` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `std::ptr::drop_in_place::> - shim(Some(std::boxed::Box))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC + = note: inside `std::mem::drop::>` at RUSTLIB/core/src/mem/mod.rs:LL:CC +note: inside closure + --> $DIR/newtype_pair_retagging.rs:LL:CC + | +LL | || drop(Box::from_raw(ptr)), + | ^^^^^^^^^^^^^^^^^^^^^^^^ +note: inside `dealloc_while_running::<[closure@$DIR/newtype_pair_retagging.rs:LL:CC]>` + --> $DIR/newtype_pair_retagging.rs:LL:CC + | +LL | dealloc(); + | ^^^^^^^^^ +note: inside `main` + --> $DIR/newtype_pair_retagging.rs:LL:CC + | +LL | / dealloc_while_running( +LL | | Newtype(&mut *ptr, 0), +LL | | || drop(Box::from_raw(ptr)), +LL | | ) + | |_________^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/newtype_retagging.rs b/tests/fail/both_borrows/newtype_retagging.rs similarity index 67% rename from tests/fail/stacked_borrows/newtype_retagging.rs rename to tests/fail/both_borrows/newtype_retagging.rs index f106274b81..c711e9ca9a 100644 --- a/tests/fail/stacked_borrows/newtype_retagging.rs +++ b/tests/fail/both_borrows/newtype_retagging.rs @@ -1,4 +1,9 @@ -//@error-in-other-file: which is strongly protected +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +//@[stack]error-in-other-file: which is strongly protected +//@[tree]error-in-other-file: /write access through .* is forbidden/ + struct Newtype<'a>(&'a mut i32); fn dealloc_while_running(_n: Newtype<'_>, dealloc: impl FnOnce()) { diff --git a/tests/fail/stacked_borrows/newtype_retagging.stderr b/tests/fail/both_borrows/newtype_retagging.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/newtype_retagging.stderr rename to tests/fail/both_borrows/newtype_retagging.stack.stderr diff --git a/tests/fail/both_borrows/newtype_retagging.tree.stderr b/tests/fail/both_borrows/newtype_retagging.tree.stderr new file mode 100644 index 0000000000..c442f398fc --- /dev/null +++ b/tests/fail/both_borrows/newtype_retagging.tree.stderr @@ -0,0 +1,55 @@ +error: Undefined Behavior: write access through is forbidden + --> RUSTLIB/alloc/src/alloc.rs:LL:CC + | +LL | unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) + = help: the access would cause the protected tag to transition from Frozen to Disabled + = help: this is a loss of read permissions, which is not allowed for protected tags +help: the accessed tag was created here + --> $DIR/newtype_retagging.rs:LL:CC + | +LL | || drop(Box::from_raw(ptr)), + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: the protected tag was created here, in the initial state Reserved + --> $DIR/newtype_retagging.rs:LL:CC + | +LL | fn dealloc_while_running(_n: Newtype<'_>, dealloc: impl FnOnce()) { + | ^^ +help: the protected tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4] + --> $DIR/newtype_retagging.rs:LL:CC + | +LL | || drop(Box::from_raw(ptr)), + | ^^^^^^^^^^^^^^^^^^ + = help: this corresponds to a loss of write permissions + = note: BACKTRACE (of the first span): + = note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `::deallocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `alloc::alloc::box_free::` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `std::ptr::drop_in_place::> - shim(Some(std::boxed::Box))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC + = note: inside `std::mem::drop::>` at RUSTLIB/core/src/mem/mod.rs:LL:CC +note: inside closure + --> $DIR/newtype_retagging.rs:LL:CC + | +LL | || drop(Box::from_raw(ptr)), + | ^^^^^^^^^^^^^^^^^^^^^^^^ +note: inside `dealloc_while_running::<[closure@$DIR/newtype_retagging.rs:LL:CC]>` + --> $DIR/newtype_retagging.rs:LL:CC + | +LL | dealloc(); + | ^^^^^^^^^ +note: inside `main` + --> $DIR/newtype_retagging.rs:LL:CC + | +LL | / dealloc_while_running( +LL | | Newtype(&mut *ptr), +LL | | || drop(Box::from_raw(ptr)), +LL | | ) + | |_________^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/both_borrows/outdated_local.rs b/tests/fail/both_borrows/outdated_local.rs new file mode 100644 index 0000000000..1330496bbe --- /dev/null +++ b/tests/fail/both_borrows/outdated_local.rs @@ -0,0 +1,14 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +fn main() { + let mut x = 0; + let y: *const i32 = &x; + x = 1; // this invalidates y by reactivating the lowermost uniq borrow for this local + + assert_eq!(unsafe { *y }, 1); + //~[stack]^ ERROR: /read access .* tag does not exist in the borrow stack/ + //~[tree]| ERROR: /read access through .* is forbidden/ + + assert_eq!(x, 1); +} diff --git a/tests/fail/stacked_borrows/outdated_local.stderr b/tests/fail/both_borrows/outdated_local.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/outdated_local.stderr rename to tests/fail/both_borrows/outdated_local.stack.stderr diff --git a/tests/fail/both_borrows/outdated_local.tree.stderr b/tests/fail/both_borrows/outdated_local.tree.stderr new file mode 100644 index 0000000000..6d0087bb83 --- /dev/null +++ b/tests/fail/both_borrows/outdated_local.tree.stderr @@ -0,0 +1,26 @@ +error: Undefined Behavior: read access through is forbidden + --> $DIR/outdated_local.rs:LL:CC + | +LL | assert_eq!(unsafe { *y }, 1); + | ^^ read access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag has state Disabled which forbids child read accesses +help: the accessed tag was created here, in the initial state Frozen + --> $DIR/outdated_local.rs:LL:CC + | +LL | let y: *const i32 = &x; + | ^^ +help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] + --> $DIR/outdated_local.rs:LL:CC + | +LL | x = 1; // this invalidates y by reactivating the lowermost uniq borrow for this local + | ^^^^^ + = help: this corresponds to a loss of read permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/outdated_local.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/pass_invalid_shr.rs b/tests/fail/both_borrows/pass_invalid_shr.rs similarity index 52% rename from tests/fail/stacked_borrows/pass_invalid_shr.rs rename to tests/fail/both_borrows/pass_invalid_shr.rs index 903c273310..a49f622f69 100644 --- a/tests/fail/stacked_borrows/pass_invalid_shr.rs +++ b/tests/fail/both_borrows/pass_invalid_shr.rs @@ -1,3 +1,6 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + // Make sure that we cannot pass by argument a `&` that got already invalidated. fn foo(_: &i32) {} @@ -6,5 +9,7 @@ fn main() { let xraw = x as *mut _; let xref = unsafe { &*xraw }; unsafe { *xraw = 42 }; // unfreeze - foo(xref); //~ ERROR: /retag .* tag does not exist in the borrow stack/ + foo(xref); + //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ + //~[tree]| ERROR: /read access through .* is forbidden/ } diff --git a/tests/fail/stacked_borrows/pass_invalid_shr.stderr b/tests/fail/both_borrows/pass_invalid_shr.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/pass_invalid_shr.stderr rename to tests/fail/both_borrows/pass_invalid_shr.stack.stderr diff --git a/tests/fail/both_borrows/pass_invalid_shr.tree.stderr b/tests/fail/both_borrows/pass_invalid_shr.tree.stderr new file mode 100644 index 0000000000..c94e6c021b --- /dev/null +++ b/tests/fail/both_borrows/pass_invalid_shr.tree.stderr @@ -0,0 +1,26 @@ +error: Undefined Behavior: read access through is forbidden + --> $DIR/pass_invalid_shr.rs:LL:CC + | +LL | foo(xref); + | ^^^^ read access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag has state Disabled which forbids child read accesses +help: the accessed tag was created here, in the initial state Frozen + --> $DIR/pass_invalid_shr.rs:LL:CC + | +LL | let xref = unsafe { &*xraw }; + | ^^^^^^ +help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] + --> $DIR/pass_invalid_shr.rs:LL:CC + | +LL | unsafe { *xraw = 42 }; // unfreeze + | ^^^^^^^^^^ + = help: this corresponds to a loss of read permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/pass_invalid_shr.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/both_borrows/pass_invalid_shr_option.rs b/tests/fail/both_borrows/pass_invalid_shr_option.rs new file mode 100644 index 0000000000..0ac6f98fbc --- /dev/null +++ b/tests/fail/both_borrows/pass_invalid_shr_option.rs @@ -0,0 +1,15 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +// Make sure that we cannot pass by argument a `&` that got already invalidated. +fn foo(_: Option<&i32>) {} + +fn main() { + let x = &mut 42; + let xraw = x as *mut _; + let some_xref = unsafe { Some(&*xraw) }; + unsafe { *xraw = 42 }; // unfreeze + foo(some_xref); + //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ + //~[tree]| ERROR: /read access through .* is forbidden/ +} diff --git a/tests/fail/both_borrows/pass_invalid_shr_option.stack.stderr b/tests/fail/both_borrows/pass_invalid_shr_option.stack.stderr new file mode 100644 index 0000000000..e35d7918c0 --- /dev/null +++ b/tests/fail/both_borrows/pass_invalid_shr_option.stack.stderr @@ -0,0 +1,28 @@ +error: Undefined Behavior: trying to retag from for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + --> $DIR/pass_invalid_shr_option.rs:LL:CC + | +LL | foo(some_xref); + | ^^^^^^^^^ + | | + | trying to retag from for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | this error occurs as part of retag at ALLOC[0x0..0x4] + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information +help: was created by a SharedReadOnly retag at offsets [0x0..0x4] + --> $DIR/pass_invalid_shr_option.rs:LL:CC + | +LL | let some_xref = unsafe { Some(&*xraw) }; + | ^^^^^^^^^^^^ +help: was later invalidated at offsets [0x0..0x4] by a write access + --> $DIR/pass_invalid_shr_option.rs:LL:CC + | +LL | unsafe { *xraw = 42 }; // unfreeze + | ^^^^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/pass_invalid_shr_option.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr b/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr new file mode 100644 index 0000000000..87b4fcba4d --- /dev/null +++ b/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr @@ -0,0 +1,32 @@ +error: Undefined Behavior: read access through is forbidden + --> $DIR/pass_invalid_shr_option.rs:LL:CC + | +LL | foo(some_xref); + | ^^^^^^^^^ read access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Disabled which forbids child read accesses +help: the accessed tag was created here + --> $DIR/pass_invalid_shr_option.rs:LL:CC + | +LL | let some_xref = unsafe { Some(&*xraw) }; + | ^^^^^^^^^^^^ +help: the conflicting tag was created here, in the initial state Frozen + --> $DIR/pass_invalid_shr_option.rs:LL:CC + | +LL | let some_xref = unsafe { Some(&*xraw) }; + | ^^^^^^ +help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] + --> $DIR/pass_invalid_shr_option.rs:LL:CC + | +LL | unsafe { *xraw = 42 }; // unfreeze + | ^^^^^^^^^^ + = help: this corresponds to a loss of read permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/pass_invalid_shr_option.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/both_borrows/pass_invalid_shr_tuple.rs b/tests/fail/both_borrows/pass_invalid_shr_tuple.rs new file mode 100644 index 0000000000..d22c0f6e8e --- /dev/null +++ b/tests/fail/both_borrows/pass_invalid_shr_tuple.rs @@ -0,0 +1,16 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +// Make sure that we cannot pass by argument a `&` that got already invalidated. +fn foo(_: (&i32, &i32)) {} + +fn main() { + let x = &mut (42i32, 31i32); + let xraw0 = &mut x.0 as *mut _; + let xraw1 = &mut x.1 as *mut _; + let pair_xref = unsafe { (&*xraw0, &*xraw1) }; + unsafe { *xraw0 = 42 }; // unfreeze + foo(pair_xref); + //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ + //~[tree]| ERROR: /read access through .* is forbidden/ +} diff --git a/tests/fail/both_borrows/pass_invalid_shr_tuple.stack.stderr b/tests/fail/both_borrows/pass_invalid_shr_tuple.stack.stderr new file mode 100644 index 0000000000..10c566d061 --- /dev/null +++ b/tests/fail/both_borrows/pass_invalid_shr_tuple.stack.stderr @@ -0,0 +1,28 @@ +error: Undefined Behavior: trying to retag from for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + --> $DIR/pass_invalid_shr_tuple.rs:LL:CC + | +LL | foo(pair_xref); + | ^^^^^^^^^ + | | + | trying to retag from for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | this error occurs as part of retag at ALLOC[0x0..0x4] + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information +help: was created by a SharedReadOnly retag at offsets [0x0..0x4] + --> $DIR/pass_invalid_shr_tuple.rs:LL:CC + | +LL | let pair_xref = unsafe { (&*xraw0, &*xraw1) }; + | ^^^^^^^^^^^^^^^^^^ +help: was later invalidated at offsets [0x0..0x4] by a write access + --> $DIR/pass_invalid_shr_tuple.rs:LL:CC + | +LL | unsafe { *xraw0 = 42 }; // unfreeze + | ^^^^^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/pass_invalid_shr_tuple.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr b/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr new file mode 100644 index 0000000000..605120e59d --- /dev/null +++ b/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr @@ -0,0 +1,32 @@ +error: Undefined Behavior: read access through is forbidden + --> $DIR/pass_invalid_shr_tuple.rs:LL:CC + | +LL | foo(pair_xref); + | ^^^^^^^^^ read access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Disabled which forbids child read accesses +help: the accessed tag was created here + --> $DIR/pass_invalid_shr_tuple.rs:LL:CC + | +LL | let pair_xref = unsafe { (&*xraw0, &*xraw1) }; + | ^^^^^^^^^^^^^^^^^^ +help: the conflicting tag was created here, in the initial state Frozen + --> $DIR/pass_invalid_shr_tuple.rs:LL:CC + | +LL | let pair_xref = unsafe { (&*xraw0, &*xraw1) }; + | ^^^^^^^ +help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] + --> $DIR/pass_invalid_shr_tuple.rs:LL:CC + | +LL | unsafe { *xraw0 = 42 }; // unfreeze + | ^^^^^^^^^^^ + = help: this corresponds to a loss of read permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/pass_invalid_shr_tuple.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/return_invalid_shr.rs b/tests/fail/both_borrows/return_invalid_shr.rs similarity index 55% rename from tests/fail/stacked_borrows/return_invalid_shr.rs rename to tests/fail/both_borrows/return_invalid_shr.rs index 45526498da..99945e5699 100644 --- a/tests/fail/stacked_borrows/return_invalid_shr.rs +++ b/tests/fail/both_borrows/return_invalid_shr.rs @@ -1,9 +1,14 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + // Make sure that we cannot return a `&` that got already invalidated. fn foo(x: &mut (i32, i32)) -> &i32 { let xraw = x as *mut (i32, i32); let ret = unsafe { &(*xraw).1 }; unsafe { *xraw = (42, 23) }; // unfreeze - ret //~ ERROR: /retag .* tag does not exist in the borrow stack/ + ret + //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ + //~[tree]| ERROR: /read access through .* is forbidden/ } fn main() { diff --git a/tests/fail/stacked_borrows/return_invalid_shr.stderr b/tests/fail/both_borrows/return_invalid_shr.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/return_invalid_shr.stderr rename to tests/fail/both_borrows/return_invalid_shr.stack.stderr diff --git a/tests/fail/both_borrows/return_invalid_shr.tree.stderr b/tests/fail/both_borrows/return_invalid_shr.tree.stderr new file mode 100644 index 0000000000..1b3a5bfbd1 --- /dev/null +++ b/tests/fail/both_borrows/return_invalid_shr.tree.stderr @@ -0,0 +1,31 @@ +error: Undefined Behavior: read access through is forbidden + --> $DIR/return_invalid_shr.rs:LL:CC + | +LL | ret + | ^^^ read access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag has state Disabled which forbids child read accesses +help: the accessed tag was created here, in the initial state Frozen + --> $DIR/return_invalid_shr.rs:LL:CC + | +LL | let ret = unsafe { &(*xraw).1 }; + | ^^^^^^^^^^ +help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8] + --> $DIR/return_invalid_shr.rs:LL:CC + | +LL | unsafe { *xraw = (42, 23) }; // unfreeze + | ^^^^^^^^^^^^^^^^ + = help: this corresponds to a loss of read permissions + = note: BACKTRACE (of the first span): + = note: inside `foo` at $DIR/return_invalid_shr.rs:LL:CC +note: inside `main` + --> $DIR/return_invalid_shr.rs:LL:CC + | +LL | foo(&mut (1, 2)); + | ^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/return_invalid_shr_option.rs b/tests/fail/both_borrows/return_invalid_shr_option.rs similarity index 63% rename from tests/fail/stacked_borrows/return_invalid_shr_option.rs rename to tests/fail/both_borrows/return_invalid_shr_option.rs index 094ce33b9c..e735ce9bed 100644 --- a/tests/fail/stacked_borrows/return_invalid_shr_option.rs +++ b/tests/fail/both_borrows/return_invalid_shr_option.rs @@ -1,9 +1,14 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + // Make sure that we cannot return a `&` that got already invalidated, not even in an `Option`. fn foo(x: &mut (i32, i32)) -> Option<&i32> { let xraw = x as *mut (i32, i32); let ret = Some(unsafe { &(*xraw).1 }); unsafe { *xraw = (42, 23) }; // unfreeze - ret //~ ERROR: /retag .* tag does not exist in the borrow stack/ + ret + //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ + //~[tree]| ERROR: /read access through .* is forbidden/ } fn main() { diff --git a/tests/fail/stacked_borrows/return_invalid_shr_option.stderr b/tests/fail/both_borrows/return_invalid_shr_option.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/return_invalid_shr_option.stderr rename to tests/fail/both_borrows/return_invalid_shr_option.stack.stderr diff --git a/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr b/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr new file mode 100644 index 0000000000..4e920b34d7 --- /dev/null +++ b/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr @@ -0,0 +1,37 @@ +error: Undefined Behavior: read access through is forbidden + --> $DIR/return_invalid_shr_option.rs:LL:CC + | +LL | ret + | ^^^ read access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Disabled which forbids child read accesses +help: the accessed tag was created here + --> $DIR/return_invalid_shr_option.rs:LL:CC + | +LL | let ret = Some(unsafe { &(*xraw).1 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the conflicting tag was created here, in the initial state Frozen + --> $DIR/return_invalid_shr_option.rs:LL:CC + | +LL | let ret = Some(unsafe { &(*xraw).1 }); + | ^^^^^^^^^^ +help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8] + --> $DIR/return_invalid_shr_option.rs:LL:CC + | +LL | unsafe { *xraw = (42, 23) }; // unfreeze + | ^^^^^^^^^^^^^^^^ + = help: this corresponds to a loss of read permissions + = note: BACKTRACE (of the first span): + = note: inside `foo` at $DIR/return_invalid_shr_option.rs:LL:CC +note: inside `main` + --> $DIR/return_invalid_shr_option.rs:LL:CC + | +LL | match foo(&mut (1, 2)) { + | ^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/return_invalid_shr_tuple.rs b/tests/fail/both_borrows/return_invalid_shr_tuple.rs similarity index 58% rename from tests/fail/stacked_borrows/return_invalid_shr_tuple.rs rename to tests/fail/both_borrows/return_invalid_shr_tuple.rs index d0fd53e06a..90a4021502 100644 --- a/tests/fail/stacked_borrows/return_invalid_shr_tuple.rs +++ b/tests/fail/both_borrows/return_invalid_shr_tuple.rs @@ -1,9 +1,14 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + // Make sure that we cannot return a `&` that got already invalidated, not even in a tuple. fn foo(x: &mut (i32, i32)) -> (&i32,) { let xraw = x as *mut (i32, i32); let ret = (unsafe { &(*xraw).1 },); unsafe { *xraw = (42, 23) }; // unfreeze - ret //~ ERROR: /retag .* tag does not exist in the borrow stack/ + ret + //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ + //~[tree]| ERROR: /read access through .* is forbidden/ } fn main() { diff --git a/tests/fail/stacked_borrows/return_invalid_shr_tuple.stderr b/tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/return_invalid_shr_tuple.stderr rename to tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr diff --git a/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr b/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr new file mode 100644 index 0000000000..b60614e98f --- /dev/null +++ b/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr @@ -0,0 +1,37 @@ +error: Undefined Behavior: read access through is forbidden + --> $DIR/return_invalid_shr_tuple.rs:LL:CC + | +LL | ret + | ^^^ read access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Disabled which forbids child read accesses +help: the accessed tag was created here + --> $DIR/return_invalid_shr_tuple.rs:LL:CC + | +LL | let ret = (unsafe { &(*xraw).1 },); + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: the conflicting tag was created here, in the initial state Frozen + --> $DIR/return_invalid_shr_tuple.rs:LL:CC + | +LL | let ret = (unsafe { &(*xraw).1 },); + | ^^^^^^^^^^ +help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8] + --> $DIR/return_invalid_shr_tuple.rs:LL:CC + | +LL | unsafe { *xraw = (42, 23) }; // unfreeze + | ^^^^^^^^^^^^^^^^ + = help: this corresponds to a loss of read permissions + = note: BACKTRACE (of the first span): + = note: inside `foo` at $DIR/return_invalid_shr_tuple.rs:LL:CC +note: inside `main` + --> $DIR/return_invalid_shr_tuple.rs:LL:CC + | +LL | foo(&mut (1, 2)).0; + | ^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/both_borrows/shr_frozen_violation1.rs b/tests/fail/both_borrows/shr_frozen_violation1.rs new file mode 100644 index 0000000000..1edd7748cd --- /dev/null +++ b/tests/fail/both_borrows/shr_frozen_violation1.rs @@ -0,0 +1,22 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +#![allow(cast_ref_to_mut)] + +fn foo(x: &mut i32) -> i32 { + *x = 5; + unknown_code(&*x); + *x // must return 5 +} + +fn main() { + println!("{}", foo(&mut 0)); +} + +fn unknown_code(x: &i32) { + unsafe { + *(x as *const i32 as *mut i32) = 7; + //~[stack]^ ERROR: /write access .* only grants SharedReadOnly permission/ + //~[tree]| ERROR: /write access through .* is forbidden/ + } +} diff --git a/tests/fail/stacked_borrows/shr_frozen_violation1.stderr b/tests/fail/both_borrows/shr_frozen_violation1.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/shr_frozen_violation1.stderr rename to tests/fail/both_borrows/shr_frozen_violation1.stack.stderr diff --git a/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr b/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr new file mode 100644 index 0000000000..893e38f5b6 --- /dev/null +++ b/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr @@ -0,0 +1,36 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/shr_frozen_violation1.rs:LL:CC + | +LL | *(x as *const i32 as *mut i32) = 7; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Frozen which forbids child write accesses +help: the accessed tag was created here + --> $DIR/shr_frozen_violation1.rs:LL:CC + | +LL | fn unknown_code(x: &i32) { + | ^ +help: the conflicting tag was created here, in the initial state Frozen + --> $DIR/shr_frozen_violation1.rs:LL:CC + | +LL | unknown_code(&*x); + | ^^^ + = note: BACKTRACE (of the first span): + = note: inside `unknown_code` at $DIR/shr_frozen_violation1.rs:LL:CC +note: inside `foo` + --> $DIR/shr_frozen_violation1.rs:LL:CC + | +LL | unknown_code(&*x); + | ^^^^^^^^^^^^^^^^^ +note: inside `main` + --> $DIR/shr_frozen_violation1.rs:LL:CC + | +LL | println!("{}", foo(&mut 0)); + | ^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/both_borrows/shr_frozen_violation2.rs b/tests/fail/both_borrows/shr_frozen_violation2.rs new file mode 100644 index 0000000000..7b2b63ef21 --- /dev/null +++ b/tests/fail/both_borrows/shr_frozen_violation2.rs @@ -0,0 +1,15 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows +fn main() { + unsafe { + let mut x = 0; + let ptr = std::ptr::addr_of_mut!(x); + let frozen = &*ptr; + let _val = *frozen; + x = 1; + let _val = *frozen; + //~[stack]^ ERROR: /read access .* tag does not exist in the borrow stack/ + //~[tree]| ERROR: /read access through .* is forbidden/ + let _val = x; // silence warning + } +} diff --git a/tests/fail/both_borrows/shr_frozen_violation2.stack.stderr b/tests/fail/both_borrows/shr_frozen_violation2.stack.stderr new file mode 100644 index 0000000000..0f09359007 --- /dev/null +++ b/tests/fail/both_borrows/shr_frozen_violation2.stack.stderr @@ -0,0 +1,28 @@ +error: Undefined Behavior: attempting a read access using at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + --> $DIR/shr_frozen_violation2.rs:LL:CC + | +LL | let _val = *frozen; + | ^^^^^^^ + | | + | attempting a read access using at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | this error occurs as part of an access at ALLOC[0x0..0x4] + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information +help: was created by a SharedReadOnly retag at offsets [0x0..0x4] + --> $DIR/shr_frozen_violation2.rs:LL:CC + | +LL | let frozen = &*ptr; + | ^^^^^ +help: was later invalidated at offsets [0x0..0x4] by a write access + --> $DIR/shr_frozen_violation2.rs:LL:CC + | +LL | x = 1; + | ^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/shr_frozen_violation2.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/both_borrows/shr_frozen_violation2.tree.stderr b/tests/fail/both_borrows/shr_frozen_violation2.tree.stderr new file mode 100644 index 0000000000..5fd5ba2fe3 --- /dev/null +++ b/tests/fail/both_borrows/shr_frozen_violation2.tree.stderr @@ -0,0 +1,26 @@ +error: Undefined Behavior: read access through is forbidden + --> $DIR/shr_frozen_violation2.rs:LL:CC + | +LL | let _val = *frozen; + | ^^^^^^^ read access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag has state Disabled which forbids child read accesses +help: the accessed tag was created here, in the initial state Frozen + --> $DIR/shr_frozen_violation2.rs:LL:CC + | +LL | let frozen = &*ptr; + | ^^^^^ +help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] + --> $DIR/shr_frozen_violation2.rs:LL:CC + | +LL | x = 1; + | ^^^^^ + = help: this corresponds to a loss of read permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/shr_frozen_violation2.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/stacked_borrows/illegal_write1.rs b/tests/fail/stacked_borrows/illegal_write1.rs deleted file mode 100644 index f28401938a..0000000000 --- a/tests/fail/stacked_borrows/illegal_write1.rs +++ /dev/null @@ -1,9 +0,0 @@ -fn main() { - let target = Box::new(42); // has an implicit raw - let xref = &*target; - { - let x: *mut u32 = xref as *const _ as *mut _; - unsafe { *x = 42 }; //~ ERROR: /write access .* tag only grants SharedReadOnly permission/ - } - let _x = *xref; -} diff --git a/tests/fail/stacked_borrows/illegal_write6.rs b/tests/fail/stacked_borrows/illegal_write6.rs deleted file mode 100644 index 7983b30ea1..0000000000 --- a/tests/fail/stacked_borrows/illegal_write6.rs +++ /dev/null @@ -1,12 +0,0 @@ -fn main() { - let x = &mut 0u32; - let p = x as *mut u32; - foo(x, p); -} - -fn foo(a: &mut u32, y: *mut u32) -> u32 { - *a = 1; - let _b = &*a; - unsafe { *y = 2 }; //~ ERROR: /not granting access .* because that would remove .* which is strongly protected/ - return *a; -} diff --git a/tests/fail/stacked_borrows/mut_exclusive_violation2.rs b/tests/fail/stacked_borrows/mut_exclusive_violation2.rs deleted file mode 100644 index 2305ce7465..0000000000 --- a/tests/fail/stacked_borrows/mut_exclusive_violation2.rs +++ /dev/null @@ -1,12 +0,0 @@ -use std::ptr::NonNull; - -fn main() { - unsafe { - let x = &mut 0; - let mut ptr1 = NonNull::from(x); - let mut ptr2 = ptr1.clone(); - let raw1 = ptr1.as_mut(); - let _raw2 = ptr2.as_mut(); - let _val = *raw1; //~ ERROR: /read access .* tag does not exist in the borrow stack/ - } -} diff --git a/tests/fail/stacked_borrows/outdated_local.rs b/tests/fail/stacked_borrows/outdated_local.rs deleted file mode 100644 index 4262157d1e..0000000000 --- a/tests/fail/stacked_borrows/outdated_local.rs +++ /dev/null @@ -1,9 +0,0 @@ -fn main() { - let mut x = 0; - let y: *const i32 = &x; - x = 1; // this invalidates y by reactivating the lowermost uniq borrow for this local - - assert_eq!(unsafe { *y }, 1); //~ ERROR: /read access .* tag does not exist in the borrow stack/ - - assert_eq!(x, 1); -} diff --git a/tests/fail/stacked_borrows/shr_frozen_violation1.rs b/tests/fail/stacked_borrows/shr_frozen_violation1.rs deleted file mode 100644 index 8cd59b3550..0000000000 --- a/tests/fail/stacked_borrows/shr_frozen_violation1.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![allow(cast_ref_to_mut)] - -fn foo(x: &mut i32) -> i32 { - *x = 5; - unknown_code(&*x); - *x // must return 5 -} - -fn main() { - println!("{}", foo(&mut 0)); -} - -fn unknown_code(x: &i32) { - unsafe { - *(x as *const i32 as *mut i32) = 7; //~ ERROR: /write access .* only grants SharedReadOnly permission/ - } -} diff --git a/tests/fail/tree-borrows/alternate-read-write.rs b/tests/fail/tree_borrows/alternate-read-write.rs similarity index 100% rename from tests/fail/tree-borrows/alternate-read-write.rs rename to tests/fail/tree_borrows/alternate-read-write.rs diff --git a/tests/fail/tree-borrows/alternate-read-write.stderr b/tests/fail/tree_borrows/alternate-read-write.stderr similarity index 100% rename from tests/fail/tree-borrows/alternate-read-write.stderr rename to tests/fail/tree_borrows/alternate-read-write.stderr diff --git a/tests/fail/tree-borrows/error-range.rs b/tests/fail/tree_borrows/error-range.rs similarity index 100% rename from tests/fail/tree-borrows/error-range.rs rename to tests/fail/tree_borrows/error-range.rs diff --git a/tests/fail/tree-borrows/error-range.stderr b/tests/fail/tree_borrows/error-range.stderr similarity index 100% rename from tests/fail/tree-borrows/error-range.stderr rename to tests/fail/tree_borrows/error-range.stderr diff --git a/tests/fail/tree_borrows/fnentry_invalidation.rs b/tests/fail/tree_borrows/fnentry_invalidation.rs new file mode 100644 index 0000000000..a21cbab6a9 --- /dev/null +++ b/tests/fail/tree_borrows/fnentry_invalidation.rs @@ -0,0 +1,29 @@ +//@compile-flags: -Zmiri-tree-borrows +// This test is the TB counterpart to fail/stacked_borrows/fnentry_invalidation, +// but the SB version passes TB without error. +// An additional write access is inserted so that this test properly fails. + +// Test that spans displayed in diagnostics identify the function call, not the function +// definition, as the location of invalidation due to FnEntry retag. Technically the FnEntry retag +// occurs inside the function, but what the user wants to know is which call produced the +// invalidation. + +fn main() { + let mut x = 0i32; + let z = &mut x as *mut i32; + unsafe { + *z = 1; + } + x.do_bad(); + unsafe { + *z = 2; //~ ERROR: /write access through .* is forbidden/ + } +} + +trait Bad { + fn do_bad(&mut self) { + // who knows + } +} + +impl Bad for i32 {} diff --git a/tests/fail/tree_borrows/fnentry_invalidation.stderr b/tests/fail/tree_borrows/fnentry_invalidation.stderr new file mode 100644 index 0000000000..dacd27f0d3 --- /dev/null +++ b/tests/fail/tree_borrows/fnentry_invalidation.stderr @@ -0,0 +1,32 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/fnentry_invalidation.rs:LL:CC + | +LL | *z = 2; + | ^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag has state Frozen which forbids child write accesses +help: the accessed tag was created here, in the initial state Reserved + --> $DIR/fnentry_invalidation.rs:LL:CC + | +LL | let z = &mut x as *mut i32; + | ^^^^^^ +help: the accessed tag later transitioned to Active due to a child write access at offsets [0x0..0x4] + --> $DIR/fnentry_invalidation.rs:LL:CC + | +LL | *z = 1; + | ^^^^^^ + = help: this corresponds to the first write to a 2-phase borrowed mutable reference +help: the accessed tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4] + --> $DIR/fnentry_invalidation.rs:LL:CC + | +LL | x.do_bad(); + | ^^^^^^^^^^ + = help: this corresponds to a loss of write permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/fnentry_invalidation.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/tree-borrows/fragile-data-race.rs b/tests/fail/tree_borrows/fragile-data-race.rs similarity index 100% rename from tests/fail/tree-borrows/fragile-data-race.rs rename to tests/fail/tree_borrows/fragile-data-race.rs diff --git a/tests/fail/tree-borrows/fragile-data-race.stderr b/tests/fail/tree_borrows/fragile-data-race.stderr similarity index 100% rename from tests/fail/tree-borrows/fragile-data-race.stderr rename to tests/fail/tree_borrows/fragile-data-race.stderr diff --git a/tests/fail/tree-borrows/outside-range.rs b/tests/fail/tree_borrows/outside-range.rs similarity index 100% rename from tests/fail/tree-borrows/outside-range.rs rename to tests/fail/tree_borrows/outside-range.rs diff --git a/tests/fail/tree-borrows/outside-range.stderr b/tests/fail/tree_borrows/outside-range.stderr similarity index 100% rename from tests/fail/tree-borrows/outside-range.stderr rename to tests/fail/tree_borrows/outside-range.stderr diff --git a/tests/fail/tree-borrows/read-to-local.rs b/tests/fail/tree_borrows/parent_read_freezes_raw_mut.rs similarity index 69% rename from tests/fail/tree-borrows/read-to-local.rs rename to tests/fail/tree_borrows/parent_read_freezes_raw_mut.rs index 025b7ad22d..303c9ee0c0 100644 --- a/tests/fail/tree-borrows/read-to-local.rs +++ b/tests/fail/tree_borrows/parent_read_freezes_raw_mut.rs @@ -1,7 +1,7 @@ //@compile-flags: -Zmiri-tree-borrows -// Read to local variable kills reborrows *and* raw pointers derived from them. -// This test would succeed under Stacked Borrows. +// Read from local variable kills reborrows *and* raw pointers derived from them. +// This test would be accepted under Stacked Borrows. fn main() { unsafe { let mut root = 6u8; diff --git a/tests/fail/tree-borrows/read-to-local.stderr b/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr similarity index 83% rename from tests/fail/tree-borrows/read-to-local.stderr rename to tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr index 6deb4b43f6..cc7ed97b38 100644 --- a/tests/fail/tree-borrows/read-to-local.stderr +++ b/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr @@ -1,5 +1,5 @@ error: Undefined Behavior: write access through is forbidden - --> $DIR/read-to-local.rs:LL:CC + --> $DIR/parent_read_freezes_raw_mut.rs:LL:CC | LL | *ptr = 0; | ^^^^^^^^ write access through is forbidden @@ -7,24 +7,24 @@ LL | *ptr = 0; = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag has state Frozen which forbids child write accesses help: the accessed tag was created here, in the initial state Reserved - --> $DIR/read-to-local.rs:LL:CC + --> $DIR/parent_read_freezes_raw_mut.rs:LL:CC | LL | let mref = &mut root; | ^^^^^^^^^ help: the accessed tag later transitioned to Active due to a child write access at offsets [0x0..0x1] - --> $DIR/read-to-local.rs:LL:CC + --> $DIR/parent_read_freezes_raw_mut.rs:LL:CC | LL | *ptr = 0; // Write | ^^^^^^^^ = help: this corresponds to the first write to a 2-phase borrowed mutable reference help: the accessed tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1] - --> $DIR/read-to-local.rs:LL:CC + --> $DIR/parent_read_freezes_raw_mut.rs:LL:CC | LL | assert_eq!(root, 0); // Parent Read | ^^^^^^^^^^^^^^^^^^^ = help: this corresponds to a loss of write permissions = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/read-to-local.rs:LL:CC + = note: inside `main` at $DIR/parent_read_freezes_raw_mut.rs:LL:CC = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/fail/tree_borrows/pass_invalid_mut.rs b/tests/fail/tree_borrows/pass_invalid_mut.rs new file mode 100644 index 0000000000..a22e311834 --- /dev/null +++ b/tests/fail/tree_borrows/pass_invalid_mut.rs @@ -0,0 +1,20 @@ +//@compile-flags: -Zmiri-tree-borrows +// This test is the TB counterpart to fail/stacked_borrows/pass_invalid_mut, +// but the SB version passes TB without error. +// An additional write access is inserted so that this test properly fails. + +// Make sure that we cannot use a `&mut` whose parent got invalidated. +// fail/both_borrows/pass_invalid_shr is already checking a forbidden read, +// so the new thing that this tests is a forbidden write. +fn foo(nope: &mut i32) { + *nope = 31; //~ ERROR: /write access through .* is forbidden/ +} + +fn main() { + let x = &mut 42; + let xraw = x as *mut _; + let xref = unsafe { &mut *xraw }; + *xref = 18; // activate xref + let _val = unsafe { *xraw }; // invalidate xref for writing + foo(xref); +} diff --git a/tests/fail/tree_borrows/pass_invalid_mut.stderr b/tests/fail/tree_borrows/pass_invalid_mut.stderr new file mode 100644 index 0000000000..4e5657f8b7 --- /dev/null +++ b/tests/fail/tree_borrows/pass_invalid_mut.stderr @@ -0,0 +1,43 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/pass_invalid_mut.rs:LL:CC + | +LL | *nope = 31; + | ^^^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Frozen which forbids child write accesses +help: the accessed tag was created here + --> $DIR/pass_invalid_mut.rs:LL:CC + | +LL | fn foo(nope: &mut i32) { + | ^^^^ +help: the conflicting tag was created here, in the initial state Reserved + --> $DIR/pass_invalid_mut.rs:LL:CC + | +LL | let xref = unsafe { &mut *xraw }; + | ^^^^^^^^^^ +help: the conflicting tag later transitioned to Active due to a child write access at offsets [0x0..0x4] + --> $DIR/pass_invalid_mut.rs:LL:CC + | +LL | *xref = 18; // activate xref + | ^^^^^^^^^^ + = help: this corresponds to the first write to a 2-phase borrowed mutable reference +help: the conflicting tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4] + --> $DIR/pass_invalid_mut.rs:LL:CC + | +LL | let _val = unsafe { *xraw }; // invalidate xref for writing + | ^^^^^ + = help: this corresponds to a loss of write permissions + = note: BACKTRACE (of the first span): + = note: inside `foo` at $DIR/pass_invalid_mut.rs:LL:CC +note: inside `main` + --> $DIR/pass_invalid_mut.rs:LL:CC + | +LL | foo(xref); + | ^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/tree-borrows/reserved/cell-protected-write.rs b/tests/fail/tree_borrows/reserved/cell-protected-write.rs similarity index 100% rename from tests/fail/tree-borrows/reserved/cell-protected-write.rs rename to tests/fail/tree_borrows/reserved/cell-protected-write.rs diff --git a/tests/fail/tree-borrows/reserved/cell-protected-write.stderr b/tests/fail/tree_borrows/reserved/cell-protected-write.stderr similarity index 100% rename from tests/fail/tree-borrows/reserved/cell-protected-write.stderr rename to tests/fail/tree_borrows/reserved/cell-protected-write.stderr diff --git a/tests/fail/tree-borrows/reserved/int-protected-write.rs b/tests/fail/tree_borrows/reserved/int-protected-write.rs similarity index 100% rename from tests/fail/tree-borrows/reserved/int-protected-write.rs rename to tests/fail/tree_borrows/reserved/int-protected-write.rs diff --git a/tests/fail/tree-borrows/reserved/int-protected-write.stderr b/tests/fail/tree_borrows/reserved/int-protected-write.stderr similarity index 100% rename from tests/fail/tree-borrows/reserved/int-protected-write.stderr rename to tests/fail/tree_borrows/reserved/int-protected-write.stderr diff --git a/tests/fail/tree-borrows/retag-data-race.rs b/tests/fail/tree_borrows/retag-data-race.rs similarity index 100% rename from tests/fail/tree-borrows/retag-data-race.rs rename to tests/fail/tree_borrows/retag-data-race.rs diff --git a/tests/fail/tree-borrows/retag-data-race.stderr b/tests/fail/tree_borrows/retag-data-race.stderr similarity index 100% rename from tests/fail/tree-borrows/retag-data-race.stderr rename to tests/fail/tree_borrows/retag-data-race.stderr diff --git a/tests/fail/tree_borrows/return_invalid_mut.rs b/tests/fail/tree_borrows/return_invalid_mut.rs new file mode 100644 index 0000000000..869e48bea5 --- /dev/null +++ b/tests/fail/tree_borrows/return_invalid_mut.rs @@ -0,0 +1,22 @@ +//@compile-flags: -Zmiri-tree-borrows +// This test is the TB counterpart to fail/stacked_borrows/return_invalid_mut, +// but the SB version passes TB without error. +// An additional write access is inserted so that this test properly fails. + +// Make sure that we cannot use a returned `&mut` that got already invalidated. +// fail/both_borrows/return_invalid_shr is already testing that we cannot return +// a reference invalidated for reading, so the new thing that we test here +// is the case where the return value cannot be used for writing. +fn foo(x: &mut (i32, i32)) -> &mut i32 { + let xraw = x as *mut (i32, i32); + let ret = unsafe { &mut (*xraw).1 }; + *ret = *ret; // activate + let _val = unsafe { *xraw }; // invalidate xref for writing + ret +} + +fn main() { + let arg = &mut (1, 2); + let ret = foo(arg); + *ret = 3; //~ ERROR: /write access through .* is forbidden/ +} diff --git a/tests/fail/tree_borrows/return_invalid_mut.stderr b/tests/fail/tree_borrows/return_invalid_mut.stderr new file mode 100644 index 0000000000..3d050d209c --- /dev/null +++ b/tests/fail/tree_borrows/return_invalid_mut.stderr @@ -0,0 +1,38 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/return_invalid_mut.rs:LL:CC + | +LL | *ret = 3; + | ^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Frozen which forbids child write accesses +help: the accessed tag was created here + --> $DIR/return_invalid_mut.rs:LL:CC + | +LL | let ret = foo(arg); + | ^^^^^^^^ +help: the conflicting tag was created here, in the initial state Reserved + --> $DIR/return_invalid_mut.rs:LL:CC + | +LL | let ret = unsafe { &mut (*xraw).1 }; + | ^^^^^^^^^^^^^^ +help: the conflicting tag later transitioned to Active due to a child write access at offsets [0x4..0x8] + --> $DIR/return_invalid_mut.rs:LL:CC + | +LL | *ret = *ret; // activate + | ^^^^^^^^^^^ + = help: this corresponds to the first write to a 2-phase borrowed mutable reference +help: the conflicting tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x8] + --> $DIR/return_invalid_mut.rs:LL:CC + | +LL | let _val = unsafe { *xraw }; // invalidate xref for writing + | ^^^^^ + = help: this corresponds to a loss of write permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/return_invalid_mut.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/tree-borrows/strongly-protected.rs b/tests/fail/tree_borrows/strongly-protected.rs similarity index 100% rename from tests/fail/tree-borrows/strongly-protected.rs rename to tests/fail/tree_borrows/strongly-protected.rs diff --git a/tests/fail/tree-borrows/strongly-protected.stderr b/tests/fail/tree_borrows/strongly-protected.stderr similarity index 100% rename from tests/fail/tree-borrows/strongly-protected.stderr rename to tests/fail/tree_borrows/strongly-protected.stderr diff --git a/tests/fail/tree-borrows/write-during-2phase.rs b/tests/fail/tree_borrows/write-during-2phase.rs similarity index 100% rename from tests/fail/tree-borrows/write-during-2phase.rs rename to tests/fail/tree_borrows/write-during-2phase.rs diff --git a/tests/fail/tree-borrows/write-during-2phase.stderr b/tests/fail/tree_borrows/write-during-2phase.stderr similarity index 100% rename from tests/fail/tree-borrows/write-during-2phase.stderr rename to tests/fail/tree_borrows/write-during-2phase.stderr diff --git a/tests/fail/tree_borrows/write_to_shr.stderr b/tests/fail/tree_borrows/write_to_shr.stderr new file mode 100644 index 0000000000..0e2002c34f --- /dev/null +++ b/tests/fail/tree_borrows/write_to_shr.stderr @@ -0,0 +1,26 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/write_to_shr.rs:LL:CC + | +LL | *xmut = 31; + | ^^^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Frozen which forbids child write accesses +help: the accessed tag was created here + --> $DIR/write_to_shr.rs:LL:CC + | +LL | let xmut = unsafe { &mut *(xref as *const u64 as *mut u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the conflicting tag was created here, in the initial state Frozen + --> $DIR/write_to_shr.rs:LL:CC + | +LL | let xref = unsafe { &*(x as *mut u64) }; + | ^^^^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/write_to_shr.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/pass/tree-borrows/transmute-unsafecell.rs b/tests/pass/tree-borrows/transmute-unsafecell.rs deleted file mode 100644 index e1a9334ab5..0000000000 --- a/tests/pass/tree-borrows/transmute-unsafecell.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@compile-flags: -Zmiri-tree-borrows - -use core::cell::UnsafeCell; -use core::mem; - -fn main() { - unsafe { - let x = &0i32; - // As long as we only read, transmuting this to UnsafeCell should be fine. - let cell_x: &UnsafeCell = mem::transmute(&x); - let _val = *cell_x.get(); - } -} diff --git a/tests/pass/tree-borrows/2phase-interiormut.rs b/tests/pass/tree_borrows/2phase-interiormut.rs similarity index 100% rename from tests/pass/tree-borrows/2phase-interiormut.rs rename to tests/pass/tree_borrows/2phase-interiormut.rs diff --git a/tests/pass/tree-borrows/cell-alternate-writes.rs b/tests/pass/tree_borrows/cell-alternate-writes.rs similarity index 100% rename from tests/pass/tree-borrows/cell-alternate-writes.rs rename to tests/pass/tree_borrows/cell-alternate-writes.rs diff --git a/tests/pass/tree-borrows/cell-alternate-writes.stderr b/tests/pass/tree_borrows/cell-alternate-writes.stderr similarity index 100% rename from tests/pass/tree-borrows/cell-alternate-writes.stderr rename to tests/pass/tree_borrows/cell-alternate-writes.stderr diff --git a/tests/pass/tree-borrows/copy-nonoverlapping.rs b/tests/pass/tree_borrows/copy-nonoverlapping.rs similarity index 100% rename from tests/pass/tree-borrows/copy-nonoverlapping.rs rename to tests/pass/tree_borrows/copy-nonoverlapping.rs diff --git a/tests/pass/tree-borrows/end-of-protector.rs b/tests/pass/tree_borrows/end-of-protector.rs similarity index 100% rename from tests/pass/tree-borrows/end-of-protector.rs rename to tests/pass/tree_borrows/end-of-protector.rs diff --git a/tests/pass/tree-borrows/end-of-protector.stderr b/tests/pass/tree_borrows/end-of-protector.stderr similarity index 100% rename from tests/pass/tree-borrows/end-of-protector.stderr rename to tests/pass/tree_borrows/end-of-protector.stderr diff --git a/tests/pass/tree-borrows/formatting.rs b/tests/pass/tree_borrows/formatting.rs similarity index 100% rename from tests/pass/tree-borrows/formatting.rs rename to tests/pass/tree_borrows/formatting.rs diff --git a/tests/pass/tree-borrows/formatting.stderr b/tests/pass/tree_borrows/formatting.stderr similarity index 100% rename from tests/pass/tree-borrows/formatting.stderr rename to tests/pass/tree_borrows/formatting.stderr diff --git a/tests/pass/tree-borrows/reborrow-is-read.rs b/tests/pass/tree_borrows/reborrow-is-read.rs similarity index 100% rename from tests/pass/tree-borrows/reborrow-is-read.rs rename to tests/pass/tree_borrows/reborrow-is-read.rs diff --git a/tests/pass/tree-borrows/reborrow-is-read.stderr b/tests/pass/tree_borrows/reborrow-is-read.stderr similarity index 100% rename from tests/pass/tree-borrows/reborrow-is-read.stderr rename to tests/pass/tree_borrows/reborrow-is-read.stderr diff --git a/tests/pass/tree-borrows/reserved.rs b/tests/pass/tree_borrows/reserved.rs similarity index 100% rename from tests/pass/tree-borrows/reserved.rs rename to tests/pass/tree_borrows/reserved.rs diff --git a/tests/pass/tree-borrows/reserved.stderr b/tests/pass/tree_borrows/reserved.stderr similarity index 100% rename from tests/pass/tree-borrows/reserved.stderr rename to tests/pass/tree_borrows/reserved.stderr diff --git a/tests/pass/tree_borrows/sb_fails.rs b/tests/pass/tree_borrows/sb_fails.rs new file mode 100644 index 0000000000..5973ef01ea --- /dev/null +++ b/tests/pass/tree_borrows/sb_fails.rs @@ -0,0 +1,81 @@ +//@compile-flags: -Zmiri-tree-borrows + +// These tests fail Stacked Borrows, but pass Tree Borrows. +// A modified version of each is also available that fails Tree Borrows. +// They all have in common that in SB a mutable reborrow is enough to produce +// write access errors, but in TB an actual write is needed. + +mod fnentry_invalidation { + // Copied directly from fail/stacked_borrows/fnentry_invalidation.rs + // Version that fails TB: fail/tree_borrows/fnentry_invalidation.rs + pub fn main() { + let mut x = 0i32; + let z = &mut x as *mut i32; + x.do_bad(); + unsafe { + let _oof = *z; + // In SB this is an error, but in TB the mutable reborrow did + // not invalidate z for reading. + } + } + + trait Bad { + fn do_bad(&mut self) { + // who knows + } + } + + impl Bad for i32 {} +} + +mod pass_invalid_mut { + // Copied directly from fail/stacked_borrows/pass_invalid_mut.rs + // Version that fails TB: fail/tree_borrows/pass_invalid_mut.rs + fn foo(_: &mut i32) {} + + pub fn main() { + let x = &mut 42; + let xraw = x as *mut _; + let xref = unsafe { &mut *xraw }; + let _val = unsafe { *xraw }; // In SB this invalidates xref... + foo(xref); // ...which then cannot be reborrowed here. + // But in TB xref is Reserved and thus still writeable. + } +} + +mod return_invalid_mut { + // Copied directly from fail/stacked_borrows/return_invalid_mut.rs + // Version that fails TB: fail/tree_borrows/return_invalid_mut.rs + fn foo(x: &mut (i32, i32)) -> &mut i32 { + let xraw = x as *mut (i32, i32); + let ret = unsafe { &mut (*xraw).1 }; + let _val = unsafe { *xraw }; // In SB this invalidates ret... + ret // ...which then cannot be reborrowed here. + // But in TB ret is Reserved and thus still writeable. + } + + pub fn main() { + foo(&mut (1, 2)); + } +} + +mod static_memory_modification { + // Copied directly from fail/stacked_borrows/static_memory_modification.rs + // Version that fails TB: fail/tree_borrows/static_memory_modification.rs + static X: usize = 5; + + #[allow(mutable_transmutes)] + pub fn main() { + let _x = unsafe { + std::mem::transmute::<&usize, &mut usize>(&X) // In SB this mutable reborrow fails. + // But in TB we are allowed to transmute as long as we don't write. + }; + } +} + +fn main() { + fnentry_invalidation::main(); + pass_invalid_mut::main(); + return_invalid_mut::main(); + static_memory_modification::main(); +} diff --git a/tests/pass/tree_borrows/transmute-unsafecell.rs b/tests/pass/tree_borrows/transmute-unsafecell.rs new file mode 100644 index 0000000000..1df0636e1e --- /dev/null +++ b/tests/pass/tree_borrows/transmute-unsafecell.rs @@ -0,0 +1,32 @@ +//@compile-flags: -Zmiri-tree-borrows + +//! Testing `mem::transmute` between types with and without interior mutability. +//! All transmutations should work, as long as we don't do any actual accesses +//! that violate immutability. + +use core::cell::UnsafeCell; +use core::mem; + +fn main() { + unsafe { + ref_to_cell(); + cell_to_ref(); + } +} + +// Pretend that the reference has interior mutability. +// Don't actually mutate it though, it will fail because it has a Frozen parent. +unsafe fn ref_to_cell() { + let x = &42i32; + let cell_x: &UnsafeCell = mem::transmute(x); + let val = *cell_x.get(); + assert_eq!(val, 42); +} + +// Forget about the interior mutability of a cell. +unsafe fn cell_to_ref() { + let x = &UnsafeCell::new(42); + let ref_x: &i32 = mem::transmute(x); + let val = *ref_x; + assert_eq!(val, 42); +} diff --git a/tests/pass/tree-borrows/tree-borrows.rs b/tests/pass/tree_borrows/tree-borrows.rs similarity index 100% rename from tests/pass/tree-borrows/tree-borrows.rs rename to tests/pass/tree_borrows/tree-borrows.rs From a357c8fd9429b63cc2fbbd3b22cff40339a421b0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 3 Jun 2023 22:46:06 +0200 Subject: [PATCH 05/32] use as_os_str_bytes --- src/lib.rs | 1 + src/shims/os_str.rs | 19 ++----------------- src/shims/unix/fs.rs | 5 ++--- 3 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f711f01f32..e79bb47c78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ #![feature(nonzero_ops)] #![feature(local_key_cell_methods)] #![feature(round_ties_even)] +#![feature(os_str_bytes)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, diff --git a/src/shims/os_str.rs b/src/shims/os_str.rs index 6bc5b8f39d..e85ab7a48b 100644 --- a/src/shims/os_str.rs +++ b/src/shims/os_str.rs @@ -17,28 +17,13 @@ pub enum PathConversion { TargetToHost, } -#[cfg(unix)] -pub fn os_str_to_bytes<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, &[u8]> { - Ok(os_str.as_bytes()) -} - -#[cfg(not(unix))] -pub fn os_str_to_bytes<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, &[u8]> { - // On non-unix platforms the best we can do to transform bytes from/to OS strings is to do the - // intermediate transformation into strings. Which invalidates non-utf8 paths that are actually - // valid. - os_str - .to_str() - .map(|s| s.as_bytes()) - .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into()) -} - #[cfg(unix)] pub fn bytes_to_os_str<'tcx>(bytes: &[u8]) -> InterpResult<'tcx, &OsStr> { Ok(OsStr::from_bytes(bytes)) } #[cfg(not(unix))] pub fn bytes_to_os_str<'tcx>(bytes: &[u8]) -> InterpResult<'tcx, &OsStr> { + // We cannot use `from_os_str_bytes_unchecked` here since we can't trust `bytes`. let s = std::str::from_utf8(bytes) .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", bytes))?; Ok(OsStr::new(s)) @@ -97,7 +82,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ptr: Pointer>, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { - let bytes = os_str_to_bytes(os_str)?; + let bytes = os_str.as_os_str_bytes(); self.eval_context_mut().write_c_str(bytes, ptr, size) } diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index d1b09cd7b5..5f1dc45146 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -16,7 +16,6 @@ use rustc_target::abi::{Align, Size}; use crate::shims::os_str::bytes_to_os_str; use crate::*; -use shims::os_str::os_str_to_bytes; use shims::time::system_time_to_duration; #[derive(Debug)] @@ -1339,7 +1338,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let mut name = dir_entry.file_name(); // not a Path as there are no separators! name.push("\0"); // Add a NUL terminator - let name_bytes = os_str_to_bytes(&name)?; + let name_bytes = name.as_os_str_bytes(); let name_len = u64::try_from(name_bytes.len()).unwrap(); let dirent64_layout = this.libc_ty_layout("dirent64"); @@ -1695,7 +1694,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Cow::Borrowed(resolved.as_ref()), crate::shims::os_str::PathConversion::HostToTarget, ); - let mut path_bytes = crate::shims::os_str::os_str_to_bytes(resolved.as_ref())?; + let mut path_bytes = resolved.as_os_str_bytes(); let bufsize: usize = bufsize.try_into().unwrap(); if path_bytes.len() > bufsize { path_bytes = &path_bytes[..bufsize] From f089f76ed4bccccd830260dc35edc3f716e4647d Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 4 Jun 2023 09:08:22 -0700 Subject: [PATCH 06/32] Remove rustc-workspace-hack --- Cargo.lock | 7 ------- Cargo.toml | 4 ---- cargo-miri/Cargo.lock | 7 ------- cargo-miri/Cargo.toml | 5 ----- 4 files changed, 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7bedb5d48f..fa30dd21ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -442,7 +442,6 @@ dependencies = [ "measureme", "rand", "regex", - "rustc-workspace-hack", "rustc_version", "smallvec", "ui_test", @@ -628,12 +627,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc-workspace-hack" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc71d2faa173b74b232dedc235e3ee1696581bb132fc116fa3626d6151a1a8fb" - [[package]] name = "rustc_version" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 683f99ca4d..143ea6e542 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,10 +24,6 @@ log = "0.4" rand = "0.8" smallvec = "1.7" -# A noop dependency that changes in the Rust repository, it's a bit of a hack. -# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` -# for more information. -rustc-workspace-hack = "1.0.0" measureme = "10.0.0" ctrlc = "3.2.5" diff --git a/cargo-miri/Cargo.lock b/cargo-miri/Cargo.lock index 727e46a028..a8cfccd37c 100644 --- a/cargo-miri/Cargo.lock +++ b/cargo-miri/Cargo.lock @@ -30,7 +30,6 @@ dependencies = [ "cargo_metadata", "directories", "rustc-build-sysroot", - "rustc-workspace-hack", "rustc_tools_util", "rustc_version", "serde", @@ -235,12 +234,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "rustc-workspace-hack" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc71d2faa173b74b232dedc235e3ee1696581bb132fc116fa3626d6151a1a8fb" - [[package]] name = "rustc_tools_util" version = "0.3.0" diff --git a/cargo-miri/Cargo.toml b/cargo-miri/Cargo.toml index 09079dbb81..cfa5ac5281 100644 --- a/cargo-miri/Cargo.toml +++ b/cargo-miri/Cargo.toml @@ -20,11 +20,6 @@ serde_json = "1.0.40" cargo_metadata = "0.15.0" rustc-build-sysroot = "0.4.1" -# A noop dependency that changes in the Rust repository, it's a bit of a hack. -# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` -# for more information. -rustc-workspace-hack = "1.0.0" - # Enable some feature flags that dev-dependencies need but dependencies # do not. This makes `./miri install` after `./miri build` faster. serde = { version = "*", features = ["derive"] } From 9fef7829684d527dbe50e47b10fbeac770524386 Mon Sep 17 00:00:00 2001 From: Neven Villani Date: Mon, 5 Jun 2023 13:19:14 +0200 Subject: [PATCH 07/32] Differentiate between explicit accesses and accesses inserted by TB --- .../tree_borrows/diagnostics.rs | 61 ++++++++++++++----- src/borrow_tracker/tree_borrows/mod.rs | 18 +++++- src/borrow_tracker/tree_borrows/tree.rs | 18 ++++-- .../alias_through_mutation.tree.stderr | 4 +- .../both_borrows/aliasing_mut1.tree.stderr | 6 +- .../both_borrows/aliasing_mut2.tree.stderr | 4 +- .../both_borrows/aliasing_mut3.tree.stderr | 6 +- .../both_borrows/aliasing_mut4.tree.stderr | 4 +- .../box_noalias_violation.tree.stderr | 6 +- .../buggy_as_mut_slice.tree.stderr | 4 +- .../buggy_split_at_mut.tree.stderr | 4 +- .../both_borrows/illegal_write1.tree.stderr | 2 +- .../both_borrows/illegal_write5.tree.stderr | 4 +- .../both_borrows/illegal_write6.tree.stderr | 6 +- .../invalidate_against_protector2.tree.stderr | 4 +- .../invalidate_against_protector3.tree.stderr | 4 +- tests/fail/both_borrows/load_invalid_shr.rs | 2 +- .../both_borrows/load_invalid_shr.tree.stderr | 8 +-- .../mut_exclusive_violation1.tree.stderr | 4 +- .../mut_exclusive_violation2.tree.stderr | 4 +- .../both_borrows/newtype_pair_retagging.rs | 2 +- .../newtype_pair_retagging.tree.stderr | 12 ++-- tests/fail/both_borrows/newtype_retagging.rs | 2 +- .../newtype_retagging.tree.stderr | 12 ++-- .../both_borrows/outdated_local.tree.stderr | 4 +- tests/fail/both_borrows/pass_invalid_shr.rs | 2 +- .../both_borrows/pass_invalid_shr.tree.stderr | 8 +-- .../both_borrows/pass_invalid_shr_option.rs | 2 +- .../pass_invalid_shr_option.tree.stderr | 8 +-- .../both_borrows/pass_invalid_shr_tuple.rs | 2 +- .../pass_invalid_shr_tuple.tree.stderr | 8 +-- tests/fail/both_borrows/return_invalid_shr.rs | 2 +- .../return_invalid_shr.tree.stderr | 8 +-- .../both_borrows/return_invalid_shr_option.rs | 2 +- .../return_invalid_shr_option.tree.stderr | 8 +-- .../both_borrows/return_invalid_shr_tuple.rs | 2 +- .../return_invalid_shr_tuple.tree.stderr | 8 +-- .../shr_frozen_violation1.tree.stderr | 2 +- .../shr_frozen_violation2.tree.stderr | 4 +- .../tree_borrows/alternate-read-write.stderr | 6 +- tests/fail/tree_borrows/error-range.stderr | 4 +- .../tree_borrows/fnentry_invalidation.stderr | 8 +-- .../tree_borrows/fragile-data-race.stderr | 6 +- tests/fail/tree_borrows/outside-range.stderr | 4 +- .../parent_read_freezes_raw_mut.stderr | 8 +-- .../fail/tree_borrows/pass_invalid_mut.stderr | 6 +- .../reserved/cell-protected-write.stderr | 4 +- .../reserved/int-protected-write.stderr | 4 +- .../tree_borrows/return_invalid_mut.stderr | 6 +- .../fail/tree_borrows/write-during-2phase.rs | 2 +- .../tree_borrows/write-during-2phase.stderr | 8 +-- 51 files changed, 195 insertions(+), 142 deletions(-) diff --git a/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/borrow_tracker/tree_borrows/diagnostics.rs index cea2cffa91..1a7f14f0ab 100644 --- a/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -12,16 +12,46 @@ use crate::borrow_tracker::tree_borrows::{ use crate::borrow_tracker::{AccessKind, ProtectorKind}; use crate::*; +/// Cause of an access: either a real access or one +/// inserted by Tree Borrows due to a reborrow or a deallocation. +#[derive(Clone, Copy, Debug)] +pub enum AccessCause { + Explicit(AccessKind), + Reborrow, + Dealloc, +} + +impl fmt::Display for AccessCause { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Explicit(kind) => write!(f, "{kind}"), + Self::Reborrow => write!(f, "reborrow"), + Self::Dealloc => write!(f, "deallocation"), + } + } +} + +impl AccessCause { + fn print_as_access(self, is_foreign: bool) -> String { + let rel = if is_foreign { "foreign" } else { "child" }; + match self { + Self::Explicit(kind) => format!("{rel} {kind}"), + Self::Reborrow => format!("reborrow (acting as a {rel} read access)"), + Self::Dealloc => format!("deallocation (acting as a {rel} write access)"), + } + } +} + /// Complete data for an event: #[derive(Clone, Debug)] pub struct Event { /// Transformation of permissions that occured because of this event pub transition: PermTransition, /// Kind of the access that triggered this event - pub access_kind: AccessKind, + pub access_cause: AccessCause, /// Relative position of the tag to the one used for the access pub is_foreign: bool, - /// User-visible range of the access + /// Whether this access was explicit or inserted implicitly by Tree Borrows. pub access_range: AllocRange, /// The transition recorded by this event only occured on a subrange of /// `access_range`: a single access on `access_range` triggers several events, @@ -83,8 +113,8 @@ impl HistoryData { self.events.push((Some(created.0.data()), msg_creation)); for &Event { transition, - access_kind, is_foreign, + access_cause, access_range, span, transition_range: _, @@ -92,8 +122,10 @@ impl HistoryData { { // NOTE: `transition_range` is explicitly absent from the error message, it has no significance // to the user. The meaningful one is `access_range`. - self.events.push((Some(span.data()), format!("{this} later transitioned to {endpoint} due to a {rel} {access_kind} at offsets {access_range:?}", endpoint = transition.endpoint(), rel = if is_foreign { "foreign" } else { "child" }))); - self.events.push((None, format!("this corresponds to {}", transition.summary()))); + let access = access_cause.print_as_access(is_foreign); + self.events.push((Some(span.data()), format!("{this} later transitioned to {endpoint} due to a {access} at offsets {access_range:?}", endpoint = transition.endpoint()))); + self.events + .push((None, format!("this transition corresponds to {}", transition.summary()))); } } } @@ -238,9 +270,8 @@ pub(super) struct TbError<'node> { /// On accesses rejected due to insufficient permissions, this is the /// tag that lacked those permissions. pub conflicting_info: &'node NodeDebugInfo, - /// Whether this was a Read or Write access. This field is ignored - /// when the error was triggered by a deallocation. - pub access_kind: AccessKind, + // What kind of access caused this error (read, write, reborrow, deallocation) + pub access_cause: AccessCause, /// Which tag the access that caused this error was made through, i.e. /// which tag was used to read/write/deallocate. pub accessed_info: &'node NodeDebugInfo, @@ -250,38 +281,39 @@ impl TbError<'_> { /// Produce a UB error. pub fn build<'tcx>(self) -> InterpError<'tcx> { use TransitionError::*; - let kind = self.access_kind; + let cause = self.access_cause; let accessed = self.accessed_info; let conflicting = self.conflicting_info; let accessed_is_conflicting = accessed.tag == conflicting.tag; + let title = format!("{cause} through {accessed} is forbidden"); let (title, details, conflicting_tag_name) = match self.error_kind { ChildAccessForbidden(perm) => { let conflicting_tag_name = if accessed_is_conflicting { "accessed" } else { "conflicting" }; - let title = format!("{kind} through {accessed} is forbidden"); let mut details = Vec::new(); if !accessed_is_conflicting { details.push(format!( "the accessed tag {accessed} is a child of the conflicting tag {conflicting}" )); } + let access = cause.print_as_access(/* is_foreign */ false); details.push(format!( - "the {conflicting_tag_name} tag {conflicting} has state {perm} which forbids child {kind}es" + "the {conflicting_tag_name} tag {conflicting} has state {perm} which forbids this {access}" )); (title, details, conflicting_tag_name) } ProtectedTransition(transition) => { let conflicting_tag_name = "protected"; - let title = format!("{kind} through {accessed} is forbidden"); + let access = cause.print_as_access(/* is_foreign */ true); let details = vec![ format!( "the accessed tag {accessed} is foreign to the {conflicting_tag_name} tag {conflicting} (i.e., it is not a child)" ), format!( - "the access would cause the {conflicting_tag_name} tag {conflicting} to transition {transition}" + "this {access} would cause the {conflicting_tag_name} tag {conflicting} to transition {transition}" ), format!( - "this is {loss}, which is not allowed for protected tags", + "this transition would be {loss}, which is not allowed for protected tags", loss = transition.summary(), ), ]; @@ -289,7 +321,6 @@ impl TbError<'_> { } ProtectedDealloc => { let conflicting_tag_name = "strongly protected"; - let title = format!("deallocation through {accessed} is forbidden"); let details = vec![ format!( "the allocation of the accessed tag {accessed} also contains the {conflicting_tag_name} tag {conflicting}" diff --git a/src/borrow_tracker/tree_borrows/mod.rs b/src/borrow_tracker/tree_borrows/mod.rs index 3361d212f2..1c7a735779 100644 --- a/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/borrow_tracker/tree_borrows/mod.rs @@ -62,7 +62,14 @@ impl<'tcx> Tree { }; let global = machine.borrow_tracker.as_ref().unwrap(); let span = machine.current_span(); - self.perform_access(access_kind, tag, range, global, span) + self.perform_access( + access_kind, + tag, + range, + global, + span, + diagnostics::AccessCause::Explicit(access_kind), + ) } /// Check that this pointer has permission to deallocate this range. @@ -273,7 +280,14 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' // Count this reborrow as a read access let global = &this.machine.borrow_tracker.as_ref().unwrap(); let span = this.machine.current_span(); - tree_borrows.perform_access(AccessKind::Read, orig_tag, range, global, span)?; + tree_borrows.perform_access( + AccessKind::Read, + orig_tag, + range, + global, + span, + diagnostics::AccessCause::Reborrow, + )?; if let Some(data_race) = alloc_extra.data_race.as_ref() { data_race.read(alloc_id, range, &this.machine)?; } diff --git a/src/borrow_tracker/tree_borrows/tree.rs b/src/borrow_tracker/tree_borrows/tree.rs index 52947aa0f9..9e8b1e1189 100644 --- a/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/borrow_tracker/tree_borrows/tree.rs @@ -349,7 +349,14 @@ impl<'tcx> Tree { global: &GlobalState, span: Span, // diagnostics ) -> InterpResult<'tcx> { - self.perform_access(AccessKind::Write, tag, access_range, global, span)?; + self.perform_access( + AccessKind::Write, + tag, + access_range, + global, + span, + diagnostics::AccessCause::Dealloc, + )?; for (perms_range, perms) in self.rperms.iter_mut(access_range.start, access_range.size) { TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, perms } .traverse_parents_this_children_others( @@ -368,7 +375,7 @@ impl<'tcx> Tree { let ErrHandlerArgs { error_kind, conflicting_info, accessed_info } = args; TbError { conflicting_info, - access_kind: AccessKind::Write, + access_cause: diagnostics::AccessCause::Dealloc, error_offset: perms_range.start, error_kind, accessed_info, @@ -391,7 +398,8 @@ impl<'tcx> Tree { tag: BorTag, access_range: AllocRange, global: &GlobalState, - span: Span, // diagnostics + span: Span, // diagnostics + access_cause: diagnostics::AccessCause, // diagnostics ) -> InterpResult<'tcx> { for (perms_range, perms) in self.rperms.iter_mut(access_range.start, access_range.size) { TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, perms } @@ -456,8 +464,8 @@ impl<'tcx> Tree { if !transition.is_noop() { node.debug_info.history.push(diagnostics::Event { transition, - access_kind, is_foreign: rel_pos.is_foreign(), + access_cause, access_range, transition_range: perms_range.clone(), span, @@ -472,7 +480,7 @@ impl<'tcx> Tree { let ErrHandlerArgs { error_kind, conflicting_info, accessed_info } = args; TbError { conflicting_info, - access_kind, + access_cause, error_offset: perms_range.start, error_kind, accessed_info, diff --git a/tests/fail/both_borrows/alias_through_mutation.tree.stderr b/tests/fail/both_borrows/alias_through_mutation.tree.stderr index a7c78b3140..655f1b5777 100644 --- a/tests/fail/both_borrows/alias_through_mutation.tree.stderr +++ b/tests/fail/both_borrows/alias_through_mutation.tree.stderr @@ -6,7 +6,7 @@ LL | let _val = *target_alias; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids child read accesses + = help: the conflicting tag has state Disabled which forbids this child read access help: the accessed tag was created here --> $DIR/alias_through_mutation.rs:LL:CC | @@ -22,7 +22,7 @@ help: the conflicting tag later transitioned to Disabled due to a foreign | LL | *target = 13; | ^^^^^^^^^^^^ - = help: this corresponds to a loss of read and write permissions + = help: this transition corresponds to a loss of read and write permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/alias_through_mutation.rs:LL:CC diff --git a/tests/fail/both_borrows/aliasing_mut1.tree.stderr b/tests/fail/both_borrows/aliasing_mut1.tree.stderr index 358bb8761e..2a9b4d688a 100644 --- a/tests/fail/both_borrows/aliasing_mut1.tree.stderr +++ b/tests/fail/both_borrows/aliasing_mut1.tree.stderr @@ -5,18 +5,18 @@ LL | *x = 1; | ^^^^^^ write access through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental - = help: the accessed tag has state Frozen which forbids child write accesses + = help: the accessed tag has state Frozen which forbids this child write access help: the accessed tag was created here, in the initial state Reserved --> $DIR/aliasing_mut1.rs:LL:CC | LL | pub fn safe(x: &mut i32, y: &mut i32) { | ^ -help: the accessed tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4] +help: the accessed tag later transitioned to Frozen due to a reborrow (acting as a foreign read access) at offsets [0x0..0x4] --> $DIR/aliasing_mut1.rs:LL:CC | LL | pub fn safe(x: &mut i32, y: &mut i32) { | ^ - = help: this corresponds to a loss of write permissions + = help: this transition corresponds to a loss of write permissions = note: BACKTRACE (of the first span): = note: inside `safe` at $DIR/aliasing_mut1.rs:LL:CC note: inside `main` diff --git a/tests/fail/both_borrows/aliasing_mut2.tree.stderr b/tests/fail/both_borrows/aliasing_mut2.tree.stderr index f485114d4e..d4858975ef 100644 --- a/tests/fail/both_borrows/aliasing_mut2.tree.stderr +++ b/tests/fail/both_borrows/aliasing_mut2.tree.stderr @@ -5,7 +5,7 @@ LL | *y = 2; | ^^^^^^ write access through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental - = help: the accessed tag has state Frozen which forbids child write accesses + = help: the accessed tag has state Frozen which forbids this child write access help: the accessed tag was created here, in the initial state Reserved --> $DIR/aliasing_mut2.rs:LL:CC | @@ -16,7 +16,7 @@ help: the accessed tag later transitioned to Frozen due to a foreign read | LL | let _v = *x; | ^^ - = help: this corresponds to a loss of write permissions + = help: this transition corresponds to a loss of write permissions = note: BACKTRACE (of the first span): = note: inside `safe` at $DIR/aliasing_mut2.rs:LL:CC note: inside `main` diff --git a/tests/fail/both_borrows/aliasing_mut3.tree.stderr b/tests/fail/both_borrows/aliasing_mut3.tree.stderr index f1d89074ad..d1afca84a8 100644 --- a/tests/fail/both_borrows/aliasing_mut3.tree.stderr +++ b/tests/fail/both_borrows/aliasing_mut3.tree.stderr @@ -5,18 +5,18 @@ LL | *x = 1; | ^^^^^^ write access through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental - = help: the accessed tag has state Frozen which forbids child write accesses + = help: the accessed tag has state Frozen which forbids this child write access help: the accessed tag was created here, in the initial state Reserved --> $DIR/aliasing_mut3.rs:LL:CC | LL | pub fn safe(x: &mut i32, y: &i32) { | ^ -help: the accessed tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4] +help: the accessed tag later transitioned to Frozen due to a reborrow (acting as a foreign read access) at offsets [0x0..0x4] --> $DIR/aliasing_mut3.rs:LL:CC | LL | pub fn safe(x: &mut i32, y: &i32) { | ^ - = help: this corresponds to a loss of write permissions + = help: this transition corresponds to a loss of write permissions = note: BACKTRACE (of the first span): = note: inside `safe` at $DIR/aliasing_mut3.rs:LL:CC note: inside `main` diff --git a/tests/fail/both_borrows/aliasing_mut4.tree.stderr b/tests/fail/both_borrows/aliasing_mut4.tree.stderr index f620d8cd51..4102c718af 100644 --- a/tests/fail/both_borrows/aliasing_mut4.tree.stderr +++ b/tests/fail/both_borrows/aliasing_mut4.tree.stderr @@ -6,8 +6,8 @@ LL | ptr::write(dest, src); | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) - = help: the access would cause the protected tag to transition from Frozen to Disabled - = help: this is a loss of read permissions, which is not allowed for protected tags + = help: this foreign write access would cause the protected tag to transition from Frozen to Disabled + = help: this transition would be a loss of read permissions, which is not allowed for protected tags help: the accessed tag was created here --> $DIR/aliasing_mut4.rs:LL:CC | diff --git a/tests/fail/both_borrows/box_noalias_violation.tree.stderr b/tests/fail/both_borrows/box_noalias_violation.tree.stderr index 3c842b4463..1d1ed82032 100644 --- a/tests/fail/both_borrows/box_noalias_violation.tree.stderr +++ b/tests/fail/both_borrows/box_noalias_violation.tree.stderr @@ -6,8 +6,8 @@ LL | *y | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) - = help: the access would cause the protected tag to transition from Active to Frozen - = help: this is a loss of write permissions, which is not allowed for protected tags + = help: this foreign read access would cause the protected tag to transition from Active to Frozen + = help: this transition would be a loss of write permissions, which is not allowed for protected tags help: the accessed tag was created here --> $DIR/box_noalias_violation.rs:LL:CC | @@ -23,7 +23,7 @@ help: the protected tag later transitioned to Active due to a child write | LL | *x = 5; | ^^^^^^ - = help: this corresponds to the first write to a 2-phase borrowed mutable reference + = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference = note: BACKTRACE (of the first span): = note: inside `test` at $DIR/box_noalias_violation.rs:LL:CC note: inside `main` diff --git a/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr b/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr index d1d643f3fb..9519c83f71 100644 --- a/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr +++ b/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr @@ -6,7 +6,7 @@ LL | v2[1] = 7; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids child write accesses + = help: the conflicting tag has state Disabled which forbids this child write access help: the accessed tag was created here --> $DIR/buggy_as_mut_slice.rs:LL:CC | @@ -22,7 +22,7 @@ help: the conflicting tag later transitioned to Disabled due to a foreign | LL | v1[1] = 5; | ^^^^^^^^^ - = help: this corresponds to a loss of read and write permissions + = help: this transition corresponds to a loss of read and write permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/buggy_as_mut_slice.rs:LL:CC diff --git a/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr b/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr index 3c619fe316..4fd92df75d 100644 --- a/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr +++ b/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr @@ -6,7 +6,7 @@ LL | b[1] = 6; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids child write accesses + = help: the conflicting tag has state Disabled which forbids this child write access help: the accessed tag was created here --> $DIR/buggy_split_at_mut.rs:LL:CC | @@ -22,7 +22,7 @@ help: the conflicting tag later transitioned to Disabled due to a foreign | LL | a[1] = 5; | ^^^^^^^^ - = help: this corresponds to a loss of read and write permissions + = help: this transition corresponds to a loss of read and write permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/buggy_split_at_mut.rs:LL:CC diff --git a/tests/fail/both_borrows/illegal_write1.tree.stderr b/tests/fail/both_borrows/illegal_write1.tree.stderr index 749fee28df..bb72159be6 100644 --- a/tests/fail/both_borrows/illegal_write1.tree.stderr +++ b/tests/fail/both_borrows/illegal_write1.tree.stderr @@ -5,7 +5,7 @@ LL | unsafe { *x = 42 }; | ^^^^^^^ write access through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental - = help: the accessed tag has state Frozen which forbids child write accesses + = help: the accessed tag has state Frozen which forbids this child write access help: the accessed tag was created here, in the initial state Frozen --> $DIR/illegal_write1.rs:LL:CC | diff --git a/tests/fail/both_borrows/illegal_write5.tree.stderr b/tests/fail/both_borrows/illegal_write5.tree.stderr index b94dc77c11..05cc69553a 100644 --- a/tests/fail/both_borrows/illegal_write5.tree.stderr +++ b/tests/fail/both_borrows/illegal_write5.tree.stderr @@ -6,7 +6,7 @@ LL | let _val = *xref; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids child read accesses + = help: the conflicting tag has state Disabled which forbids this child read access help: the accessed tag was created here --> $DIR/illegal_write5.rs:LL:CC | @@ -22,7 +22,7 @@ help: the conflicting tag later transitioned to Disabled due to a foreign | LL | unsafe { *xraw = 15 }; | ^^^^^^^^^^ - = help: this corresponds to a loss of read and write permissions + = help: this transition corresponds to a loss of read and write permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/illegal_write5.rs:LL:CC diff --git a/tests/fail/both_borrows/illegal_write6.tree.stderr b/tests/fail/both_borrows/illegal_write6.tree.stderr index e808d4a0ae..7308d045d1 100644 --- a/tests/fail/both_borrows/illegal_write6.tree.stderr +++ b/tests/fail/both_borrows/illegal_write6.tree.stderr @@ -6,8 +6,8 @@ LL | unsafe { *y = 2 }; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) - = help: the access would cause the protected tag to transition from Active to Disabled - = help: this is a loss of read and write permissions, which is not allowed for protected tags + = help: this foreign write access would cause the protected tag to transition from Active to Disabled + = help: this transition would be a loss of read and write permissions, which is not allowed for protected tags help: the accessed tag was created here --> $DIR/illegal_write6.rs:LL:CC | @@ -23,7 +23,7 @@ help: the protected tag later transitioned to Active due to a child write | LL | *a = 1; | ^^^^^^ - = help: this corresponds to the first write to a 2-phase borrowed mutable reference + = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference = note: BACKTRACE (of the first span): = note: inside `foo` at $DIR/illegal_write6.rs:LL:CC note: inside `main` diff --git a/tests/fail/both_borrows/invalidate_against_protector2.tree.stderr b/tests/fail/both_borrows/invalidate_against_protector2.tree.stderr index b6744fcfa0..ee64a66cc2 100644 --- a/tests/fail/both_borrows/invalidate_against_protector2.tree.stderr +++ b/tests/fail/both_borrows/invalidate_against_protector2.tree.stderr @@ -6,8 +6,8 @@ LL | unsafe { *x = 0 }; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) - = help: the access would cause the protected tag to transition from Frozen to Disabled - = help: this is a loss of read permissions, which is not allowed for protected tags + = help: this foreign write access would cause the protected tag to transition from Frozen to Disabled + = help: this transition would be a loss of read permissions, which is not allowed for protected tags help: the accessed tag was created here --> $DIR/invalidate_against_protector2.rs:LL:CC | diff --git a/tests/fail/both_borrows/invalidate_against_protector3.tree.stderr b/tests/fail/both_borrows/invalidate_against_protector3.tree.stderr index cfbe0463e2..e217170afb 100644 --- a/tests/fail/both_borrows/invalidate_against_protector3.tree.stderr +++ b/tests/fail/both_borrows/invalidate_against_protector3.tree.stderr @@ -6,8 +6,8 @@ LL | unsafe { *x = 0 }; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag (root of the allocation) is foreign to the protected tag (i.e., it is not a child) - = help: the access would cause the protected tag to transition from Frozen to Disabled - = help: this is a loss of read permissions, which is not allowed for protected tags + = help: this foreign write access would cause the protected tag to transition from Frozen to Disabled + = help: this transition would be a loss of read permissions, which is not allowed for protected tags help: the accessed tag was created here --> $DIR/invalidate_against_protector3.rs:LL:CC | diff --git a/tests/fail/both_borrows/load_invalid_shr.rs b/tests/fail/both_borrows/load_invalid_shr.rs index 9dc2bdac85..2e0f14e32d 100644 --- a/tests/fail/both_borrows/load_invalid_shr.rs +++ b/tests/fail/both_borrows/load_invalid_shr.rs @@ -12,5 +12,5 @@ fn main() { unsafe { *xraw = 42 }; // unfreeze let _val = *xref_in_mem; //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ - //~[tree]| ERROR: /read access through .* is forbidden/ + //~[tree]| ERROR: /reborrow through .* is forbidden/ } diff --git a/tests/fail/both_borrows/load_invalid_shr.tree.stderr b/tests/fail/both_borrows/load_invalid_shr.tree.stderr index f68887f059..9a3618ed85 100644 --- a/tests/fail/both_borrows/load_invalid_shr.tree.stderr +++ b/tests/fail/both_borrows/load_invalid_shr.tree.stderr @@ -1,12 +1,12 @@ -error: Undefined Behavior: read access through is forbidden +error: Undefined Behavior: reborrow through is forbidden --> $DIR/load_invalid_shr.rs:LL:CC | LL | let _val = *xref_in_mem; - | ^^^^^^^^^^^^ read access through is forbidden + | ^^^^^^^^^^^^ reborrow through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids child read accesses + = help: the conflicting tag has state Disabled which forbids this reborrow (acting as a child read access) help: the accessed tag was created here --> $DIR/load_invalid_shr.rs:LL:CC | @@ -22,7 +22,7 @@ help: the conflicting tag later transitioned to Disabled due to a foreign | LL | unsafe { *xraw = 42 }; // unfreeze | ^^^^^^^^^^ - = help: this corresponds to a loss of read permissions + = help: this transition corresponds to a loss of read permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/load_invalid_shr.rs:LL:CC diff --git a/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr b/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr index d80cfbea54..9cb500679f 100644 --- a/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr +++ b/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr @@ -6,7 +6,7 @@ LL | *LEAK = 7; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids child write accesses + = help: the conflicting tag has state Disabled which forbids this child write access help: the accessed tag was created here --> $DIR/mut_exclusive_violation1.rs:LL:CC | @@ -22,7 +22,7 @@ help: the conflicting tag later transitioned to Disabled due to a foreign | LL | *our = 5; | ^^^^^^^^ - = help: this corresponds to a loss of read permissions + = help: this transition corresponds to a loss of read permissions = note: BACKTRACE (of the first span): = note: inside `unknown_code_2` at $DIR/mut_exclusive_violation1.rs:LL:CC note: inside `demo_mut_advanced_unique` diff --git a/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr b/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr index 94ca9ffb54..5d126bdaeb 100644 --- a/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr +++ b/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr @@ -6,7 +6,7 @@ LL | *raw1 = 3; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids child write accesses + = help: the conflicting tag has state Disabled which forbids this child write access help: the accessed tag was created here --> $DIR/mut_exclusive_violation2.rs:LL:CC | @@ -22,7 +22,7 @@ help: the conflicting tag later transitioned to Disabled due to a foreign | LL | *raw2 = 2; | ^^^^^^^^^ - = help: this corresponds to a loss of read and write permissions + = help: this transition corresponds to a loss of read and write permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/mut_exclusive_violation2.rs:LL:CC diff --git a/tests/fail/both_borrows/newtype_pair_retagging.rs b/tests/fail/both_borrows/newtype_pair_retagging.rs index 013d722135..98dbef6a99 100644 --- a/tests/fail/both_borrows/newtype_pair_retagging.rs +++ b/tests/fail/both_borrows/newtype_pair_retagging.rs @@ -2,7 +2,7 @@ //@[tree]compile-flags: -Zmiri-tree-borrows //@[stack]error-in-other-file: which is strongly protected -//@[tree]error-in-other-file: /write access through .* is forbidden/ +//@[tree]error-in-other-file: /deallocation through .* is forbidden/ struct Newtype<'a>(&'a mut i32, i32); fn dealloc_while_running(_n: Newtype<'_>, dealloc: impl FnOnce()) { diff --git a/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr b/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr index 11ac000da9..81054972a9 100644 --- a/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr +++ b/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr @@ -1,13 +1,13 @@ -error: Undefined Behavior: write access through is forbidden +error: Undefined Behavior: deallocation through is forbidden --> RUSTLIB/alloc/src/alloc.rs:LL:CC | LL | unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ write access through is forbidden + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocation through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) - = help: the access would cause the protected tag to transition from Frozen to Disabled - = help: this is a loss of read permissions, which is not allowed for protected tags + = help: this deallocation (acting as a foreign write access) would cause the protected tag to transition from Frozen to Disabled + = help: this transition would be a loss of read permissions, which is not allowed for protected tags help: the accessed tag was created here --> $DIR/newtype_pair_retagging.rs:LL:CC | @@ -18,12 +18,12 @@ help: the protected tag was created here, in the initial state Reserved | LL | fn dealloc_while_running(_n: Newtype<'_>, dealloc: impl FnOnce()) { | ^^ -help: the protected tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4] +help: the protected tag later transitioned to Frozen due to a reborrow (acting as a foreign read access) at offsets [0x0..0x4] --> $DIR/newtype_pair_retagging.rs:LL:CC | LL | || drop(Box::from_raw(ptr)), | ^^^^^^^^^^^^^^^^^^ - = help: this corresponds to a loss of write permissions + = help: this transition corresponds to a loss of write permissions = note: BACKTRACE (of the first span): = note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `::deallocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC diff --git a/tests/fail/both_borrows/newtype_retagging.rs b/tests/fail/both_borrows/newtype_retagging.rs index c711e9ca9a..e280050cda 100644 --- a/tests/fail/both_borrows/newtype_retagging.rs +++ b/tests/fail/both_borrows/newtype_retagging.rs @@ -2,7 +2,7 @@ //@[tree]compile-flags: -Zmiri-tree-borrows //@[stack]error-in-other-file: which is strongly protected -//@[tree]error-in-other-file: /write access through .* is forbidden/ +//@[tree]error-in-other-file: /deallocation through .* is forbidden/ struct Newtype<'a>(&'a mut i32); diff --git a/tests/fail/both_borrows/newtype_retagging.tree.stderr b/tests/fail/both_borrows/newtype_retagging.tree.stderr index c442f398fc..3a3b24fd7d 100644 --- a/tests/fail/both_borrows/newtype_retagging.tree.stderr +++ b/tests/fail/both_borrows/newtype_retagging.tree.stderr @@ -1,13 +1,13 @@ -error: Undefined Behavior: write access through is forbidden +error: Undefined Behavior: deallocation through is forbidden --> RUSTLIB/alloc/src/alloc.rs:LL:CC | LL | unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ write access through is forbidden + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocation through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) - = help: the access would cause the protected tag to transition from Frozen to Disabled - = help: this is a loss of read permissions, which is not allowed for protected tags + = help: this deallocation (acting as a foreign write access) would cause the protected tag to transition from Frozen to Disabled + = help: this transition would be a loss of read permissions, which is not allowed for protected tags help: the accessed tag was created here --> $DIR/newtype_retagging.rs:LL:CC | @@ -18,12 +18,12 @@ help: the protected tag was created here, in the initial state Reserved | LL | fn dealloc_while_running(_n: Newtype<'_>, dealloc: impl FnOnce()) { | ^^ -help: the protected tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4] +help: the protected tag later transitioned to Frozen due to a reborrow (acting as a foreign read access) at offsets [0x0..0x4] --> $DIR/newtype_retagging.rs:LL:CC | LL | || drop(Box::from_raw(ptr)), | ^^^^^^^^^^^^^^^^^^ - = help: this corresponds to a loss of write permissions + = help: this transition corresponds to a loss of write permissions = note: BACKTRACE (of the first span): = note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `::deallocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC diff --git a/tests/fail/both_borrows/outdated_local.tree.stderr b/tests/fail/both_borrows/outdated_local.tree.stderr index 6d0087bb83..21800d6f4e 100644 --- a/tests/fail/both_borrows/outdated_local.tree.stderr +++ b/tests/fail/both_borrows/outdated_local.tree.stderr @@ -5,7 +5,7 @@ LL | assert_eq!(unsafe { *y }, 1); | ^^ read access through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental - = help: the accessed tag has state Disabled which forbids child read accesses + = help: the accessed tag has state Disabled which forbids this child read access help: the accessed tag was created here, in the initial state Frozen --> $DIR/outdated_local.rs:LL:CC | @@ -16,7 +16,7 @@ help: the accessed tag later transitioned to Disabled due to a foreign wri | LL | x = 1; // this invalidates y by reactivating the lowermost uniq borrow for this local | ^^^^^ - = help: this corresponds to a loss of read permissions + = help: this transition corresponds to a loss of read permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/outdated_local.rs:LL:CC diff --git a/tests/fail/both_borrows/pass_invalid_shr.rs b/tests/fail/both_borrows/pass_invalid_shr.rs index a49f622f69..5884780544 100644 --- a/tests/fail/both_borrows/pass_invalid_shr.rs +++ b/tests/fail/both_borrows/pass_invalid_shr.rs @@ -11,5 +11,5 @@ fn main() { unsafe { *xraw = 42 }; // unfreeze foo(xref); //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ - //~[tree]| ERROR: /read access through .* is forbidden/ + //~[tree]| ERROR: /reborrow through .* is forbidden/ } diff --git a/tests/fail/both_borrows/pass_invalid_shr.tree.stderr b/tests/fail/both_borrows/pass_invalid_shr.tree.stderr index c94e6c021b..677e6eeb48 100644 --- a/tests/fail/both_borrows/pass_invalid_shr.tree.stderr +++ b/tests/fail/both_borrows/pass_invalid_shr.tree.stderr @@ -1,11 +1,11 @@ -error: Undefined Behavior: read access through is forbidden +error: Undefined Behavior: reborrow through is forbidden --> $DIR/pass_invalid_shr.rs:LL:CC | LL | foo(xref); - | ^^^^ read access through is forbidden + | ^^^^ reborrow through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental - = help: the accessed tag has state Disabled which forbids child read accesses + = help: the accessed tag has state Disabled which forbids this reborrow (acting as a child read access) help: the accessed tag was created here, in the initial state Frozen --> $DIR/pass_invalid_shr.rs:LL:CC | @@ -16,7 +16,7 @@ help: the accessed tag later transitioned to Disabled due to a foreign wri | LL | unsafe { *xraw = 42 }; // unfreeze | ^^^^^^^^^^ - = help: this corresponds to a loss of read permissions + = help: this transition corresponds to a loss of read permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/pass_invalid_shr.rs:LL:CC diff --git a/tests/fail/both_borrows/pass_invalid_shr_option.rs b/tests/fail/both_borrows/pass_invalid_shr_option.rs index 0ac6f98fbc..d23bb7555f 100644 --- a/tests/fail/both_borrows/pass_invalid_shr_option.rs +++ b/tests/fail/both_borrows/pass_invalid_shr_option.rs @@ -11,5 +11,5 @@ fn main() { unsafe { *xraw = 42 }; // unfreeze foo(some_xref); //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ - //~[tree]| ERROR: /read access through .* is forbidden/ + //~[tree]| ERROR: /reborrow through .* is forbidden/ } diff --git a/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr b/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr index 87b4fcba4d..242ae0de73 100644 --- a/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr +++ b/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr @@ -1,12 +1,12 @@ -error: Undefined Behavior: read access through is forbidden +error: Undefined Behavior: reborrow through is forbidden --> $DIR/pass_invalid_shr_option.rs:LL:CC | LL | foo(some_xref); - | ^^^^^^^^^ read access through is forbidden + | ^^^^^^^^^ reborrow through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids child read accesses + = help: the conflicting tag has state Disabled which forbids this reborrow (acting as a child read access) help: the accessed tag was created here --> $DIR/pass_invalid_shr_option.rs:LL:CC | @@ -22,7 +22,7 @@ help: the conflicting tag later transitioned to Disabled due to a foreign | LL | unsafe { *xraw = 42 }; // unfreeze | ^^^^^^^^^^ - = help: this corresponds to a loss of read permissions + = help: this transition corresponds to a loss of read permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/pass_invalid_shr_option.rs:LL:CC diff --git a/tests/fail/both_borrows/pass_invalid_shr_tuple.rs b/tests/fail/both_borrows/pass_invalid_shr_tuple.rs index d22c0f6e8e..2244ab41d7 100644 --- a/tests/fail/both_borrows/pass_invalid_shr_tuple.rs +++ b/tests/fail/both_borrows/pass_invalid_shr_tuple.rs @@ -12,5 +12,5 @@ fn main() { unsafe { *xraw0 = 42 }; // unfreeze foo(pair_xref); //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ - //~[tree]| ERROR: /read access through .* is forbidden/ + //~[tree]| ERROR: /reborrow through .* is forbidden/ } diff --git a/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr b/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr index 605120e59d..87b9b1361b 100644 --- a/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr +++ b/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr @@ -1,12 +1,12 @@ -error: Undefined Behavior: read access through is forbidden +error: Undefined Behavior: reborrow through is forbidden --> $DIR/pass_invalid_shr_tuple.rs:LL:CC | LL | foo(pair_xref); - | ^^^^^^^^^ read access through is forbidden + | ^^^^^^^^^ reborrow through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids child read accesses + = help: the conflicting tag has state Disabled which forbids this reborrow (acting as a child read access) help: the accessed tag was created here --> $DIR/pass_invalid_shr_tuple.rs:LL:CC | @@ -22,7 +22,7 @@ help: the conflicting tag later transitioned to Disabled due to a foreign | LL | unsafe { *xraw0 = 42 }; // unfreeze | ^^^^^^^^^^^ - = help: this corresponds to a loss of read permissions + = help: this transition corresponds to a loss of read permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/pass_invalid_shr_tuple.rs:LL:CC diff --git a/tests/fail/both_borrows/return_invalid_shr.rs b/tests/fail/both_borrows/return_invalid_shr.rs index 99945e5699..43163ab1b8 100644 --- a/tests/fail/both_borrows/return_invalid_shr.rs +++ b/tests/fail/both_borrows/return_invalid_shr.rs @@ -8,7 +8,7 @@ fn foo(x: &mut (i32, i32)) -> &i32 { unsafe { *xraw = (42, 23) }; // unfreeze ret //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ - //~[tree]| ERROR: /read access through .* is forbidden/ + //~[tree]| ERROR: /reborrow through .* is forbidden/ } fn main() { diff --git a/tests/fail/both_borrows/return_invalid_shr.tree.stderr b/tests/fail/both_borrows/return_invalid_shr.tree.stderr index 1b3a5bfbd1..4c016ea621 100644 --- a/tests/fail/both_borrows/return_invalid_shr.tree.stderr +++ b/tests/fail/both_borrows/return_invalid_shr.tree.stderr @@ -1,11 +1,11 @@ -error: Undefined Behavior: read access through is forbidden +error: Undefined Behavior: reborrow through is forbidden --> $DIR/return_invalid_shr.rs:LL:CC | LL | ret - | ^^^ read access through is forbidden + | ^^^ reborrow through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental - = help: the accessed tag has state Disabled which forbids child read accesses + = help: the accessed tag has state Disabled which forbids this reborrow (acting as a child read access) help: the accessed tag was created here, in the initial state Frozen --> $DIR/return_invalid_shr.rs:LL:CC | @@ -16,7 +16,7 @@ help: the accessed tag later transitioned to Disabled due to a foreign wri | LL | unsafe { *xraw = (42, 23) }; // unfreeze | ^^^^^^^^^^^^^^^^ - = help: this corresponds to a loss of read permissions + = help: this transition corresponds to a loss of read permissions = note: BACKTRACE (of the first span): = note: inside `foo` at $DIR/return_invalid_shr.rs:LL:CC note: inside `main` diff --git a/tests/fail/both_borrows/return_invalid_shr_option.rs b/tests/fail/both_borrows/return_invalid_shr_option.rs index e735ce9bed..a5b53c57a9 100644 --- a/tests/fail/both_borrows/return_invalid_shr_option.rs +++ b/tests/fail/both_borrows/return_invalid_shr_option.rs @@ -8,7 +8,7 @@ fn foo(x: &mut (i32, i32)) -> Option<&i32> { unsafe { *xraw = (42, 23) }; // unfreeze ret //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ - //~[tree]| ERROR: /read access through .* is forbidden/ + //~[tree]| ERROR: /reborrow through .* is forbidden/ } fn main() { diff --git a/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr b/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr index 4e920b34d7..b9ef6dea6e 100644 --- a/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr +++ b/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr @@ -1,12 +1,12 @@ -error: Undefined Behavior: read access through is forbidden +error: Undefined Behavior: reborrow through is forbidden --> $DIR/return_invalid_shr_option.rs:LL:CC | LL | ret - | ^^^ read access through is forbidden + | ^^^ reborrow through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids child read accesses + = help: the conflicting tag has state Disabled which forbids this reborrow (acting as a child read access) help: the accessed tag was created here --> $DIR/return_invalid_shr_option.rs:LL:CC | @@ -22,7 +22,7 @@ help: the conflicting tag later transitioned to Disabled due to a foreign | LL | unsafe { *xraw = (42, 23) }; // unfreeze | ^^^^^^^^^^^^^^^^ - = help: this corresponds to a loss of read permissions + = help: this transition corresponds to a loss of read permissions = note: BACKTRACE (of the first span): = note: inside `foo` at $DIR/return_invalid_shr_option.rs:LL:CC note: inside `main` diff --git a/tests/fail/both_borrows/return_invalid_shr_tuple.rs b/tests/fail/both_borrows/return_invalid_shr_tuple.rs index 90a4021502..925525aaa5 100644 --- a/tests/fail/both_borrows/return_invalid_shr_tuple.rs +++ b/tests/fail/both_borrows/return_invalid_shr_tuple.rs @@ -8,7 +8,7 @@ fn foo(x: &mut (i32, i32)) -> (&i32,) { unsafe { *xraw = (42, 23) }; // unfreeze ret //~[stack]^ ERROR: /retag .* tag does not exist in the borrow stack/ - //~[tree]| ERROR: /read access through .* is forbidden/ + //~[tree]| ERROR: /reborrow through .* is forbidden/ } fn main() { diff --git a/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr b/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr index b60614e98f..c3fd124e6c 100644 --- a/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr +++ b/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr @@ -1,12 +1,12 @@ -error: Undefined Behavior: read access through is forbidden +error: Undefined Behavior: reborrow through is forbidden --> $DIR/return_invalid_shr_tuple.rs:LL:CC | LL | ret - | ^^^ read access through is forbidden + | ^^^ reborrow through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids child read accesses + = help: the conflicting tag has state Disabled which forbids this reborrow (acting as a child read access) help: the accessed tag was created here --> $DIR/return_invalid_shr_tuple.rs:LL:CC | @@ -22,7 +22,7 @@ help: the conflicting tag later transitioned to Disabled due to a foreign | LL | unsafe { *xraw = (42, 23) }; // unfreeze | ^^^^^^^^^^^^^^^^ - = help: this corresponds to a loss of read permissions + = help: this transition corresponds to a loss of read permissions = note: BACKTRACE (of the first span): = note: inside `foo` at $DIR/return_invalid_shr_tuple.rs:LL:CC note: inside `main` diff --git a/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr b/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr index 893e38f5b6..9aeaa7ff07 100644 --- a/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr +++ b/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr @@ -6,7 +6,7 @@ LL | *(x as *const i32 as *mut i32) = 7; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Frozen which forbids child write accesses + = help: the conflicting tag has state Frozen which forbids this child write access help: the accessed tag was created here --> $DIR/shr_frozen_violation1.rs:LL:CC | diff --git a/tests/fail/both_borrows/shr_frozen_violation2.tree.stderr b/tests/fail/both_borrows/shr_frozen_violation2.tree.stderr index 5fd5ba2fe3..0269f3ab64 100644 --- a/tests/fail/both_borrows/shr_frozen_violation2.tree.stderr +++ b/tests/fail/both_borrows/shr_frozen_violation2.tree.stderr @@ -5,7 +5,7 @@ LL | let _val = *frozen; | ^^^^^^^ read access through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental - = help: the accessed tag has state Disabled which forbids child read accesses + = help: the accessed tag has state Disabled which forbids this child read access help: the accessed tag was created here, in the initial state Frozen --> $DIR/shr_frozen_violation2.rs:LL:CC | @@ -16,7 +16,7 @@ help: the accessed tag later transitioned to Disabled due to a foreign wri | LL | x = 1; | ^^^^^ - = help: this corresponds to a loss of read permissions + = help: this transition corresponds to a loss of read permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/shr_frozen_violation2.rs:LL:CC diff --git a/tests/fail/tree_borrows/alternate-read-write.stderr b/tests/fail/tree_borrows/alternate-read-write.stderr index d82cb8288a..fcbc2e1278 100644 --- a/tests/fail/tree_borrows/alternate-read-write.stderr +++ b/tests/fail/tree_borrows/alternate-read-write.stderr @@ -6,7 +6,7 @@ LL | *y += 1; // Failure | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Frozen which forbids child write accesses + = help: the conflicting tag has state Frozen which forbids this child write access help: the accessed tag was created here --> $DIR/alternate-read-write.rs:LL:CC | @@ -22,13 +22,13 @@ help: the conflicting tag later transitioned to Active due to a child writ | LL | *y += 1; // Success | ^^^^^^^ - = help: this corresponds to the first write to a 2-phase borrowed mutable reference + = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference help: the conflicting tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1] --> $DIR/alternate-read-write.rs:LL:CC | LL | let _val = *x; | ^^ - = help: this corresponds to a loss of write permissions + = help: this transition corresponds to a loss of write permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/alternate-read-write.rs:LL:CC diff --git a/tests/fail/tree_borrows/error-range.stderr b/tests/fail/tree_borrows/error-range.stderr index 10c2e95ca2..b58dcd7572 100644 --- a/tests/fail/tree_borrows/error-range.stderr +++ b/tests/fail/tree_borrows/error-range.stderr @@ -6,7 +6,7 @@ LL | rmut[5] += 1; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids child read accesses + = help: the conflicting tag has state Disabled which forbids this child read access help: the accessed tag was created here --> $DIR/error-range.rs:LL:CC | @@ -22,7 +22,7 @@ help: the conflicting tag later transitioned to Disabled due to a foreign | LL | data[5] = 1; | ^^^^^^^^^^^ - = help: this corresponds to a loss of read permissions + = help: this transition corresponds to a loss of read permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/error-range.rs:LL:CC diff --git a/tests/fail/tree_borrows/fnentry_invalidation.stderr b/tests/fail/tree_borrows/fnentry_invalidation.stderr index dacd27f0d3..227c465b8f 100644 --- a/tests/fail/tree_borrows/fnentry_invalidation.stderr +++ b/tests/fail/tree_borrows/fnentry_invalidation.stderr @@ -5,7 +5,7 @@ LL | *z = 2; | ^^^^^^ write access through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental - = help: the accessed tag has state Frozen which forbids child write accesses + = help: the accessed tag has state Frozen which forbids this child write access help: the accessed tag was created here, in the initial state Reserved --> $DIR/fnentry_invalidation.rs:LL:CC | @@ -16,13 +16,13 @@ help: the accessed tag later transitioned to Active due to a child write a | LL | *z = 1; | ^^^^^^ - = help: this corresponds to the first write to a 2-phase borrowed mutable reference -help: the accessed tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4] + = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference +help: the accessed tag later transitioned to Frozen due to a reborrow (acting as a foreign read access) at offsets [0x0..0x4] --> $DIR/fnentry_invalidation.rs:LL:CC | LL | x.do_bad(); | ^^^^^^^^^^ - = help: this corresponds to a loss of write permissions + = help: this transition corresponds to a loss of write permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/fnentry_invalidation.rs:LL:CC diff --git a/tests/fail/tree_borrows/fragile-data-race.stderr b/tests/fail/tree_borrows/fragile-data-race.stderr index 86fdf97ac4..910f51ba8a 100644 --- a/tests/fail/tree_borrows/fragile-data-race.stderr +++ b/tests/fail/tree_borrows/fragile-data-race.stderr @@ -6,7 +6,7 @@ LL | unsafe { *p = 1 }; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Frozen which forbids child write accesses + = help: the conflicting tag has state Frozen which forbids this child write access help: the accessed tag was created here --> $DIR/fragile-data-race.rs:LL:CC | @@ -17,12 +17,12 @@ help: the conflicting tag was created here, in the initial state Reserved | LL | pub fn catch_unwind R + UnwindSafe, R>(f: F) -> Result { | ^ -help: the conflicting tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1] +help: the conflicting tag later transitioned to Frozen due to a reborrow (acting as a foreign read access) at offsets [0x0..0x1] --> RUSTLIB/core/src/ptr/mod.rs:LL:CC | LL | crate::intrinsics::read_via_copy(src) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: this corresponds to a loss of write permissions + = help: this transition corresponds to a loss of write permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/fragile-data-race.rs:LL:CC diff --git a/tests/fail/tree_borrows/outside-range.stderr b/tests/fail/tree_borrows/outside-range.stderr index 14696c704f..0feafb1a71 100644 --- a/tests/fail/tree_borrows/outside-range.stderr +++ b/tests/fail/tree_borrows/outside-range.stderr @@ -6,8 +6,8 @@ LL | *y.add(3) = 42; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is foreign to the protected tag (i.e., it is not a child) - = help: the access would cause the protected tag to transition from Reserved to Disabled - = help: this is a loss of read and write permissions, which is not allowed for protected tags + = help: this foreign write access would cause the protected tag to transition from Reserved to Disabled + = help: this transition would be a loss of read and write permissions, which is not allowed for protected tags help: the accessed tag was created here --> $DIR/outside-range.rs:LL:CC | diff --git a/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr b/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr index cc7ed97b38..addba9d809 100644 --- a/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr +++ b/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr @@ -5,7 +5,7 @@ LL | *ptr = 0; | ^^^^^^^^ write access through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental - = help: the accessed tag has state Frozen which forbids child write accesses + = help: the accessed tag has state Frozen which forbids this child write access help: the accessed tag was created here, in the initial state Reserved --> $DIR/parent_read_freezes_raw_mut.rs:LL:CC | @@ -16,13 +16,13 @@ help: the accessed tag later transitioned to Active due to a child write a | LL | *ptr = 0; // Write | ^^^^^^^^ - = help: this corresponds to the first write to a 2-phase borrowed mutable reference -help: the accessed tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1] + = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference +help: the accessed tag later transitioned to Frozen due to a reborrow (acting as a foreign read access) at offsets [0x0..0x1] --> $DIR/parent_read_freezes_raw_mut.rs:LL:CC | LL | assert_eq!(root, 0); // Parent Read | ^^^^^^^^^^^^^^^^^^^ - = help: this corresponds to a loss of write permissions + = help: this transition corresponds to a loss of write permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/parent_read_freezes_raw_mut.rs:LL:CC = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/fail/tree_borrows/pass_invalid_mut.stderr b/tests/fail/tree_borrows/pass_invalid_mut.stderr index 4e5657f8b7..056ac6db55 100644 --- a/tests/fail/tree_borrows/pass_invalid_mut.stderr +++ b/tests/fail/tree_borrows/pass_invalid_mut.stderr @@ -6,7 +6,7 @@ LL | *nope = 31; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Frozen which forbids child write accesses + = help: the conflicting tag has state Frozen which forbids this child write access help: the accessed tag was created here --> $DIR/pass_invalid_mut.rs:LL:CC | @@ -22,13 +22,13 @@ help: the conflicting tag later transitioned to Active due to a child writ | LL | *xref = 18; // activate xref | ^^^^^^^^^^ - = help: this corresponds to the first write to a 2-phase borrowed mutable reference + = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference help: the conflicting tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4] --> $DIR/pass_invalid_mut.rs:LL:CC | LL | let _val = unsafe { *xraw }; // invalidate xref for writing | ^^^^^ - = help: this corresponds to a loss of write permissions + = help: this transition corresponds to a loss of write permissions = note: BACKTRACE (of the first span): = note: inside `foo` at $DIR/pass_invalid_mut.rs:LL:CC note: inside `main` diff --git a/tests/fail/tree_borrows/reserved/cell-protected-write.stderr b/tests/fail/tree_borrows/reserved/cell-protected-write.stderr index b85793ff06..5e91077962 100644 --- a/tests/fail/tree_borrows/reserved/cell-protected-write.stderr +++ b/tests/fail/tree_borrows/reserved/cell-protected-write.stderr @@ -16,8 +16,8 @@ LL | *y = 1; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag (y, callee:y, caller:y) is foreign to the protected tag (callee:x) (i.e., it is not a child) - = help: the access would cause the protected tag (callee:x) to transition from Reserved to Disabled - = help: this is a loss of read and write permissions, which is not allowed for protected tags + = help: this foreign write access would cause the protected tag (callee:x) to transition from Reserved to Disabled + = help: this transition would be a loss of read and write permissions, which is not allowed for protected tags help: the accessed tag was created here --> $DIR/cell-protected-write.rs:LL:CC | diff --git a/tests/fail/tree_borrows/reserved/int-protected-write.stderr b/tests/fail/tree_borrows/reserved/int-protected-write.stderr index 5de7dc0c7c..e28aac306c 100644 --- a/tests/fail/tree_borrows/reserved/int-protected-write.stderr +++ b/tests/fail/tree_borrows/reserved/int-protected-write.stderr @@ -16,8 +16,8 @@ LL | *y = 0; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag (y, callee:y, caller:y) is foreign to the protected tag (callee:x) (i.e., it is not a child) - = help: the access would cause the protected tag (callee:x) to transition from Reserved to Disabled - = help: this is a loss of read and write permissions, which is not allowed for protected tags + = help: this foreign write access would cause the protected tag (callee:x) to transition from Reserved to Disabled + = help: this transition would be a loss of read and write permissions, which is not allowed for protected tags help: the accessed tag was created here --> $DIR/int-protected-write.rs:LL:CC | diff --git a/tests/fail/tree_borrows/return_invalid_mut.stderr b/tests/fail/tree_borrows/return_invalid_mut.stderr index 3d050d209c..978e5a340f 100644 --- a/tests/fail/tree_borrows/return_invalid_mut.stderr +++ b/tests/fail/tree_borrows/return_invalid_mut.stderr @@ -6,7 +6,7 @@ LL | *ret = 3; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag is a child of the conflicting tag - = help: the conflicting tag has state Frozen which forbids child write accesses + = help: the conflicting tag has state Frozen which forbids this child write access help: the accessed tag was created here --> $DIR/return_invalid_mut.rs:LL:CC | @@ -22,13 +22,13 @@ help: the conflicting tag later transitioned to Active due to a child writ | LL | *ret = *ret; // activate | ^^^^^^^^^^^ - = help: this corresponds to the first write to a 2-phase borrowed mutable reference + = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference help: the conflicting tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x8] --> $DIR/return_invalid_mut.rs:LL:CC | LL | let _val = unsafe { *xraw }; // invalidate xref for writing | ^^^^^ - = help: this corresponds to a loss of write permissions + = help: this transition corresponds to a loss of write permissions = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/return_invalid_mut.rs:LL:CC diff --git a/tests/fail/tree_borrows/write-during-2phase.rs b/tests/fail/tree_borrows/write-during-2phase.rs index 6695d36306..3f8c9b6219 100644 --- a/tests/fail/tree_borrows/write-during-2phase.rs +++ b/tests/fail/tree_borrows/write-during-2phase.rs @@ -9,7 +9,7 @@ struct Foo(u64); impl Foo { #[rustfmt::skip] // rustfmt is wrong about which line contains an error - fn add(&mut self, n: u64) -> u64 { //~ ERROR: /read access through .* is forbidden/ + fn add(&mut self, n: u64) -> u64 { //~ ERROR: /reborrow through .* is forbidden/ self.0 + n } } diff --git a/tests/fail/tree_borrows/write-during-2phase.stderr b/tests/fail/tree_borrows/write-during-2phase.stderr index 898f6108cc..ce71468425 100644 --- a/tests/fail/tree_borrows/write-during-2phase.stderr +++ b/tests/fail/tree_borrows/write-during-2phase.stderr @@ -1,11 +1,11 @@ -error: Undefined Behavior: read access through is forbidden +error: Undefined Behavior: reborrow through is forbidden --> $DIR/write-during-2phase.rs:LL:CC | LL | fn add(&mut self, n: u64) -> u64 { - | ^^^^^^^^^ read access through is forbidden + | ^^^^^^^^^ reborrow through is forbidden | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental - = help: the accessed tag has state Disabled which forbids child read accesses + = help: the accessed tag has state Disabled which forbids this reborrow (acting as a child read access) help: the accessed tag was created here, in the initial state Reserved --> $DIR/write-during-2phase.rs:LL:CC | @@ -23,7 +23,7 @@ help: the accessed tag later transitioned to Disabled due to a foreign wri | LL | *inner = 42; | ^^^^^^^^^^^ - = help: this corresponds to a loss of read and write permissions + = help: this transition corresponds to a loss of read and write permissions = note: BACKTRACE (of the first span): = note: inside `Foo::add` at $DIR/write-during-2phase.rs:LL:CC note: inside `main` From f00a93d3e3ecd3766b3a8edeca882ef1a3a6ab1b Mon Sep 17 00:00:00 2001 From: Neven Villani Date: Mon, 5 Jun 2023 21:14:48 +0200 Subject: [PATCH 08/32] Revert error in doc comment Also punctuation --- src/borrow_tracker/tree_borrows/diagnostics.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/borrow_tracker/tree_borrows/diagnostics.rs index 1a7f14f0ab..a87a4bbdda 100644 --- a/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -45,13 +45,13 @@ impl AccessCause { /// Complete data for an event: #[derive(Clone, Debug)] pub struct Event { - /// Transformation of permissions that occured because of this event + /// Transformation of permissions that occured because of this event. pub transition: PermTransition, - /// Kind of the access that triggered this event + /// Kind of the access that triggered this event. pub access_cause: AccessCause, - /// Relative position of the tag to the one used for the access + /// Relative position of the tag to the one used for the access. pub is_foreign: bool, - /// Whether this access was explicit or inserted implicitly by Tree Borrows. + /// User-visible range of the access. pub access_range: AllocRange, /// The transition recorded by this event only occured on a subrange of /// `access_range`: a single access on `access_range` triggers several events, @@ -66,7 +66,7 @@ pub struct Event { /// the `TbError`, which should satisfy /// `event.transition_range.contains(error.error_offset)`. pub transition_range: Range, - /// Line of code that triggered this event + /// Line of code that triggered this event. pub span: Span, } From 8404d3734b8c36e0802c5e425d808554841ab81c Mon Sep 17 00:00:00 2001 From: Neven Villani Date: Sat, 10 Jun 2023 14:50:59 +0200 Subject: [PATCH 09/32] box_exclusive_violation --- .../box_exclusive_violation1.rs | 7 +++- .../box_exclusive_violation1.stack.stderr} | 0 .../box_exclusive_violation1.tree.stderr | 42 +++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) rename tests/fail/{stacked_borrows => both_borrows}/box_exclusive_violation1.rs (70%) rename tests/fail/{stacked_borrows/box_exclusive_violation1.stderr => both_borrows/box_exclusive_violation1.stack.stderr} (100%) create mode 100644 tests/fail/both_borrows/box_exclusive_violation1.tree.stderr diff --git a/tests/fail/stacked_borrows/box_exclusive_violation1.rs b/tests/fail/both_borrows/box_exclusive_violation1.rs similarity index 70% rename from tests/fail/stacked_borrows/box_exclusive_violation1.rs rename to tests/fail/both_borrows/box_exclusive_violation1.rs index 87a6b7bbd6..4936c1a901 100644 --- a/tests/fail/stacked_borrows/box_exclusive_violation1.rs +++ b/tests/fail/both_borrows/box_exclusive_violation1.rs @@ -1,3 +1,6 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + fn demo_box_advanced_unique(mut our: Box) -> i32 { unknown_code_1(&*our); @@ -24,7 +27,9 @@ fn unknown_code_1(x: &i32) { fn unknown_code_2() { unsafe { - *LEAK = 7; //~ ERROR: /write access .* tag does not exist in the borrow stack/ + *LEAK = 7; + //~[stack]^ ERROR: /write access .* tag does not exist in the borrow stack/ + //~[tree]| ERROR: /write access through .* is forbidden/ } } diff --git a/tests/fail/stacked_borrows/box_exclusive_violation1.stderr b/tests/fail/both_borrows/box_exclusive_violation1.stack.stderr similarity index 100% rename from tests/fail/stacked_borrows/box_exclusive_violation1.stderr rename to tests/fail/both_borrows/box_exclusive_violation1.stack.stderr diff --git a/tests/fail/both_borrows/box_exclusive_violation1.tree.stderr b/tests/fail/both_borrows/box_exclusive_violation1.tree.stderr new file mode 100644 index 0000000000..97f82db6fe --- /dev/null +++ b/tests/fail/both_borrows/box_exclusive_violation1.tree.stderr @@ -0,0 +1,42 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/box_exclusive_violation1.rs:LL:CC + | +LL | *LEAK = 7; + | ^^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Disabled which forbids this child write access +help: the accessed tag was created here + --> $DIR/box_exclusive_violation1.rs:LL:CC + | +LL | fn unknown_code_1(x: &i32) { + | ^ +help: the conflicting tag was created here, in the initial state Frozen + --> $DIR/box_exclusive_violation1.rs:LL:CC + | +LL | unknown_code_1(&*our); + | ^^^^^ +help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] + --> $DIR/box_exclusive_violation1.rs:LL:CC + | +LL | *our = 5; + | ^^^^^^^^ + = help: this transition corresponds to a loss of read permissions + = note: BACKTRACE (of the first span): + = note: inside `unknown_code_2` at $DIR/box_exclusive_violation1.rs:LL:CC +note: inside `demo_box_advanced_unique` + --> $DIR/box_exclusive_violation1.rs:LL:CC + | +LL | unknown_code_2(); + | ^^^^^^^^^^^^^^^^ +note: inside `main` + --> $DIR/box_exclusive_violation1.rs:LL:CC + | +LL | demo_box_advanced_unique(Box::new(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + From 80b0935fe0386261460a67719914c734c2397546 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 11 Jun 2023 22:08:00 +0200 Subject: [PATCH 10/32] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index ee815ae6f8..918c738ed1 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -33c3d101280c8eb3cd8af421bfb56a8afcc3881d +37998ab508d5d9fa0d465d7b535dc673087dda8f From 9e6f0b0f92a257315afcb6b9e3142e473a95a6de Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 15 Jun 2023 06:30:20 +0000 Subject: [PATCH 11/32] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 918c738ed1..77460a7c89 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -37998ab508d5d9fa0d465d7b535dc673087dda8f +314c39d2ea07d8b50649149358ebeb1a6bd09179 From 2bc1baaa5dfaf1a28426a94436c3a8bdc38d02b8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 16 Jun 2023 08:44:41 +0200 Subject: [PATCH 12/32] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 77460a7c89..4b6de3601c 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -314c39d2ea07d8b50649149358ebeb1a6bd09179 +c84d5e7078435a0ddd8770c3530fe7fdbf026ec4 From 90111b8873c86b92370b81cdd68590cf5b98b8fc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 16 Jun 2023 08:22:57 +0200 Subject: [PATCH 13/32] add tests for panicky drop in thread_local destructor --- .../panic/thread_local_const_drop_panic.rs | 17 ++++++++++++ .../thread_local_const_drop_panic.stderr | 26 +++++++++++++++++++ tests/fail/panic/thread_local_drop_panic.rs | 17 ++++++++++++ .../fail/panic/thread_local_drop_panic.stderr | 15 +++++++++++ 4 files changed, 75 insertions(+) create mode 100644 tests/fail/panic/thread_local_const_drop_panic.rs create mode 100644 tests/fail/panic/thread_local_const_drop_panic.stderr create mode 100644 tests/fail/panic/thread_local_drop_panic.rs create mode 100644 tests/fail/panic/thread_local_drop_panic.stderr diff --git a/tests/fail/panic/thread_local_const_drop_panic.rs b/tests/fail/panic/thread_local_const_drop_panic.rs new file mode 100644 index 0000000000..53d169967f --- /dev/null +++ b/tests/fail/panic/thread_local_const_drop_panic.rs @@ -0,0 +1,17 @@ +//@error-in-other-file: aborted execution + +pub struct NoisyDrop {} + +impl Drop for NoisyDrop { + fn drop(&mut self) { + panic!("ow"); + } +} + +thread_local! { + pub static NOISY: NoisyDrop = const { NoisyDrop {} }; +} + +fn main() { + NOISY.with(|_| ()); +} diff --git a/tests/fail/panic/thread_local_const_drop_panic.stderr b/tests/fail/panic/thread_local_const_drop_panic.stderr new file mode 100644 index 0000000000..122af7babe --- /dev/null +++ b/tests/fail/panic/thread_local_const_drop_panic.stderr @@ -0,0 +1,26 @@ +thread '' panicked at 'ow', $DIR/thread_local_const_drop_panic.rs:LL:CC +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +fatal runtime error: thread local panicked on drop +error: abnormal termination: the program aborted execution + --> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + | +LL | unsafe { libc::abort() } + | ^^^^^^^^^^^^^ the program aborted execution + | + = note: inside `std::sys::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + = note: inside `::drop` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::ptr::drop_in_place:: - shim(Some(std::thread::local_impl::abort_on_dtor_unwind::DtorUnwindGuard))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC + = note: inside `RUSTLIB/std/src/sys/PLATFORM/thread_local/fast_local.rs:LL:CC]>` at RUSTLIB/std/src/sys/PLATFORM/thread_local/mod.rs:LL:CC +note: inside `NOISY::__getit::destroy` + --> $DIR/thread_local_const_drop_panic.rs:LL:CC + | +LL | / thread_local! { +LL | | pub static NOISY: NoisyDrop = const { NoisyDrop {} }; +LL | | } + | |_^ + = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/panic/thread_local_drop_panic.rs b/tests/fail/panic/thread_local_drop_panic.rs new file mode 100644 index 0000000000..7880aeff10 --- /dev/null +++ b/tests/fail/panic/thread_local_drop_panic.rs @@ -0,0 +1,17 @@ +//@error-in-other-file: aborted execution + +pub struct NoisyDrop {} + +impl Drop for NoisyDrop { + fn drop(&mut self) { + panic!("ow"); + } +} + +thread_local! { + pub static NOISY: NoisyDrop = NoisyDrop {}; +} + +fn main() { + NOISY.with(|_| ()); +} diff --git a/tests/fail/panic/thread_local_drop_panic.stderr b/tests/fail/panic/thread_local_drop_panic.stderr new file mode 100644 index 0000000000..6b6627331b --- /dev/null +++ b/tests/fail/panic/thread_local_drop_panic.stderr @@ -0,0 +1,15 @@ +thread '' panicked at 'ow', $DIR/thread_local_drop_panic.rs:LL:CC +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +fatal runtime error: thread local panicked on drop +error: abnormal termination: the program aborted execution + --> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + | +LL | unsafe { libc::abort() } + | ^^^^^^^^^^^^^ the program aborted execution + | + = note: inside `std::sys::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC + = note: inside `std::sys::PLATFORM::thread_local::fast_local::destroy_value::` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::sys_common::thread_local_dtor::register_dtor_fallback::run_dtors` at RUSTLIB/std/src/sys_common/thread_local_dtor.rs:LL:CC + +error: aborting due to previous error + From ad615b64839e7868df04794a1f74cb9cdc83289a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 16 Jun 2023 12:17:33 +0200 Subject: [PATCH 14/32] make test work cross-platform --- .../panic/thread_local_const_drop_panic.rs | 5 +++++ .../thread_local_const_drop_panic.stderr | 22 +------------------ tests/fail/panic/thread_local_drop_panic.rs | 3 +++ .../fail/panic/thread_local_drop_panic.stderr | 11 +--------- 4 files changed, 10 insertions(+), 31 deletions(-) diff --git a/tests/fail/panic/thread_local_const_drop_panic.rs b/tests/fail/panic/thread_local_const_drop_panic.rs index 53d169967f..93ad42ea1c 100644 --- a/tests/fail/panic/thread_local_const_drop_panic.rs +++ b/tests/fail/panic/thread_local_const_drop_panic.rs @@ -1,4 +1,9 @@ //@error-in-other-file: aborted execution +// Backtraces vary wildly between platforms, we have to normalize away almost the entire thing. +// Full backtraces avoid annoying empty line differences. +//@compile-flags: -Zmiri-backtrace=full +//@normalize-stderr-test: "'main'|''" -> "$$NAME" +//@normalize-stderr-test: ".*(note|-->|\|).*\n" -> "" pub struct NoisyDrop {} diff --git a/tests/fail/panic/thread_local_const_drop_panic.stderr b/tests/fail/panic/thread_local_const_drop_panic.stderr index 122af7babe..47962e44ad 100644 --- a/tests/fail/panic/thread_local_const_drop_panic.stderr +++ b/tests/fail/panic/thread_local_const_drop_panic.stderr @@ -1,26 +1,6 @@ -thread '' panicked at 'ow', $DIR/thread_local_const_drop_panic.rs:LL:CC -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +thread $NAME panicked at 'ow', $DIR/thread_local_const_drop_panic.rs:LL:CC fatal runtime error: thread local panicked on drop error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC - | -LL | unsafe { libc::abort() } - | ^^^^^^^^^^^^^ the program aborted execution - | - = note: inside `std::sys::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC - = note: inside `::drop` at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside `std::ptr::drop_in_place:: - shim(Some(std::thread::local_impl::abort_on_dtor_unwind::DtorUnwindGuard))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC - = note: inside `RUSTLIB/std/src/sys/PLATFORM/thread_local/fast_local.rs:LL:CC]>` at RUSTLIB/std/src/sys/PLATFORM/thread_local/mod.rs:LL:CC -note: inside `NOISY::__getit::destroy` - --> $DIR/thread_local_const_drop_panic.rs:LL:CC - | -LL | / thread_local! { -LL | | pub static NOISY: NoisyDrop = const { NoisyDrop {} }; -LL | | } - | |_^ - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace error: aborting due to previous error diff --git a/tests/fail/panic/thread_local_drop_panic.rs b/tests/fail/panic/thread_local_drop_panic.rs index 7880aeff10..107d70a3b3 100644 --- a/tests/fail/panic/thread_local_drop_panic.rs +++ b/tests/fail/panic/thread_local_drop_panic.rs @@ -1,4 +1,7 @@ //@error-in-other-file: aborted execution +// Backtraces vary wildly between platforms, we have to normalize away almost the entire thing +//@normalize-stderr-test: "'main'|''" -> "$$NAME" +//@normalize-stderr-test: ".*(note|-->|\|).*\n" -> "" pub struct NoisyDrop {} diff --git a/tests/fail/panic/thread_local_drop_panic.stderr b/tests/fail/panic/thread_local_drop_panic.stderr index 6b6627331b..2a56e2b9c8 100644 --- a/tests/fail/panic/thread_local_drop_panic.stderr +++ b/tests/fail/panic/thread_local_drop_panic.stderr @@ -1,15 +1,6 @@ -thread '' panicked at 'ow', $DIR/thread_local_drop_panic.rs:LL:CC -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +thread $NAME panicked at 'ow', $DIR/thread_local_drop_panic.rs:LL:CC fatal runtime error: thread local panicked on drop error: abnormal termination: the program aborted execution - --> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC - | -LL | unsafe { libc::abort() } - | ^^^^^^^^^^^^^ the program aborted execution - | - = note: inside `std::sys::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC - = note: inside `std::sys::PLATFORM::thread_local::fast_local::destroy_value::` at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside `std::sys_common::thread_local_dtor::register_dtor_fallback::run_dtors` at RUSTLIB/std/src/sys_common/thread_local_dtor.rs:LL:CC error: aborting due to previous error From 0ec710b25e2a56a0aed1793fd83dcb6ef6402b71 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 16 Jun 2023 15:56:05 +0200 Subject: [PATCH 15/32] comment tweaks --- src/shims/foreign_items.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index d52582ce90..f4e91c30d9 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -417,8 +417,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // // First thing: load all the arguments. Details depend on the shim. // let arg1 = this.read_scalar(arg1)?.to_u32()?; // let arg2 = this.read_pointer(arg2)?; // when you need to work with the pointer directly - // let arg3 = this.deref_operand(arg3)?; // when you want to load/store through the pointer - // let arg4 = this.deref_operand_as(arg4, this.libc_ty_layout("some_libc_struct")?) + // let arg3 = this.deref_operand_as(arg3, this.libc_ty_layout("some_libc_struct"))?; // when you want to load/store + // // through the pointer and supply the type information yourself + // let arg4 = this.deref_operand(arg4)?; // when you want to load/store through the pointer and trust + // // the user-given type (which you shouldn't usually do) // // // ... // From 0002f33c706a3ce366e9f0a283da579b22d13e6d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 Jun 2023 17:42:11 +0200 Subject: [PATCH 16/32] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 4b6de3601c..aa38296089 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -c84d5e7078435a0ddd8770c3530fe7fdbf026ec4 +7513407ac8e673f536d34743fe393bd8b7c45441 From cd5918463ed2de8531ee19df837a8e70de1a7512 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 18 Jun 2023 20:04:56 +0200 Subject: [PATCH 17/32] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index aa38296089..73385aee85 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -7513407ac8e673f536d34743fe393bd8b7c45441 +677710eaf0a0bdb008959ee8717c9fe1c6d187b3 From c0cfea518cb845ec3ba3480ba31e4bd278ddd51d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 19 Jun 2023 10:09:45 +0200 Subject: [PATCH 18/32] bless new tests --- tests/fail/both_borrows/newtype_pair_retagging.tree.stderr | 2 +- tests/fail/both_borrows/newtype_retagging.tree.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr b/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr index 81054972a9..00b2708a65 100644 --- a/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr +++ b/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr @@ -27,7 +27,7 @@ LL | || drop(Box::from_raw(ptr)), = note: BACKTRACE (of the first span): = note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `::deallocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC - = note: inside `alloc::alloc::box_free::` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside ` as std::ops::Drop>::drop` at RUSTLIB/alloc/src/boxed.rs:LL:CC = note: inside `std::ptr::drop_in_place::> - shim(Some(std::boxed::Box))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC = note: inside `std::mem::drop::>` at RUSTLIB/core/src/mem/mod.rs:LL:CC note: inside closure diff --git a/tests/fail/both_borrows/newtype_retagging.tree.stderr b/tests/fail/both_borrows/newtype_retagging.tree.stderr index 3a3b24fd7d..aed2e7abeb 100644 --- a/tests/fail/both_borrows/newtype_retagging.tree.stderr +++ b/tests/fail/both_borrows/newtype_retagging.tree.stderr @@ -27,7 +27,7 @@ LL | || drop(Box::from_raw(ptr)), = note: BACKTRACE (of the first span): = note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `::deallocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC - = note: inside `alloc::alloc::box_free::` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside ` as std::ops::Drop>::drop` at RUSTLIB/alloc/src/boxed.rs:LL:CC = note: inside `std::ptr::drop_in_place::> - shim(Some(std::boxed::Box))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC = note: inside `std::mem::drop::>` at RUSTLIB/core/src/mem/mod.rs:LL:CC note: inside closure From 17e4c73c66b760e465a5fd1e7752ba1d2070df8e Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 13 Aug 2022 18:37:59 -0400 Subject: [PATCH 19/32] mmap/munmap/mremamp shims --- src/concurrency/data_race.rs | 3 +- src/machine.rs | 14 +- src/shims/unix/foreign_items.rs | 17 ++ src/shims/unix/macos/foreign_items.rs | 10 - src/shims/unix/mem.rs | 236 ++++++++++++++++++++++++ src/shims/unix/mod.rs | 1 + tests/fail/mmap_invalid_dealloc.rs | 18 ++ tests/fail/mmap_invalid_dealloc.stderr | 15 ++ tests/fail/mmap_use_after_munmap.rs | 19 ++ tests/fail/mmap_use_after_munmap.stderr | 30 +++ tests/fail/munmap.stderr | 29 +++ tests/fail/munmap_partial.rs | 18 ++ tests/fail/munmap_partial.stderr | 29 +++ tests/pass-dep/shims/mmap.rs | 83 +++++++++ 14 files changed, 510 insertions(+), 12 deletions(-) create mode 100644 src/shims/unix/mem.rs create mode 100644 tests/fail/mmap_invalid_dealloc.rs create mode 100644 tests/fail/mmap_invalid_dealloc.stderr create mode 100644 tests/fail/mmap_use_after_munmap.rs create mode 100644 tests/fail/mmap_use_after_munmap.stderr create mode 100644 tests/fail/munmap.stderr create mode 100644 tests/fail/munmap_partial.rs create mode 100644 tests/fail/munmap_partial.stderr create mode 100644 tests/pass-dep/shims/mmap.rs diff --git a/src/concurrency/data_race.rs b/src/concurrency/data_race.rs index f6252c43f9..84ef27f736 100644 --- a/src/concurrency/data_race.rs +++ b/src/concurrency/data_race.rs @@ -714,7 +714,8 @@ impl VClockAlloc { MiriMemoryKind::Rust | MiriMemoryKind::Miri | MiriMemoryKind::C - | MiriMemoryKind::WinHeap, + | MiriMemoryKind::WinHeap + | MiriMemoryKind::Mmap, ) | MemoryKind::Stack => { let (alloc_index, clocks) = global.current_thread_state(thread_mgr); diff --git a/src/machine.rs b/src/machine.rs index 0ba7dad564..f88ad040e0 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -112,6 +112,8 @@ pub enum MiriMemoryKind { /// Memory for thread-local statics. /// This memory may leak. Tls, + /// Memory mapped directly by the program + Mmap, } impl From for MemoryKind { @@ -127,7 +129,7 @@ impl MayLeak for MiriMemoryKind { use self::MiriMemoryKind::*; match self { Rust | Miri | C | WinHeap | Runtime => false, - Machine | Global | ExternStatic | Tls => true, + Machine | Global | ExternStatic | Tls | Mmap => true, } } } @@ -145,6 +147,7 @@ impl fmt::Display for MiriMemoryKind { Global => write!(f, "global (static or const)"), ExternStatic => write!(f, "extern static"), Tls => write!(f, "thread-local static"), + Mmap => write!(f, "mmap"), } } } @@ -726,6 +729,15 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { // will panic when given the file. drop(self.profiler.take()); } + + pub(crate) fn round_up_to_multiple_of_page_size(&self, length: u64) -> Option { + #[allow(clippy::arithmetic_side_effects)] // page size is nonzero + (length.checked_add(self.page_size - 1)? / self.page_size).checked_mul(self.page_size) + } + + pub(crate) fn page_align(&self) -> Align { + Align::from_bytes(self.page_size).unwrap() + } } impl VisitTags for MiriMachine<'_, '_> { diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index c371e85c31..d0f6530637 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -10,6 +10,7 @@ use rustc_target::spec::abi::Abi; use crate::*; use shims::foreign_items::EmulateByNameResult; use shims::unix::fs::EvalContextExt as _; +use shims::unix::mem::EvalContextExt as _; use shims::unix::sync::EvalContextExt as _; use shims::unix::thread::EvalContextExt as _; @@ -213,6 +214,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } + "mmap" => { + let [addr, length, prot, flags, fd, offset] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?; + let ptr = this.mmap(addr, length, prot, flags, fd, offset)?; + this.write_scalar(ptr, dest)?; + } + "mremap" => { + let [old_address, old_size, new_size, flags] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?; + let ptr = this.mremap(old_address, old_size, new_size, flags)?; + this.write_scalar(ptr, dest)?; + } + "munmap" => { + let [addr, length] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?; + let result = this.munmap(addr, length)?; + this.write_scalar(result, dest)?; + } + // Dynamic symbol loading "dlsym" => { let [handle, symbol] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/src/shims/unix/macos/foreign_items.rs b/src/shims/unix/macos/foreign_items.rs index 1271788a97..85b950da4f 100644 --- a/src/shims/unix/macos/foreign_items.rs +++ b/src/shims/unix/macos/foreign_items.rs @@ -197,16 +197,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(res, dest)?; } - // Incomplete shims that we "stub out" just to get pre-main initialization code to work. - // These shims are enabled only when the caller is in the standard library. - "mmap" if this.frame_in_std() => { - // This is a horrible hack, but since the guard page mechanism calls mmap and expects a particular return value, we just give it that value. - let [addr, _, _, _, _, _] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let addr = this.read_scalar(addr)?; - this.write_scalar(addr, dest)?; - } - _ => return Ok(EmulateByNameResult::NotSupported), }; diff --git a/src/shims/unix/mem.rs b/src/shims/unix/mem.rs new file mode 100644 index 0000000000..1d01bc82a8 --- /dev/null +++ b/src/shims/unix/mem.rs @@ -0,0 +1,236 @@ +//! This is an incomplete implementation of mmap/mremap/munmap which is restricted in order to be +//! implementable on top of the existing memory system. The point of these function as-written is +//! to allow memory allocators written entirely in Rust to be executed by Miri. This implementation +//! does not support other uses of mmap such as file mappings. +//! +//! mmap/mremap/munmap behave a lot like alloc/realloc/dealloc, and for simple use they are exactly +//! equivalent. That is the only part we support: no MAP_FIXED or MAP_SHARED or anything +//! else that goes beyond a basic allocation API. + +use crate::*; +use rustc_target::abi::{Align, Size}; + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + fn mmap( + &mut self, + addr: &OpTy<'tcx, Provenance>, + length: &OpTy<'tcx, Provenance>, + prot: &OpTy<'tcx, Provenance>, + flags: &OpTy<'tcx, Provenance>, + fd: &OpTy<'tcx, Provenance>, + offset: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + // We do not support MAP_FIXED, so the addr argument is always ignored + let addr = this.read_pointer(addr)?; + let length = this.read_target_usize(length)?; + let prot = this.read_scalar(prot)?.to_i32()?; + let flags = this.read_scalar(flags)?.to_i32()?; + let fd = this.read_scalar(fd)?.to_i32()?; + let offset = this.read_scalar(offset)?.to_target_usize(this)?; + + let map_private = this.eval_libc_i32("MAP_PRIVATE"); + let map_anonymous = this.eval_libc_i32("MAP_ANONYMOUS"); + let map_shared = this.eval_libc_i32("MAP_SHARED"); + let map_fixed = this.eval_libc_i32("MAP_FIXED"); + + // This is a horrible hack, but on macos the guard page mechanism uses mmap + // in a way we do not support. We just give it the return value it expects. + if this.frame_in_std() && this.tcx.sess.target.os == "macos" && (flags & map_fixed) != 0 { + return Ok(Scalar::from_maybe_pointer(addr, this)); + } + + let prot_read = this.eval_libc_i32("PROT_READ"); + let prot_write = this.eval_libc_i32("PROT_WRITE"); + + // First, we do some basic argument validation as required by mmap + if (flags & (map_private | map_shared)).count_ones() != 1 { + this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + return Ok(Scalar::from_maybe_pointer(Pointer::null(), this)); + } + if length == 0 { + this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + return Ok(Scalar::from_maybe_pointer(Pointer::null(), this)); + } + + // If a user tries to map a file, we want to loudly inform them that this is not going + // to work. It is possible that POSIX gives us enough leeway to return an error, but the + // outcome for the user (I need to add cfg(miri)) is the same, just more frustrating. + if fd != -1 { + throw_unsup_format!("Miri does not support file-backed memory mappings"); + } + + // POSIX says: + // [ENOTSUP] + // * MAP_FIXED or MAP_PRIVATE was specified in the flags argument and the implementation + // does not support this functionality. + // * The implementation does not support the combination of accesses requested in the + // prot argument. + // + // Miri doesn't support MAP_FIXED or any any protections other than PROT_READ|PROT_WRITE. + if flags & map_fixed != 0 || prot != prot_read | prot_write { + this.set_last_error(Scalar::from_i32(this.eval_libc_i32("ENOTSUP")))?; + return Ok(Scalar::from_maybe_pointer(Pointer::null(), this)); + } + + // Miri does not support shared mappings, or any of the other extensions that for example + // Linux has added to the flags arguments. + if flags != map_private | map_anonymous { + throw_unsup_format!( + "Miri only supports calls to mmap which set the flags argument to MAP_PRIVATE|MAP_ANONYMOUS" + ); + } + + // This is only used for file mappings, which we don't support anyway. + if offset != 0 { + throw_unsup_format!("Miri does not support non-zero offsets to mmap"); + } + + let align = Align::from_bytes(this.machine.page_size).unwrap(); + let map_length = this.machine.round_up_to_multiple_of_page_size(length).unwrap_or(u64::MAX); + + let ptr = + this.allocate_ptr(Size::from_bytes(map_length), align, MiriMemoryKind::Mmap.into())?; + // We just allocated this, the access is definitely in-bounds and fits into our address space. + // mmap guarantees new mappings are zero-init. + this.write_bytes_ptr( + ptr.into(), + std::iter::repeat(0u8).take(usize::try_from(map_length).unwrap()), + ) + .unwrap(); + // Memory mappings don't use provenance, and are always exposed. + Machine::expose_ptr(this, ptr)?; + + Ok(Scalar::from_pointer(ptr, this)) + } + + fn mremap( + &mut self, + old_address: &OpTy<'tcx, Provenance>, + old_size: &OpTy<'tcx, Provenance>, + new_size: &OpTy<'tcx, Provenance>, + flags: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let old_address = this.read_pointer(old_address)?; + let old_size = this.read_scalar(old_size)?.to_target_usize(this)?; + let new_size = this.read_scalar(new_size)?.to_target_usize(this)?; + let flags = this.read_scalar(flags)?.to_i32()?; + + // old_address must be a multiple of the page size + #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero + if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 { + this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + return Ok(this.eval_libc("MAP_FAILED")); + } + + if flags & this.eval_libc_i32("MREMAP_FIXED") != 0 { + throw_unsup_format!("Miri does not support mremap wth MREMAP_FIXED"); + } + + if flags & this.eval_libc_i32("MREMAP_DONTUNMAP") != 0 { + throw_unsup_format!("Miri does not support mremap wth MREMAP_DONTUNMAP"); + } + + if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 { + // We only support MREMAP_MAYMOVE, so not passing the flag is just a failure + this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + return Ok(Scalar::from_maybe_pointer(Pointer::null(), this)); + } + + let align = this.machine.page_align(); + let ptr = this.reallocate_ptr( + old_address, + Some((Size::from_bytes(old_size), align)), + Size::from_bytes(new_size), + align, + MiriMemoryKind::Mmap.into(), + )?; + if let Some(increase) = new_size.checked_sub(old_size) { + // We just allocated this, the access is definitely in-bounds and fits into our address space. + // mmap guarantees new mappings are zero-init. + this.write_bytes_ptr( + ptr.offset(Size::from_bytes(old_size), this).unwrap().into(), + std::iter::repeat(0u8).take(usize::try_from(increase).unwrap()), + ) + .unwrap(); + } + // Memory mappings are always exposed + Machine::expose_ptr(this, ptr)?; + + Ok(Scalar::from_pointer(ptr, this)) + } + + fn munmap( + &mut self, + addr: &OpTy<'tcx, Provenance>, + length: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let addr = this.read_pointer(addr)?; + let length = this.read_scalar(length)?.to_target_usize(this)?; + + // addr must be a multiple of the page size + #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero + if addr.addr().bytes() % this.machine.page_size != 0 { + this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + return Ok(Scalar::from_i32(-1)); + } + + let length = this.machine.round_up_to_multiple_of_page_size(length).unwrap_or(u64::MAX); + + let mut addr = addr.addr().bytes(); + let mut bytes_unmapped = 0; + while bytes_unmapped < length { + // munmap specifies: + // It is not an error if the indicated range does not contain any mapped pages. + // So we make sure that if our address is not that of an exposed allocation, we just + // step forward to the next page. + let ptr = Machine::ptr_from_addr_cast(this, addr)?; + let Ok(ptr) = ptr.into_pointer_or_addr() else { + bytes_unmapped = bytes_unmapped.checked_add(this.machine.page_size).unwrap(); + addr = addr.wrapping_add(this.machine.page_size); + continue; + }; + // FIXME: This should fail if the pointer is to an unexposed allocation. But it + // doesn't. + let Some((alloc_id, offset, _prov)) = Machine::ptr_get_alloc(this, ptr) else { + bytes_unmapped = bytes_unmapped.checked_add(this.machine.page_size).unwrap(); + addr = addr.wrapping_add(this.machine.page_size); + continue; + }; + + if offset != Size::ZERO { + throw_unsup_format!("Miri does not support partial munmap"); + } + let (_kind, alloc) = this.memory.alloc_map().get(alloc_id).unwrap(); + let this_alloc_len = alloc.len() as u64; + bytes_unmapped = bytes_unmapped.checked_add(this_alloc_len).unwrap(); + if bytes_unmapped > length { + throw_unsup_format!("Miri does not support partial munmap"); + } + + this.deallocate_ptr( + Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr)), + Some((Size::from_bytes(this_alloc_len), this.machine.page_align())), + MemoryKind::Machine(MiriMemoryKind::Mmap), + )?; + addr = addr.wrapping_add(this_alloc_len); + } + + Ok(Scalar::from_i32(0)) + } +} + +trait RangeExt { + fn overlaps(&self, other: &Self) -> bool; +} +impl RangeExt for std::ops::Range { + fn overlaps(&self, other: &Self) -> bool { + self.start.max(other.start) <= self.end.min(other.end) + } +} diff --git a/src/shims/unix/mod.rs b/src/shims/unix/mod.rs index 6fefb054f3..a8ebd369ab 100644 --- a/src/shims/unix/mod.rs +++ b/src/shims/unix/mod.rs @@ -2,6 +2,7 @@ pub mod dlsym; pub mod foreign_items; mod fs; +mod mem; mod sync; mod thread; diff --git a/tests/fail/mmap_invalid_dealloc.rs b/tests/fail/mmap_invalid_dealloc.rs new file mode 100644 index 0000000000..70f7a6a7ce --- /dev/null +++ b/tests/fail/mmap_invalid_dealloc.rs @@ -0,0 +1,18 @@ +//@compile-flags: -Zmiri-disable-isolation +//@ignore-target-windows: No libc on Windows + +#![feature(rustc_private)] + +fn main() { + unsafe { + let ptr = libc::mmap( + std::ptr::null_mut(), + 4096, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ); + libc::free(ptr); //~ ERROR: which is mmap memory, using C heap deallocation operation + } +} diff --git a/tests/fail/mmap_invalid_dealloc.stderr b/tests/fail/mmap_invalid_dealloc.stderr new file mode 100644 index 0000000000..54e0cd5275 --- /dev/null +++ b/tests/fail/mmap_invalid_dealloc.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: deallocating ALLOC, which is mmap memory, using C heap deallocation operation + --> $DIR/mmap_invalid_dealloc.rs:LL:CC + | +LL | libc::free(ptr); + | ^^^^^^^^^^^^^^^ deallocating ALLOC, which is mmap memory, using C heap deallocation operation + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/mmap_invalid_dealloc.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/mmap_use_after_munmap.rs b/tests/fail/mmap_use_after_munmap.rs new file mode 100644 index 0000000000..1e00bc6b64 --- /dev/null +++ b/tests/fail/mmap_use_after_munmap.rs @@ -0,0 +1,19 @@ +//@compile-flags: -Zmiri-disable-isolation +//@ignore-target-windows: No libc on Windows + +#![feature(rustc_private)] + +fn main() { + unsafe { + let ptr = libc::mmap( + std::ptr::null_mut(), + 4096, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ); + libc::munmap(ptr, 4096); + let _x = *(ptr as *mut u8); //~ ERROR: was dereferenced after this allocation got freed + } +} diff --git a/tests/fail/mmap_use_after_munmap.stderr b/tests/fail/mmap_use_after_munmap.stderr new file mode 100644 index 0000000000..f90701d400 --- /dev/null +++ b/tests/fail/mmap_use_after_munmap.stderr @@ -0,0 +1,30 @@ +warning: integer-to-pointer cast + --> $DIR/mmap_use_after_munmap.rs:LL:CC + | +LL | libc::munmap(ptr, 4096); + | ^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast + | + = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`, + = help: which means that Miri might miss pointer bugs in this program. + = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation. + = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. + = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics. + = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. + = note: BACKTRACE: + = note: inside `main` at $DIR/mmap_use_after_munmap.rs:LL:CC + +error: Undefined Behavior: pointer to ALLOC was dereferenced after this allocation got freed + --> $DIR/mmap_use_after_munmap.rs:LL:CC + | +LL | let _x = *(ptr as *mut u8); + | ^^^^^^^^^^^^^^^^^ pointer to ALLOC was dereferenced after this allocation got freed + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/mmap_use_after_munmap.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error; 1 warning emitted + diff --git a/tests/fail/munmap.stderr b/tests/fail/munmap.stderr new file mode 100644 index 0000000000..a8bcbd098f --- /dev/null +++ b/tests/fail/munmap.stderr @@ -0,0 +1,29 @@ +warning: integer-to-pointer cast + --> $DIR/munmap.rs:LL:CC + | +LL | libc::munmap(ptr, 1); + | ^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast + | + = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`, + = help: which means that Miri might miss pointer bugs in this program. + = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation. + = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. + = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics. + = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. + = note: BACKTRACE: + = note: inside `main` at $DIR/munmap.rs:LL:CC + +error: unsupported operation: Miri does not support partial munmap + --> $DIR/munmap.rs:LL:CC + | +LL | libc::munmap(ptr, 1); + | ^^^^^^^^^^^^^^^^^^^^ Miri does not support partial munmap + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = note: BACKTRACE: + = note: inside `main` at $DIR/munmap.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error; 1 warning emitted + diff --git a/tests/fail/munmap_partial.rs b/tests/fail/munmap_partial.rs new file mode 100644 index 0000000000..41387ae0b9 --- /dev/null +++ b/tests/fail/munmap_partial.rs @@ -0,0 +1,18 @@ +//! Our mmap/munmap support is a thin wrapper over Interpcx::allocate_ptr. Since the underlying +//! layer has much more UB than munmap does, we need to be sure we throw an unsupported error here. +//@ignore-target-windows: No libc on Windows + +fn main() { + unsafe { + let ptr = libc::mmap( + std::ptr::null_mut(), + page_size::get() * 2, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ); + libc::munmap(ptr, 1); + //~^ ERROR: unsupported operation: Miri does not support partial munmap + } +} diff --git a/tests/fail/munmap_partial.stderr b/tests/fail/munmap_partial.stderr new file mode 100644 index 0000000000..a8bd999e66 --- /dev/null +++ b/tests/fail/munmap_partial.stderr @@ -0,0 +1,29 @@ +warning: integer-to-pointer cast + --> $DIR/munmap_partial.rs:LL:CC + | +LL | libc::munmap(ptr, 1); + | ^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast + | + = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`, + = help: which means that Miri might miss pointer bugs in this program. + = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation. + = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. + = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics. + = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. + = note: BACKTRACE: + = note: inside `main` at $DIR/munmap_partial.rs:LL:CC + +error: unsupported operation: Miri does not support partial munmap + --> $DIR/munmap_partial.rs:LL:CC + | +LL | libc::munmap(ptr, 1); + | ^^^^^^^^^^^^^^^^^^^^ Miri does not support partial munmap + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = note: BACKTRACE: + = note: inside `main` at $DIR/munmap_partial.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error; 1 warning emitted + diff --git a/tests/pass-dep/shims/mmap.rs b/tests/pass-dep/shims/mmap.rs new file mode 100644 index 0000000000..4e5060e85a --- /dev/null +++ b/tests/pass-dep/shims/mmap.rs @@ -0,0 +1,83 @@ +//@ignore-target-windows: No libc on Windows +//@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance +#![feature(strict_provenance)] + +use std::{ptr, slice}; + +fn test_mmap() { + let page_size = page_size::get(); + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ) + }; + assert!(!ptr.is_null()); + + // Ensure that freshly mapped allocations are zeroed + let slice = unsafe { slice::from_raw_parts_mut(ptr as *mut u8, page_size) }; + assert!(slice.iter().all(|b| *b == 0)); + + // Do some writes, make sure they worked + for b in slice.iter_mut() { + *b = 1; + } + assert!(slice.iter().all(|b| *b == 1)); + + // Ensure that we can munmap with just an integer + let just_an_address = ptr::invalid_mut(ptr.addr()); + let res = unsafe { libc::munmap(just_an_address, page_size) }; + assert_eq!(res, 0i32); +} + +#[cfg(target_os = "linux")] +fn test_mremap() { + let page_size = page_size::get(); + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ) + }; + let slice = unsafe { slice::from_raw_parts_mut(ptr as *mut u8, page_size) }; + for b in slice.iter_mut() { + *b = 1; + } + + let ptr = unsafe { libc::mremap(ptr, page_size, page_size * 2, libc::MREMAP_MAYMOVE) }; + assert!(!ptr.is_null()); + + let slice = unsafe { slice::from_raw_parts_mut(ptr as *mut u8, page_size * 2) }; + assert!(&slice[..page_size].iter().all(|b| *b == 1)); + assert!(&slice[page_size..].iter().all(|b| *b == 0)); + + let res = unsafe { libc::munmap(ptr, page_size * 2) }; + assert_eq!(res, 0i32); +} + +fn test_munmap() { + // Linux specifies that it is not an error if the specified range does not contain any pages. + let res = unsafe { + libc::munmap( + // Some high address we surely have no allocated anything at + ptr::invalid_mut(1 << 30), + 4096, + ) + }; + assert_eq!(res, 0); +} + +fn main() { + test_mmap(); + test_munmap(); + #[cfg(target_os = "linux")] + test_mremap(); +} From 00fdee2950b65822331e1d16eababd9dd3b644ef Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Fri, 16 Jun 2023 19:10:54 -0400 Subject: [PATCH 20/32] Make munmap throw unsup errors instead of trying to work --- src/shims/unix/mem.rs | 71 +++++++++++++------------------- tests/fail/munmap.rs | 22 ++++++++++ tests/fail/munmap.stderr | 20 ++++++--- tests/fail/munmap_partial.rs | 2 +- tests/fail/munmap_partial.stderr | 4 +- tests/pass-dep/shims/mmap.rs | 13 ------ 6 files changed, 68 insertions(+), 64 deletions(-) create mode 100644 tests/fail/munmap.rs diff --git a/src/shims/unix/mem.rs b/src/shims/unix/mem.rs index 1d01bc82a8..f133dca609 100644 --- a/src/shims/unix/mem.rs +++ b/src/shims/unix/mem.rs @@ -8,7 +8,7 @@ //! else that goes beyond a basic allocation API. use crate::*; -use rustc_target::abi::{Align, Size}; +use rustc_target::abi::Size; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { @@ -88,7 +88,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_unsup_format!("Miri does not support non-zero offsets to mmap"); } - let align = Align::from_bytes(this.machine.page_size).unwrap(); + let align = this.machine.page_align(); let map_length = this.machine.round_up_to_multiple_of_page_size(length).unwrap_or(u64::MAX); let ptr = @@ -115,14 +115,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let old_address = this.read_pointer(old_address)?; + let old_address = this.read_scalar(old_address)?.to_target_usize(this)?; let old_size = this.read_scalar(old_size)?.to_target_usize(this)?; let new_size = this.read_scalar(new_size)?.to_target_usize(this)?; let flags = this.read_scalar(flags)?.to_i32()?; // old_address must be a multiple of the page size #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero - if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 { + if old_address % this.machine.page_size != 0 || new_size == 0 { this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; return Ok(this.eval_libc("MAP_FAILED")); } @@ -141,6 +141,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { return Ok(Scalar::from_maybe_pointer(Pointer::null(), this)); } + let old_address = Machine::ptr_from_addr_cast(this, old_address)?; let align = this.machine.page_align(); let ptr = this.reallocate_ptr( old_address, @@ -171,57 +172,41 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let addr = this.read_pointer(addr)?; + let addr = this.read_scalar(addr)?.to_target_usize(this)?; let length = this.read_scalar(length)?.to_target_usize(this)?; // addr must be a multiple of the page size #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero - if addr.addr().bytes() % this.machine.page_size != 0 { + if addr % this.machine.page_size != 0 { this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; return Ok(Scalar::from_i32(-1)); } let length = this.machine.round_up_to_multiple_of_page_size(length).unwrap_or(u64::MAX); - let mut addr = addr.addr().bytes(); - let mut bytes_unmapped = 0; - while bytes_unmapped < length { - // munmap specifies: - // It is not an error if the indicated range does not contain any mapped pages. - // So we make sure that if our address is not that of an exposed allocation, we just - // step forward to the next page. - let ptr = Machine::ptr_from_addr_cast(this, addr)?; - let Ok(ptr) = ptr.into_pointer_or_addr() else { - bytes_unmapped = bytes_unmapped.checked_add(this.machine.page_size).unwrap(); - addr = addr.wrapping_add(this.machine.page_size); - continue; - }; - // FIXME: This should fail if the pointer is to an unexposed allocation. But it - // doesn't. - let Some((alloc_id, offset, _prov)) = Machine::ptr_get_alloc(this, ptr) else { - bytes_unmapped = bytes_unmapped.checked_add(this.machine.page_size).unwrap(); - addr = addr.wrapping_add(this.machine.page_size); - continue; - }; - - if offset != Size::ZERO { - throw_unsup_format!("Miri does not support partial munmap"); - } - let (_kind, alloc) = this.memory.alloc_map().get(alloc_id).unwrap(); - let this_alloc_len = alloc.len() as u64; - bytes_unmapped = bytes_unmapped.checked_add(this_alloc_len).unwrap(); - if bytes_unmapped > length { - throw_unsup_format!("Miri does not support partial munmap"); - } - - this.deallocate_ptr( - Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr)), - Some((Size::from_bytes(this_alloc_len), this.machine.page_align())), - MemoryKind::Machine(MiriMemoryKind::Mmap), - )?; - addr = addr.wrapping_add(this_alloc_len); + let ptr = Machine::ptr_from_addr_cast(this, addr)?; + + let Ok(ptr) = ptr.into_pointer_or_addr() else { + throw_unsup_format!("Miri only supports munmap on memory allocated directly by mmap"); + }; + let Some((alloc_id, offset, _prov)) = Machine::ptr_get_alloc(this, ptr) else { + throw_unsup_format!("Miri only supports munmap on memory allocated directly by mmap"); + }; + + let (_kind, alloc) = this.memory.alloc_map().get(alloc_id).unwrap(); + if offset != Size::ZERO || alloc.len() as u64 != length { + throw_unsup_format!( + "Miri only supports munmap calls that exactly unmap a region previously returned by mmap" + ); } + let len = Size::from_bytes(alloc.len() as u64); + this.deallocate_ptr( + Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr)), + Some((len, this.machine.page_align())), + MemoryKind::Machine(MiriMemoryKind::Mmap), + )?; + Ok(Scalar::from_i32(0)) } } diff --git a/tests/fail/munmap.rs b/tests/fail/munmap.rs new file mode 100644 index 0000000000..453437a06c --- /dev/null +++ b/tests/fail/munmap.rs @@ -0,0 +1,22 @@ +//@compile-flags: -Zmiri-disable-isolation +//@ignore-target-windows: No libc on Windows + +#![feature(rustc_private)] +#![feature(strict_provenance)] + +use std::ptr; + +fn main() { + // Linux specifies that it is not an error if the specified range does not contain any pages. + // But we simply do not support such calls. This test checks that we report this as + // unsupported, not Undefined Behavior. + let res = unsafe { + libc::munmap( + //~^ ERROR: unsupported operation + // Some high address we surely have not allocated anything at + ptr::invalid_mut(1 << 30), + 4096, + ) + }; + assert_eq!(res, 0); +} diff --git a/tests/fail/munmap.stderr b/tests/fail/munmap.stderr index a8bcbd098f..cb47769c06 100644 --- a/tests/fail/munmap.stderr +++ b/tests/fail/munmap.stderr @@ -1,8 +1,13 @@ warning: integer-to-pointer cast --> $DIR/munmap.rs:LL:CC | -LL | libc::munmap(ptr, 1); - | ^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast +LL | / libc::munmap( +LL | | +LL | | // Some high address we surely have not allocated anything at +LL | | ptr::invalid_mut(1 << 30), +LL | | 4096, +LL | | ) + | |_________^ integer-to-pointer cast | = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`, = help: which means that Miri might miss pointer bugs in this program. @@ -13,11 +18,16 @@ LL | libc::munmap(ptr, 1); = note: BACKTRACE: = note: inside `main` at $DIR/munmap.rs:LL:CC -error: unsupported operation: Miri does not support partial munmap +error: unsupported operation: Miri only supports munmap on memory allocated directly by mmap --> $DIR/munmap.rs:LL:CC | -LL | libc::munmap(ptr, 1); - | ^^^^^^^^^^^^^^^^^^^^ Miri does not support partial munmap +LL | / libc::munmap( +LL | | +LL | | // Some high address we surely have not allocated anything at +LL | | ptr::invalid_mut(1 << 30), +LL | | 4096, +LL | | ) + | |_________^ Miri only supports munmap on memory allocated directly by mmap | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: BACKTRACE: diff --git a/tests/fail/munmap_partial.rs b/tests/fail/munmap_partial.rs index 41387ae0b9..938850ee28 100644 --- a/tests/fail/munmap_partial.rs +++ b/tests/fail/munmap_partial.rs @@ -13,6 +13,6 @@ fn main() { 0, ); libc::munmap(ptr, 1); - //~^ ERROR: unsupported operation: Miri does not support partial munmap + //~^ ERROR: unsupported operation } } diff --git a/tests/fail/munmap_partial.stderr b/tests/fail/munmap_partial.stderr index a8bd999e66..9a084c5043 100644 --- a/tests/fail/munmap_partial.stderr +++ b/tests/fail/munmap_partial.stderr @@ -13,11 +13,11 @@ LL | libc::munmap(ptr, 1); = note: BACKTRACE: = note: inside `main` at $DIR/munmap_partial.rs:LL:CC -error: unsupported operation: Miri does not support partial munmap +error: unsupported operation: Miri only supports munmap calls that exactly unmap a region previously returned by mmap --> $DIR/munmap_partial.rs:LL:CC | LL | libc::munmap(ptr, 1); - | ^^^^^^^^^^^^^^^^^^^^ Miri does not support partial munmap + | ^^^^^^^^^^^^^^^^^^^^ Miri only supports munmap calls that exactly unmap a region previously returned by mmap | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: BACKTRACE: diff --git a/tests/pass-dep/shims/mmap.rs b/tests/pass-dep/shims/mmap.rs index 4e5060e85a..03ffc3b389 100644 --- a/tests/pass-dep/shims/mmap.rs +++ b/tests/pass-dep/shims/mmap.rs @@ -63,21 +63,8 @@ fn test_mremap() { assert_eq!(res, 0i32); } -fn test_munmap() { - // Linux specifies that it is not an error if the specified range does not contain any pages. - let res = unsafe { - libc::munmap( - // Some high address we surely have no allocated anything at - ptr::invalid_mut(1 << 30), - 4096, - ) - }; - assert_eq!(res, 0); -} - fn main() { test_mmap(); - test_munmap(); #[cfg(target_os = "linux")] test_mremap(); } From 4916a77cb6c3f1c49a7e6bdbcf784543c9cac607 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 17 Jun 2023 11:55:30 -0400 Subject: [PATCH 21/32] Improve organization --- src/shims/unix/foreign_items.rs | 5 - src/shims/unix/linux/foreign_items.rs | 7 ++ src/shims/unix/linux/mem.rs | 67 +++++++++++++ src/shims/unix/linux/mod.rs | 1 + src/shims/unix/mem.rs | 93 +++---------------- .../fail/{ => shims}/mmap_invalid_dealloc.rs | 0 .../{ => shims}/mmap_invalid_dealloc.stderr | 0 .../fail/{ => shims}/mmap_use_after_munmap.rs | 0 .../{ => shims}/mmap_use_after_munmap.stderr | 0 tests/fail/{ => shims}/munmap.rs | 0 tests/fail/{ => shims}/munmap.stderr | 0 tests/fail/{ => shims}/munmap_partial.rs | 0 tests/fail/{ => shims}/munmap_partial.stderr | 0 13 files changed, 90 insertions(+), 83 deletions(-) create mode 100644 src/shims/unix/linux/mem.rs rename tests/fail/{ => shims}/mmap_invalid_dealloc.rs (100%) rename tests/fail/{ => shims}/mmap_invalid_dealloc.stderr (100%) rename tests/fail/{ => shims}/mmap_use_after_munmap.rs (100%) rename tests/fail/{ => shims}/mmap_use_after_munmap.stderr (100%) rename tests/fail/{ => shims}/munmap.rs (100%) rename tests/fail/{ => shims}/munmap.stderr (100%) rename tests/fail/{ => shims}/munmap_partial.rs (100%) rename tests/fail/{ => shims}/munmap_partial.stderr (100%) diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index d0f6530637..bd66a67950 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -219,11 +219,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let ptr = this.mmap(addr, length, prot, flags, fd, offset)?; this.write_scalar(ptr, dest)?; } - "mremap" => { - let [old_address, old_size, new_size, flags] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?; - let ptr = this.mremap(old_address, old_size, new_size, flags)?; - this.write_scalar(ptr, dest)?; - } "munmap" => { let [addr, length] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?; let result = this.munmap(addr, length)?; diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index 4cb7ee8efc..a32ef54bd9 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -7,6 +7,7 @@ use crate::*; use shims::foreign_items::EmulateByNameResult; use shims::unix::fs::EvalContextExt as _; use shims::unix::linux::fd::EvalContextExt as _; +use shims::unix::linux::mem::EvalContextExt as _; use shims::unix::linux::sync::futex; use shims::unix::sync::EvalContextExt as _; use shims::unix::thread::EvalContextExt as _; @@ -68,6 +69,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let result = this.eventfd(val, flag)?; this.write_scalar(result, dest)?; } + "mremap" => { + let [old_address, old_size, new_size, flags] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let ptr = this.mremap(old_address, old_size, new_size, flags)?; + this.write_scalar(ptr, dest)?; + } "socketpair" => { let [domain, type_, protocol, sv] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/src/shims/unix/linux/mem.rs b/src/shims/unix/linux/mem.rs new file mode 100644 index 0000000000..026fd6ae5e --- /dev/null +++ b/src/shims/unix/linux/mem.rs @@ -0,0 +1,67 @@ +//! This follows the pattern in src/shims/unix/mem.rs: We only support uses of mremap that would +//! correspond to valid uses of realloc. + +use crate::*; +use rustc_target::abi::Size; + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + fn mremap( + &mut self, + old_address: &OpTy<'tcx, Provenance>, + old_size: &OpTy<'tcx, Provenance>, + new_size: &OpTy<'tcx, Provenance>, + flags: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let old_address = this.read_target_usize(old_address)?; + let old_size = this.read_target_usize(old_size)?; + let new_size = this.read_target_usize(new_size)?; + let flags = this.read_scalar(flags)?.to_i32()?; + + // old_address must be a multiple of the page size + #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero + if old_address % this.machine.page_size != 0 || new_size == 0 { + this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + return Ok(this.eval_libc("MAP_FAILED")); + } + + if flags & this.eval_libc_i32("MREMAP_FIXED") != 0 { + throw_unsup_format!("Miri does not support mremap wth MREMAP_FIXED"); + } + + if flags & this.eval_libc_i32("MREMAP_DONTUNMAP") != 0 { + throw_unsup_format!("Miri does not support mremap wth MREMAP_DONTUNMAP"); + } + + if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 { + // We only support MREMAP_MAYMOVE, so not passing the flag is just a failure + this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + return Ok(Scalar::from_maybe_pointer(Pointer::null(), this)); + } + + let old_address = Machine::ptr_from_addr_cast(this, old_address)?; + let align = this.machine.page_align(); + let ptr = this.reallocate_ptr( + old_address, + Some((Size::from_bytes(old_size), align)), + Size::from_bytes(new_size), + align, + MiriMemoryKind::Mmap.into(), + )?; + if let Some(increase) = new_size.checked_sub(old_size) { + // We just allocated this, the access is definitely in-bounds and fits into our address space. + // mmap guarantees new mappings are zero-init. + this.write_bytes_ptr( + ptr.offset(Size::from_bytes(old_size), this).unwrap().into(), + std::iter::repeat(0u8).take(usize::try_from(increase).unwrap()), + ) + .unwrap(); + } + // Memory mappings are always exposed + Machine::expose_ptr(this, ptr)?; + + Ok(Scalar::from_pointer(ptr, this)) + } +} diff --git a/src/shims/unix/linux/mod.rs b/src/shims/unix/linux/mod.rs index 437764c382..856ec226de 100644 --- a/src/shims/unix/linux/mod.rs +++ b/src/shims/unix/linux/mod.rs @@ -1,4 +1,5 @@ pub mod dlsym; pub mod fd; pub mod foreign_items; +pub mod mem; pub mod sync; diff --git a/src/shims/unix/mem.rs b/src/shims/unix/mem.rs index f133dca609..a33d784d16 100644 --- a/src/shims/unix/mem.rs +++ b/src/shims/unix/mem.rs @@ -1,9 +1,9 @@ -//! This is an incomplete implementation of mmap/mremap/munmap which is restricted in order to be +//! This is an incomplete implementation of mmap/munmap which is restricted in order to be //! implementable on top of the existing memory system. The point of these function as-written is //! to allow memory allocators written entirely in Rust to be executed by Miri. This implementation //! does not support other uses of mmap such as file mappings. //! -//! mmap/mremap/munmap behave a lot like alloc/realloc/dealloc, and for simple use they are exactly +//! mmap/munmap behave a lot like alloc/dealloc, and for simple use they are exactly //! equivalent. That is the only part we support: no MAP_FIXED or MAP_SHARED or anything //! else that goes beyond a basic allocation API. @@ -23,23 +23,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - // We do not support MAP_FIXED, so the addr argument is always ignored - let addr = this.read_pointer(addr)?; + // We do not support MAP_FIXED, so the addr argument is always ignored (except for the MacOS hack) + let addr = this.read_target_usize(addr)?; let length = this.read_target_usize(length)?; let prot = this.read_scalar(prot)?.to_i32()?; let flags = this.read_scalar(flags)?.to_i32()?; let fd = this.read_scalar(fd)?.to_i32()?; - let offset = this.read_scalar(offset)?.to_target_usize(this)?; + let offset = this.read_target_usize(offset)?; let map_private = this.eval_libc_i32("MAP_PRIVATE"); let map_anonymous = this.eval_libc_i32("MAP_ANONYMOUS"); let map_shared = this.eval_libc_i32("MAP_SHARED"); let map_fixed = this.eval_libc_i32("MAP_FIXED"); - // This is a horrible hack, but on macos the guard page mechanism uses mmap + // This is a horrible hack, but on MacOS the guard page mechanism uses mmap // in a way we do not support. We just give it the return value it expects. if this.frame_in_std() && this.tcx.sess.target.os == "macos" && (flags & map_fixed) != 0 { - return Ok(Scalar::from_maybe_pointer(addr, this)); + return Ok(Scalar::from_maybe_pointer(Pointer::from_addr_invalid(addr), this)); } let prot_read = this.eval_libc_i32("PROT_READ"); @@ -106,65 +106,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(Scalar::from_pointer(ptr, this)) } - fn mremap( - &mut self, - old_address: &OpTy<'tcx, Provenance>, - old_size: &OpTy<'tcx, Provenance>, - new_size: &OpTy<'tcx, Provenance>, - flags: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_mut(); - - let old_address = this.read_scalar(old_address)?.to_target_usize(this)?; - let old_size = this.read_scalar(old_size)?.to_target_usize(this)?; - let new_size = this.read_scalar(new_size)?.to_target_usize(this)?; - let flags = this.read_scalar(flags)?.to_i32()?; - - // old_address must be a multiple of the page size - #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero - if old_address % this.machine.page_size != 0 || new_size == 0 { - this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; - return Ok(this.eval_libc("MAP_FAILED")); - } - - if flags & this.eval_libc_i32("MREMAP_FIXED") != 0 { - throw_unsup_format!("Miri does not support mremap wth MREMAP_FIXED"); - } - - if flags & this.eval_libc_i32("MREMAP_DONTUNMAP") != 0 { - throw_unsup_format!("Miri does not support mremap wth MREMAP_DONTUNMAP"); - } - - if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 { - // We only support MREMAP_MAYMOVE, so not passing the flag is just a failure - this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; - return Ok(Scalar::from_maybe_pointer(Pointer::null(), this)); - } - - let old_address = Machine::ptr_from_addr_cast(this, old_address)?; - let align = this.machine.page_align(); - let ptr = this.reallocate_ptr( - old_address, - Some((Size::from_bytes(old_size), align)), - Size::from_bytes(new_size), - align, - MiriMemoryKind::Mmap.into(), - )?; - if let Some(increase) = new_size.checked_sub(old_size) { - // We just allocated this, the access is definitely in-bounds and fits into our address space. - // mmap guarantees new mappings are zero-init. - this.write_bytes_ptr( - ptr.offset(Size::from_bytes(old_size), this).unwrap().into(), - std::iter::repeat(0u8).take(usize::try_from(increase).unwrap()), - ) - .unwrap(); - } - // Memory mappings are always exposed - Machine::expose_ptr(this, ptr)?; - - Ok(Scalar::from_pointer(ptr, this)) - } - fn munmap( &mut self, addr: &OpTy<'tcx, Provenance>, @@ -172,8 +113,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let addr = this.read_scalar(addr)?.to_target_usize(this)?; - let length = this.read_scalar(length)?.to_target_usize(this)?; + let addr = this.read_target_usize(addr)?; + let length = this.read_target_usize(length)?; // addr must be a multiple of the page size #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero @@ -193,6 +134,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_unsup_format!("Miri only supports munmap on memory allocated directly by mmap"); }; + // Elsewhere in this function we are careful to check what we can and throw an unsupported + // error instead of Undefined Behavior when use of this function falls outside of the + // narrow scope we support. We deliberately do not check the MemoryKind of this allocation, + // because we want to report UB on attempting to unmap memory that Rust "understands", such + // the stack, heap, or statics. let (_kind, alloc) = this.memory.alloc_map().get(alloc_id).unwrap(); if offset != Size::ZERO || alloc.len() as u64 != length { throw_unsup_format!( @@ -202,7 +148,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let len = Size::from_bytes(alloc.len() as u64); this.deallocate_ptr( - Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr)), + ptr.into(), Some((len, this.machine.page_align())), MemoryKind::Machine(MiriMemoryKind::Mmap), )?; @@ -210,12 +156,3 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(Scalar::from_i32(0)) } } - -trait RangeExt { - fn overlaps(&self, other: &Self) -> bool; -} -impl RangeExt for std::ops::Range { - fn overlaps(&self, other: &Self) -> bool { - self.start.max(other.start) <= self.end.min(other.end) - } -} diff --git a/tests/fail/mmap_invalid_dealloc.rs b/tests/fail/shims/mmap_invalid_dealloc.rs similarity index 100% rename from tests/fail/mmap_invalid_dealloc.rs rename to tests/fail/shims/mmap_invalid_dealloc.rs diff --git a/tests/fail/mmap_invalid_dealloc.stderr b/tests/fail/shims/mmap_invalid_dealloc.stderr similarity index 100% rename from tests/fail/mmap_invalid_dealloc.stderr rename to tests/fail/shims/mmap_invalid_dealloc.stderr diff --git a/tests/fail/mmap_use_after_munmap.rs b/tests/fail/shims/mmap_use_after_munmap.rs similarity index 100% rename from tests/fail/mmap_use_after_munmap.rs rename to tests/fail/shims/mmap_use_after_munmap.rs diff --git a/tests/fail/mmap_use_after_munmap.stderr b/tests/fail/shims/mmap_use_after_munmap.stderr similarity index 100% rename from tests/fail/mmap_use_after_munmap.stderr rename to tests/fail/shims/mmap_use_after_munmap.stderr diff --git a/tests/fail/munmap.rs b/tests/fail/shims/munmap.rs similarity index 100% rename from tests/fail/munmap.rs rename to tests/fail/shims/munmap.rs diff --git a/tests/fail/munmap.stderr b/tests/fail/shims/munmap.stderr similarity index 100% rename from tests/fail/munmap.stderr rename to tests/fail/shims/munmap.stderr diff --git a/tests/fail/munmap_partial.rs b/tests/fail/shims/munmap_partial.rs similarity index 100% rename from tests/fail/munmap_partial.rs rename to tests/fail/shims/munmap_partial.rs diff --git a/tests/fail/munmap_partial.stderr b/tests/fail/shims/munmap_partial.stderr similarity index 100% rename from tests/fail/munmap_partial.stderr rename to tests/fail/shims/munmap_partial.stderr From c70307f0ef84ad3eee99e16f9aaacf07ce89059e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 22 Jun 2023 10:38:47 +0200 Subject: [PATCH 22/32] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 73385aee85..0ac1e277eb 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -677710eaf0a0bdb008959ee8717c9fe1c6d187b3 +0928a1f7574f5ca019b5443b3a90008588d18c8c From df1a7c0157bdbae14fbb7cefb5098d5a128ed7c8 Mon Sep 17 00:00:00 2001 From: Neven Villani Date: Mon, 19 Jun 2023 12:05:30 +0200 Subject: [PATCH 23/32] drop perform_read_access (always read) in favor of zero_size --- src/borrow_tracker/tree_borrows/mod.rs | 64 +++++++++++++++----------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/src/borrow_tracker/tree_borrows/mod.rs b/src/borrow_tracker/tree_borrows/mod.rs index 1c7a735779..31cc03828f 100644 --- a/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/borrow_tracker/tree_borrows/mod.rs @@ -99,9 +99,9 @@ impl<'tcx> Tree { /// Policy for a new borrow. #[derive(Debug, Clone, Copy)] struct NewPermission { - /// Whether this borrow requires a read access on its parent. - /// `perform_read_access` is `true` for all pointers marked `dereferenceable`. - perform_read_access: bool, + /// Optionally ignore the actual size to do a zero-size reborrow. + /// If this is set then `dereferenceable` is not enforced. + zero_size: bool, /// Which permission should the pointer start with. initial_state: Permission, /// Whether this pointer is part of the arguments of a function call. @@ -128,20 +128,19 @@ impl<'tcx> NewPermission { // `&`s, which are excluded above. _ => return None, }; - // This field happens to be redundant since right now we always do a read, - // but it could be useful in the future. - let perform_read_access = true; let protector = (kind == RetagKind::FnEntry).then_some(ProtectorKind::StrongProtector); - Some(Self { perform_read_access, initial_state, protector }) + Some(Self { zero_size: false, initial_state, protector }) } - // Boxes are not handled by `from_ref_ty`, they need special behavior - // implemented here. - fn from_box_ty( + /// Compute permission for `Box`-like type (`Box` always, and also `Unique` if enabled). + /// These pointers allow deallocation so need a different kind of protector not handled + /// by `from_ref_ty`. + fn from_unique_ty( ty: Ty<'tcx>, kind: RetagKind, cx: &crate::MiriInterpCx<'_, 'tcx>, + zero_size: bool, ) -> Option { let pointee = ty.builtin_deref(true).unwrap().ty; pointee.is_unpin(*cx.tcx, cx.param_env()).then_some(()).map(|()| { @@ -149,7 +148,7 @@ impl<'tcx> NewPermission { // because it is valid to deallocate it within the function. let ty_is_freeze = ty.is_freeze(*cx.tcx, cx.param_env()); Self { - perform_read_access: true, + zero_size, initial_state: Permission::new_unique_2phase(ty_is_freeze), protector: (kind == RetagKind::FnEntry).then_some(ProtectorKind::WeakProtector), } @@ -201,6 +200,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' Ok(()) }; + trace!("Reborrow of size {:?}", ptr_size); let (alloc_id, base_offset, parent_prov) = if ptr_size > Size::ZERO { this.ptr_get_alloc_id(place.ptr)? } else { @@ -276,8 +276,8 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' let range = alloc_range(base_offset, ptr_size); let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut(); - if new_perm.perform_read_access { - // Count this reborrow as a read access + // All reborrows incur a (possibly zero-sized) read access to the parent + { let global = &this.machine.borrow_tracker.as_ref().unwrap(); let span = this.machine.current_span(); tree_borrows.perform_access( @@ -308,12 +308,19 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' // We want a place for where the ptr *points to*, so we get one. let place = this.ref_to_mplace(val)?; - // Get a lower bound of the size of this place. - // (When `extern type` are involved, use the size of the known prefix.) - let size = this - .size_and_align_of_mplace(&place)? - .map(|(size, _)| size) - .unwrap_or(place.layout.size); + // Determine the size of the reborrow. + // For most types this is the entire size of the place, however + // - when `extern type` is involved we use the size of the known prefix, + // - if the pointer is not reborrowed (raw pointer) or if `zero_size` is set + // then we override the size to do a zero-length reborrow. + let reborrow_size = match new_perm { + Some(NewPermission { zero_size: false, .. }) => + this.size_and_align_of_mplace(&place)? + .map(|(size, _)| size) + .unwrap_or(place.layout.size), + _ => Size::from_bytes(0), + }; + trace!("Creating new permission: {:?} with size {:?}", new_perm, reborrow_size); // This new tag is not guaranteed to actually be used. // @@ -324,7 +331,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr(); // Compute the actual reborrow. - let reborrowed = this.tb_reborrow(&place, size, new_perm, new_tag)?; + let reborrowed = this.tb_reborrow(&place, reborrow_size, new_perm, new_tag)?; // Adjust pointer. let new_place = place.map_provenance(|p| { @@ -359,10 +366,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { val: &ImmTy<'tcx, Provenance>, ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { let this = self.eval_context_mut(); - let new_perm = if let &ty::Ref(_, pointee, mutability) = val.layout.ty.kind() { - NewPermission::from_ref_ty(pointee, mutability, kind, this) - } else { - None + let new_perm = match val.layout.ty.kind() { + &ty::Ref(_, pointee, mutability) => + NewPermission::from_ref_ty(pointee, mutability, kind, this), + _ => None, }; this.tb_retag_reference(val, new_perm) } @@ -408,7 +415,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { - let new_perm = NewPermission::from_box_ty(place.layout.ty, self.kind, self.ecx); + let new_perm = NewPermission::from_unique_ty( + place.layout.ty, + self.kind, + self.ecx, + /* zero_size */ false, + ); self.retag_ptr_inplace(place, new_perm) } @@ -486,7 +498,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // FIXME: do we truly want a 2phase borrow here? let new_perm = Some(NewPermission { initial_state: Permission::new_unique_2phase(/*freeze*/ false), - perform_read_access: true, + zero_size: false, protector: Some(ProtectorKind::StrongProtector), }); let val = this.tb_retag_reference(&val, new_perm)?; From 0f8cb04129a6d07ec711ae57ee2279a7482f65a8 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 26 Jun 2023 08:33:41 +0000 Subject: [PATCH 24/32] Update ui test crate --- Cargo.lock | 4 +- Cargo.toml | 2 +- tests/compiletest.rs | 102 ++++++++++++++----------------------------- 3 files changed, 36 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fa30dd21ea..60ebf3057a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -842,9 +842,9 @@ dependencies = [ [[package]] name = "ui_test" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "191a442639ea102fa62671026047e51d574bfda44b7fdf32151d7314624c1cd2" +checksum = "b75049e51d3db204b2de79c8ff7a8675c628d81ceef6ec97598c1ab7d4d66802" dependencies = [ "bstr", "cargo-platform", diff --git a/Cargo.toml b/Cargo.toml index 143ea6e542..98dab49f3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ libloading = "0.7" [dev-dependencies] colored = "2" -ui_test = "0.10" +ui_test = "0.11" rustc_version = "0.4" # Features chosen to match those required by env_logger, to avoid rebuilds regex = { version = "1.5.5", default-features = false, features = ["perf", "std"] } diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 9e6e370315..c57c6c2f25 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -3,7 +3,6 @@ use regex::bytes::Regex; use std::ffi::OsString; use std::path::{Path, PathBuf}; use std::{env, process::Command}; -use ui_test::status_emitter::StatusEmitter; use ui_test::CommandBuilder; use ui_test::{color_eyre::Result, Config, Match, Mode, OutputConflictHandling}; @@ -76,7 +75,7 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> let skip_ui_checks = env::var_os("MIRI_SKIP_UI_CHECKS").is_some(); let output_conflict_handling = match (env::var_os("MIRI_BLESS").is_some(), skip_ui_checks) { - (false, false) => OutputConflictHandling::Error, + (false, false) => OutputConflictHandling::Error("./miri bless".into()), (true, false) => OutputConflictHandling::Bless, (false, true) => OutputConflictHandling::Ignore, (true, true) => panic!("cannot use MIRI_BLESS and MIRI_SKIP_UI_CHECKS at the same time"), @@ -86,13 +85,11 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> target: Some(target.to_owned()), stderr_filters: STDERR.clone(), stdout_filters: STDOUT.clone(), - root_dir: PathBuf::from(path), mode, program, output_conflict_handling, - quiet: false, edition: Some("2021".into()), - ..Config::default() + ..Config::rustc(path.into()) }; let use_std = env::var_os("MIRI_NO_STD").is_none(); @@ -113,39 +110,48 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> } fn run_tests(mode: Mode, path: &str, target: &str, with_dependencies: bool) -> Result<()> { - let mut config = test_config(target, path, mode, with_dependencies); + let config = test_config(target, path, mode, with_dependencies); // Handle command-line arguments. let mut after_dashdash = false; - config.path_filter.extend(std::env::args().skip(1).filter(|arg| { - if after_dashdash { - // Just propagate everything. - return true; - } - match &**arg { - "--quiet" => { - config.quiet = true; - false - } - "--" => { - after_dashdash = true; - false + let mut quiet = false; + let filters = std::env::args() + .skip(1) + .filter(|arg| { + if after_dashdash { + // Just propagate everything. + return true; } - s if s.starts_with('-') => { - panic!("unknown compiletest flag `{s}`"); + match &**arg { + "--quiet" => { + quiet = true; + false + } + "--" => { + after_dashdash = true; + false + } + s if s.starts_with('-') => { + panic!("unknown compiletest flag `{s}`"); + } + _ => true, } - _ => true, - } - })); - + }) + .collect::>(); eprintln!(" Compiler: {}", config.program.display()); ui_test::run_tests_generic( config, // The files we're actually interested in (all `.rs` files). - |path| path.extension().is_some_and(|ext| ext == "rs"), + |path| { + path.extension().is_some_and(|ext| ext == "rs") + && (filters.is_empty() || filters.iter().any(|f| path.starts_with(f))) + }, // This could be used to overwrite the `Config` on a per-test basis. |_, _| None, - TextAndGha, + ( + ui_test::status_emitter::Text, + ui_test::status_emitter::Gha:: { name: format!("{mode:?} {path} ({target})") }, + ), ) } @@ -270,45 +276,3 @@ fn run_dep_mode(target: String, mut args: impl Iterator) -> Res cmd.args(args); if cmd.spawn()?.wait()?.success() { Ok(()) } else { std::process::exit(1) } } - -/// This is a custom renderer for `ui_test` output that does not emit github actions -/// `group`s, while still producing regular github actions messages on test failures. -struct TextAndGha; -impl StatusEmitter for TextAndGha { - fn failed_test<'a>( - &'a self, - revision: &'a str, - path: &'a Path, - cmd: &'a Command, - stderr: &'a [u8], - ) -> Box { - Box::new(( - ui_test::status_emitter::Gha::.failed_test(revision, path, cmd, stderr), - ui_test::status_emitter::Text.failed_test(revision, path, cmd, stderr), - )) - } - - fn run_tests(&self, _config: &Config) -> Box { - Box::new(TextAndGha) - } - - fn finalize( - &self, - failures: usize, - succeeded: usize, - ignored: usize, - filtered: usize, - ) -> Box { - Box::new(( - ui_test::status_emitter::Gha::.finalize(failures, succeeded, ignored, filtered), - ui_test::status_emitter::Text.finalize(failures, succeeded, ignored, filtered), - )) - } -} - -impl ui_test::status_emitter::DuringTestRun for TextAndGha { - fn test_result(&mut self, path: &Path, revision: &str, result: &ui_test::TestResult) { - ui_test::status_emitter::Text.test_result(path, revision, result); - ui_test::status_emitter::Gha::.test_result(path, revision, result); - } -} From 4eb8e48c5447828ad5a3cac1363ae80d19f667c7 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 27 Jun 2023 08:08:34 +0000 Subject: [PATCH 25/32] Make `--quiet` actually do something --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- tests/compiletest.rs | 13 ++++++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60ebf3057a..9fca72b095 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -842,9 +842,9 @@ dependencies = [ [[package]] name = "ui_test" -version = "0.11.0" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75049e51d3db204b2de79c8ff7a8675c628d81ceef6ec97598c1ab7d4d66802" +checksum = "24a2e70adc9d18b9b4dd80ea57aeec447103c6fbb354a07c080adad451c645e1" dependencies = [ "bstr", "cargo-platform", diff --git a/Cargo.toml b/Cargo.toml index 98dab49f3e..50864334fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ libloading = "0.7" [dev-dependencies] colored = "2" -ui_test = "0.11" +ui_test = "0.11.6" rustc_version = "0.4" # Features chosen to match those required by env_logger, to avoid rebuilds regex = { version = "1.5.5", default-features = false, features = ["perf", "std"] } diff --git a/tests/compiletest.rs b/tests/compiletest.rs index c57c6c2f25..d43dc6b92a 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -3,8 +3,8 @@ use regex::bytes::Regex; use std::ffi::OsString; use std::path::{Path, PathBuf}; use std::{env, process::Command}; -use ui_test::CommandBuilder; use ui_test::{color_eyre::Result, Config, Match, Mode, OutputConflictHandling}; +use ui_test::{status_emitter, CommandBuilder}; fn miri_path() -> PathBuf { PathBuf::from(option_env!("MIRI").unwrap_or(env!("CARGO_BIN_EXE_miri"))) @@ -149,8 +149,15 @@ fn run_tests(mode: Mode, path: &str, target: &str, with_dependencies: bool) -> R // This could be used to overwrite the `Config` on a per-test basis. |_, _| None, ( - ui_test::status_emitter::Text, - ui_test::status_emitter::Gha:: { name: format!("{mode:?} {path} ({target})") }, + if quiet { + Box::::default() + as Box + } else { + Box::new(status_emitter::Text) + }, + status_emitter::Gha:: { + name: format!("{mode:?} {path} ({target})"), + }, ), ) } From 4b12e68c6e1cfe7bdfe10b194af072910388074d Mon Sep 17 00:00:00 2001 From: Andy Wang Date: Tue, 27 Jun 2023 14:41:30 +0200 Subject: [PATCH 26/32] Add trophy Appears to have been missed --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 79b0daf9e8..8d23da53bc 100644 --- a/README.md +++ b/README.md @@ -575,6 +575,7 @@ Definite bugs found: * [`crossbeam-epoch` calling `assume_init` on a partly-initialized `MaybeUninit`](https://github.com/crossbeam-rs/crossbeam/pull/779) * [`integer-encoding` dereferencing a misaligned pointer](https://github.com/dermesser/integer-encoding-rs/pull/23) * [`rkyv` constructing a `Box<[u8]>` from an overaligned allocation](https://github.com/rkyv/rkyv/commit/a9417193a34757e12e24263178be8b2eebb72456) +* [Data race in `arc-swap`](https://github.com/vorner/arc-swap/issues/76) * [Data race in `thread::scope`](https://github.com/rust-lang/rust/issues/98498) * [`regex` incorrectly handling unaligned `Vec` buffers](https://www.reddit.com/r/rust/comments/vq3mmu/comment/ienc7t0?context=3) * [Incorrect use of `compare_exchange_weak` in `once_cell`](https://github.com/matklad/once_cell/issues/186) From 0b74f243136b79e64b38a043a44ff55c3af36799 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 28 Jun 2023 06:55:53 +0000 Subject: [PATCH 27/32] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 0ac1e277eb..3e024086a6 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -0928a1f7574f5ca019b5443b3a90008588d18c8c +08fd6f719ee764cf62659ddf481e22dbfe4b8894 From 4dcb95d98a958c8ed37d84cb94b598970b3a8e3b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 28 Jun 2023 07:12:31 +0000 Subject: [PATCH 28/32] fmt --- src/bin/miri.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 219617b1d6..cc14cfecea 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -31,9 +31,9 @@ use rustc_middle::{ query::{ExternProviders, LocalCrate}, ty::TyCtxt, }; -use rustc_session::{EarlyErrorHandler, CtfeBacktrace}; -use rustc_session::config::{OptLevel, CrateType, ErrorOutputType}; +use rustc_session::config::{CrateType, ErrorOutputType, OptLevel}; use rustc_session::search_paths::PathKind; +use rustc_session::{CtfeBacktrace, EarlyErrorHandler}; use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields}; From b55b7bc13f9d8e792a1d30046d510760056abf9f Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 28 Jun 2023 07:28:32 +0000 Subject: [PATCH 29/32] Try running a sync automatically --- .github/workflows/ci.yml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da1c2f770a..8068db1cf4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -156,13 +156,13 @@ jobs: - name: mark the job as a failure run: exit 1 - # Send a Zulip notification when a cron job fails cron-fail-notify: name: cronjob failure notification runs-on: ubuntu-latest needs: [build, style] if: github.event_name == 'schedule' && (failure() || cancelled()) steps: + # Send a Zulip notification - name: Install zulip-send run: pip3 install zulip - name: Send Zulip notification @@ -185,3 +185,27 @@ jobs: Sincerely, The Miri Cronjobs Bot' \ --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com + # Attempt to auto-sync with rustc + - name: install josh-proxy + run: cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r22.12.06 + - name: start josh-proxy + run: josh-proxy --local=$HOME/.cache/josh --remote=https://github.com --no-background & + - name: setup bot git name and email + run: | + git config --global user.name 'The Miri Conjob Bot' + git config --global user.email 'miri@cron.bot' + - name: get changes from rustc + run: ./miri rustc-pull + - name: format changes (if any) + run: | + ./miri toolchain + ./miri fmt --check || (./miri fmt && git commit -am "fmt") + - name: Push changes to a branch + run: | + git switch -c "rustup$(date -u +%Y-%m)" + git push + - name: Create Pull Request + run: gh pr create -B master --title 'Automatic sync from rustc' --body '' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + From 71f0db4e834ba8449a4644ddc54f0089673a31d5 Mon Sep 17 00:00:00 2001 From: Neven Villani Date: Mon, 19 Jun 2023 12:07:23 +0200 Subject: [PATCH 30/32] Unique gets special treatment when -Zmiri-unique-is-unique --- README.md | 3 + src/bin/miri.rs | 10 +++ src/borrow_tracker/mod.rs | 5 ++ src/borrow_tracker/tree_borrows/mod.rs | 47 ++++++++++++- src/eval.rs | 5 ++ .../children-can-alias.default.stderr | 15 ++++ tests/fail/tree_borrows/children-can-alias.rs | 59 ++++++++++++++++ .../children-can-alias.uniq.stderr | 37 ++++++++++ tests/fail/tree_borrows/unique.default.stderr | 32 +++++++++ tests/fail/tree_borrows/unique.rs | 27 ++++++++ tests/fail/tree_borrows/unique.uniq.stderr | 38 +++++++++++ tests/pass/tree_borrows/formatting.rs | 1 + tests/pass/tree_borrows/formatting.stderr | 2 +- tests/pass/tree_borrows/tree-borrows.rs | 2 + tests/pass/tree_borrows/unique.default.stderr | 21 ++++++ tests/pass/tree_borrows/unique.rs | 66 ++++++++++++++++++ tests/pass/tree_borrows/unique.uniq.stderr | 24 +++++++ .../tree_borrows/vec_unique.default.stderr | 6 ++ tests/pass/tree_borrows/vec_unique.rs | 68 +++++++++++++++++++ .../pass/tree_borrows/vec_unique.uniq.stderr | 8 +++ tests/pass/vec.rs | 5 +- tests/pass/vecdeque.rs | 6 +- tests/pass/vecdeque.tree_uniq.stdout | 2 + tests/utils/macros.rs | 8 +-- tests/utils/miri_extern.rs | 2 +- 25 files changed, 486 insertions(+), 13 deletions(-) create mode 100644 tests/fail/tree_borrows/children-can-alias.default.stderr create mode 100644 tests/fail/tree_borrows/children-can-alias.rs create mode 100644 tests/fail/tree_borrows/children-can-alias.uniq.stderr create mode 100644 tests/fail/tree_borrows/unique.default.stderr create mode 100644 tests/fail/tree_borrows/unique.rs create mode 100644 tests/fail/tree_borrows/unique.uniq.stderr create mode 100644 tests/pass/tree_borrows/unique.default.stderr create mode 100644 tests/pass/tree_borrows/unique.rs create mode 100644 tests/pass/tree_borrows/unique.uniq.stderr create mode 100644 tests/pass/tree_borrows/vec_unique.default.stderr create mode 100644 tests/pass/tree_borrows/vec_unique.rs create mode 100644 tests/pass/tree_borrows/vec_unique.uniq.stderr create mode 100644 tests/pass/vecdeque.tree_uniq.stdout diff --git a/README.md b/README.md index 79b0daf9e8..daf13984fb 100644 --- a/README.md +++ b/README.md @@ -435,6 +435,9 @@ to Miri failing to detect cases of undefined behavior in a program. so with this flag. * `-Zmiri-force-page-size=` overrides the default page size for an architecture, in multiples of 1k. `4` is default for most targets. This value should always be a power of 2 and nonzero. +* `-Zmiri-unique-is-unique` performs additional aliasing checks for `core::ptr::Unique` to ensure + that it could theoretically be considered `noalias`. This flag is experimental and has + an effect only when used with `-Zmiri-tree-borrows`. [function ABI]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 083dd4800d..8a51a7e663 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -340,6 +340,8 @@ fn main() { miri_config.borrow_tracker = None; } else if arg == "-Zmiri-tree-borrows" { miri_config.borrow_tracker = Some(BorrowTrackerMethod::TreeBorrows); + } else if arg == "-Zmiri-unique-is-unique" { + miri_config.unique_is_unique = true; } else if arg == "-Zmiri-disable-data-race-detector" { miri_config.data_race_detector = false; miri_config.weak_memory_emulation = false; @@ -557,6 +559,14 @@ fn main() { rustc_args.push(arg); } } + // `-Zmiri-unique-is-unique` should only be used with `-Zmiri-tree-borrows` + if miri_config.unique_is_unique + && !matches!(miri_config.borrow_tracker, Some(BorrowTrackerMethod::TreeBorrows)) + { + show_error!( + "-Zmiri-unique-is-unique only has an effect when -Zmiri-tree-borrows is also used" + ); + } debug!("rustc arguments: {:?}", rustc_args); debug!("crate arguments: {:?}", miri_config.args); diff --git a/src/borrow_tracker/mod.rs b/src/borrow_tracker/mod.rs index ffc49eedb5..faa23fd262 100644 --- a/src/borrow_tracker/mod.rs +++ b/src/borrow_tracker/mod.rs @@ -103,6 +103,8 @@ pub struct GlobalStateInner { pub tracked_call_ids: FxHashSet, /// Whether to recurse into datatypes when searching for pointers to retag. pub retag_fields: RetagFields, + /// Whether `core::ptr::Unique` gets special (`Box`-like) handling. + pub unique_is_unique: bool, } impl VisitTags for GlobalStateInner { @@ -170,6 +172,7 @@ impl GlobalStateInner { tracked_pointer_tags: FxHashSet, tracked_call_ids: FxHashSet, retag_fields: RetagFields, + unique_is_unique: bool, ) -> Self { GlobalStateInner { borrow_tracker_method, @@ -180,6 +183,7 @@ impl GlobalStateInner { tracked_pointer_tags, tracked_call_ids, retag_fields, + unique_is_unique, } } @@ -244,6 +248,7 @@ impl BorrowTrackerMethod { config.tracked_pointer_tags.clone(), config.tracked_call_ids.clone(), config.retag_fields, + config.unique_is_unique, )) } } diff --git a/src/borrow_tracker/tree_borrows/mod.rs b/src/borrow_tracker/tree_borrows/mod.rs index 31cc03828f..c4fc2fea74 100644 --- a/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/borrow_tracker/tree_borrows/mod.rs @@ -11,6 +11,7 @@ use rustc_middle::{ Ty, }, }; +use rustc_span::def_id::DefId; use crate::*; @@ -381,8 +382,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { place: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let retag_fields = this.machine.borrow_tracker.as_mut().unwrap().get_mut().retag_fields; - let mut visitor = RetagVisitor { ecx: this, kind, retag_fields }; + let options = this.machine.borrow_tracker.as_mut().unwrap().get_mut(); + let retag_fields = options.retag_fields; + let unique_did = + options.unique_is_unique.then(|| this.tcx.lang_items().ptr_unique()).flatten(); + let mut visitor = RetagVisitor { ecx: this, kind, retag_fields, unique_did }; return visitor.visit_value(place); // The actual visitor. @@ -390,6 +394,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ecx: &'ecx mut MiriInterpCx<'mir, 'tcx>, kind: RetagKind, retag_fields: RetagFields, + unique_did: Option, } impl<'ecx, 'mir, 'tcx> RetagVisitor<'ecx, 'mir, 'tcx> { #[inline(always)] // yes this helps in our benchmarks @@ -414,6 +419,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { self.ecx } + /// Regardless of how `Unique` is handled, Boxes are always reborrowed. + /// When `Unique` is also reborrowed, then it behaves exactly like `Box` + /// except for the fact that `Box` has a non-zero-sized reborrow. fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { let new_perm = NewPermission::from_unique_ty( place.layout.ty, @@ -452,6 +460,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // even if field retagging is not enabled. *shrug*) self.walk_value(place)?; } + ty::Adt(adt, _) if self.unique_did == Some(adt.did()) => { + let place = inner_ptr_of_unique(self.ecx, place)?; + let new_perm = NewPermission::from_unique_ty( + place.layout.ty, + self.kind, + self.ecx, + /* zero_size */ true, + ); + self.retag_ptr_inplace(&place, new_perm)?; + } _ => { // Not a reference/pointer/box. Only recurse if configured appropriately. let recurse = match self.retag_fields { @@ -468,7 +486,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } } - Ok(()) } } @@ -564,3 +581,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { tree_borrows.give_pointer_debug_name(tag, nth_parent, name) } } + +/// Takes a place for a `Unique` and turns it into a place with the inner raw pointer. +/// I.e. input is what you get from the visitor upon encountering an `adt` that is `Unique`, +/// and output can be used by `retag_ptr_inplace`. +fn inner_ptr_of_unique<'tcx>( + ecx: &mut MiriInterpCx<'_, 'tcx>, + place: &PlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, PlaceTy<'tcx, Provenance>> { + // Follows the same layout as `interpret/visitor.rs:walk_value` for `Box` in + // `rustc_const_eval`, just with one fewer layer. + // Here we have a `Unique(NonNull(*mut), PhantomData)` + assert_eq!(place.layout.fields.count(), 2, "Unique must have exactly 2 fields"); + let (nonnull, phantom) = (ecx.place_field(place, 0)?, ecx.place_field(place, 1)?); + assert!( + phantom.layout.ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()), + "2nd field of `Unique` should be `PhantomData` but is `{:?}`", + phantom.layout.ty, + ); + // Now down to `NonNull(*mut)` + assert_eq!(nonnull.layout.fields.count(), 1, "NonNull must have exactly 1 field"); + let ptr = ecx.place_field(&nonnull, 0)?; + // Finally a plain `*mut` + Ok(ptr) +} diff --git a/src/eval.rs b/src/eval.rs index 1e9d48be65..91e004e010 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -90,6 +90,10 @@ pub struct MiriConfig { pub validate: bool, /// Determines if Stacked Borrows or Tree Borrows is enabled. pub borrow_tracker: Option, + /// Whether `core::ptr::Unique` receives special treatment. + /// If `true` then `Unique` is reborrowed with its own new tag and permission, + /// otherwise `Unique` is just another raw pointer. + pub unique_is_unique: bool, /// Controls alignment checking. pub check_alignment: AlignmentCheck, /// Controls function [ABI](Abi) checking. @@ -156,6 +160,7 @@ impl Default for MiriConfig { env: vec![], validate: true, borrow_tracker: Some(BorrowTrackerMethod::StackedBorrows), + unique_is_unique: false, check_alignment: AlignmentCheck::Int, check_abi: true, isolated_op: IsolatedOp::Reject(RejectOpWith::Abort), diff --git a/tests/fail/tree_borrows/children-can-alias.default.stderr b/tests/fail/tree_borrows/children-can-alias.default.stderr new file mode 100644 index 0000000000..1b05be9c54 --- /dev/null +++ b/tests/fail/tree_borrows/children-can-alias.default.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> $DIR/children-can-alias.rs:LL:CC + | +LL | std::hint::unreachable_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ entering unreachable code + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/children-can-alias.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/tree_borrows/children-can-alias.rs b/tests/fail/tree_borrows/children-can-alias.rs new file mode 100644 index 0000000000..b5a01cd432 --- /dev/null +++ b/tests/fail/tree_borrows/children-can-alias.rs @@ -0,0 +1,59 @@ +//@revisions: default uniq +//@compile-flags: -Zmiri-tree-borrows +//@[uniq]compile-flags: -Zmiri-unique-is-unique + +//! This is NOT intended behavior. +//! We should eventually find a solution so that the version with `Unique` passes too, +//! otherwise `Unique` is more strict than `&mut`! + +#![feature(ptr_internals)] + +use core::ptr::addr_of_mut; +use core::ptr::Unique; + +fn main() { + let mut data = 0u8; + let raw = addr_of_mut!(data); + unsafe { + raw_children_of_refmut_can_alias(&mut *raw); + raw_children_of_unique_can_alias(Unique::new_unchecked(raw)); + + // Ultimately the intended behavior is that both above tests would + // succeed. + std::hint::unreachable_unchecked(); + //~[default]^ ERROR: entering unreachable code + } +} + +unsafe fn raw_children_of_refmut_can_alias(x: &mut u8) { + let child1 = addr_of_mut!(*x); + let child2 = addr_of_mut!(*x); + // We create two raw aliases of `x`: they have the exact same + // tag and can be used interchangeably. + child1.write(1); + child2.write(2); + child1.write(1); + child2.write(2); +} + +unsafe fn raw_children_of_unique_can_alias(x: Unique) { + let child1 = x.as_ptr(); + let child2 = x.as_ptr(); + // Under `-Zmiri-unique-is-unique`, `Unique` accidentally offers more guarantees + // than `&mut`. Not because it responds differently to accesses but because + // there is no easy way to obtain a copy with the same tag. + // + // The closest (non-hack) attempt is two calls to `as_ptr`. + // - Without `-Zmiri-unique-is-unique`, independent `as_ptr` calls return pointers + // with the same tag that can thus be used interchangeably. + // - With the current implementation of `-Zmiri-unique-is-unique`, they return cousin + // tags with permissions that do not tolerate aliasing. + // Eventually we should make such aliasing allowed in some situations + // (e.g. when there is no protector), which will probably involve + // introducing a new kind of permission. + child1.write(1); + child2.write(2); + //~[uniq]^ ERROR: /write access through .* is forbidden/ + child1.write(1); + child2.write(2); +} diff --git a/tests/fail/tree_borrows/children-can-alias.uniq.stderr b/tests/fail/tree_borrows/children-can-alias.uniq.stderr new file mode 100644 index 0000000000..2f77b27729 --- /dev/null +++ b/tests/fail/tree_borrows/children-can-alias.uniq.stderr @@ -0,0 +1,37 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/children-can-alias.rs:LL:CC + | +LL | child2.write(2); + | ^^^^^^^^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Disabled which forbids this child write access +help: the accessed tag was created here + --> $DIR/children-can-alias.rs:LL:CC + | +LL | let child2 = x.as_ptr(); + | ^^^^^^^^^^ +help: the conflicting tag was created here, in the initial state Reserved + --> $DIR/children-can-alias.rs:LL:CC + | +LL | let child2 = x.as_ptr(); + | ^ +help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x1] + --> $DIR/children-can-alias.rs:LL:CC + | +LL | child1.write(1); + | ^^^^^^^^^^^^^^^ + = help: this transition corresponds to a loss of read and write permissions + = note: BACKTRACE (of the first span): + = note: inside `raw_children_of_unique_can_alias` at $DIR/children-can-alias.rs:LL:CC +note: inside `main` + --> $DIR/children-can-alias.rs:LL:CC + | +LL | raw_children_of_unique_can_alias(Unique::new_unchecked(raw)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/tree_borrows/unique.default.stderr b/tests/fail/tree_borrows/unique.default.stderr new file mode 100644 index 0000000000..eb4d5c10c2 --- /dev/null +++ b/tests/fail/tree_borrows/unique.default.stderr @@ -0,0 +1,32 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/unique.rs:LL:CC + | +LL | *uniq.as_ptr() = 3; + | ^^^^^^^^^^^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag has state Frozen which forbids this child write access +help: the accessed tag was created here, in the initial state Reserved + --> $DIR/unique.rs:LL:CC + | +LL | let refmut = &mut data; + | ^^^^^^^^^ +help: the accessed tag later transitioned to Active due to a child write access at offsets [0x0..0x1] + --> $DIR/unique.rs:LL:CC + | +LL | *uniq.as_ptr() = 1; // activation + | ^^^^^^^^^^^^^^^^^^ + = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference +help: the accessed tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1] + --> $DIR/unique.rs:LL:CC + | +LL | let _definitely_parent = data; // definitely Frozen by now + | ^^^^ + = help: this transition corresponds to a loss of write permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/unique.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/tree_borrows/unique.rs b/tests/fail/tree_borrows/unique.rs new file mode 100644 index 0000000000..0844dd21a5 --- /dev/null +++ b/tests/fail/tree_borrows/unique.rs @@ -0,0 +1,27 @@ +//@revisions: default uniq +//@compile-flags: -Zmiri-tree-borrows +//@[uniq]compile-flags: -Zmiri-unique-is-unique + +// A pattern that detects if `Unique` is treated as exclusive or not: +// activate the pointer behind a `Unique` then do a read that is parent +// iff `Unique` was specially reborrowed. + +#![feature(ptr_internals)] +use core::ptr::Unique; + +fn main() { + let mut data = 0u8; + let refmut = &mut data; + let rawptr = refmut as *mut u8; + + unsafe { + let uniq = Unique::new_unchecked(rawptr); + *uniq.as_ptr() = 1; // activation + let _maybe_parent = *rawptr; // maybe becomes Frozen + *uniq.as_ptr() = 2; + //~[uniq]^ ERROR: /write access through .* is forbidden/ + let _definitely_parent = data; // definitely Frozen by now + *uniq.as_ptr() = 3; + //~[default]^ ERROR: /write access through .* is forbidden/ + } +} diff --git a/tests/fail/tree_borrows/unique.uniq.stderr b/tests/fail/tree_borrows/unique.uniq.stderr new file mode 100644 index 0000000000..2d8936f273 --- /dev/null +++ b/tests/fail/tree_borrows/unique.uniq.stderr @@ -0,0 +1,38 @@ +error: Undefined Behavior: write access through is forbidden + --> $DIR/unique.rs:LL:CC + | +LL | *uniq.as_ptr() = 2; + | ^^^^^^^^^^^^^^^^^^ write access through is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag is a child of the conflicting tag + = help: the conflicting tag has state Frozen which forbids this child write access +help: the accessed tag was created here + --> $DIR/unique.rs:LL:CC + | +LL | *uniq.as_ptr() = 2; + | ^^^^^^^^^^^^^ +help: the conflicting tag was created here, in the initial state Reserved + --> $DIR/unique.rs:LL:CC + | +LL | let uniq = Unique::new_unchecked(rawptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the conflicting tag later transitioned to Active due to a child write access at offsets [0x0..0x1] + --> $DIR/unique.rs:LL:CC + | +LL | *uniq.as_ptr() = 1; // activation + | ^^^^^^^^^^^^^^^^^^ + = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference +help: the conflicting tag later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1] + --> $DIR/unique.rs:LL:CC + | +LL | let _maybe_parent = *rawptr; // maybe becomes Frozen + | ^^^^^^^ + = help: this transition corresponds to a loss of write permissions + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/unique.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/pass/tree_borrows/formatting.rs b/tests/pass/tree_borrows/formatting.rs index 9021c41763..64697cac26 100644 --- a/tests/pass/tree_borrows/formatting.rs +++ b/tests/pass/tree_borrows/formatting.rs @@ -17,6 +17,7 @@ fn main() { unsafe fn alignment_check() { let data: &mut [u8] = &mut [0; 1024]; name!(data.as_ptr()=>2, "data"); + name!(data.as_ptr()=>2, "data"); let alloc_id = alloc_id!(data.as_ptr()); let x = &mut data[1]; name!(x as *mut _, "data[1]"); diff --git a/tests/pass/tree_borrows/formatting.stderr b/tests/pass/tree_borrows/formatting.stderr index 8c3779fe1f..effd0d9f96 100644 --- a/tests/pass/tree_borrows/formatting.stderr +++ b/tests/pass/tree_borrows/formatting.stderr @@ -2,7 +2,7 @@ Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1.. 2.. 10.. 11..100..101..1000..1001..1024 | Act| Act| Act| Act| Act| Act| Act| Act| Act| └─┬── -| Res| Act| Res| Act| Res| Act| Res| Act| Res| └─┬── +| Res| Act| Res| Act| Res| Act| Res| Act| Res| └─┬── |----| Act|----|?Dis|----|?Dis| ----| ?Dis| ----| ├──── |----|----|----| Act|----|?Dis| ----| ?Dis| ----| ├──── |----|----|----|----|----| Frz| ----| ?Dis| ----| ├──── diff --git a/tests/pass/tree_borrows/tree-borrows.rs b/tests/pass/tree_borrows/tree-borrows.rs index aa6f707889..6bdad69596 100644 --- a/tests/pass/tree_borrows/tree-borrows.rs +++ b/tests/pass/tree_borrows/tree-borrows.rs @@ -1,4 +1,6 @@ +//@revisions: default uniq //@compile-flags: -Zmiri-tree-borrows +//@[uniq]compile-flags: -Zmiri-unique-is-unique #![feature(allocator_api)] use std::mem; diff --git a/tests/pass/tree_borrows/unique.default.stderr b/tests/pass/tree_borrows/unique.default.stderr new file mode 100644 index 0000000000..11e05d50f2 --- /dev/null +++ b/tests/pass/tree_borrows/unique.default.stderr @@ -0,0 +1,21 @@ +────────────────────────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 1 +| Act| └─┬── +| Res| └─┬── +| Res| └──── +────────────────────────────────────────────────────────────────────── +────────────────────────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 1 +| Act| └─┬── +| Act| └─┬── +| Act| └──── +────────────────────────────────────────────────────────────────────── +────────────────────────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 1 +| Act| └─┬── +| Act| └─┬── +| Act| └──── +────────────────────────────────────────────────────────────────────── diff --git a/tests/pass/tree_borrows/unique.rs b/tests/pass/tree_borrows/unique.rs new file mode 100644 index 0000000000..d0c3d133da --- /dev/null +++ b/tests/pass/tree_borrows/unique.rs @@ -0,0 +1,66 @@ +//@revisions: default uniq +//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0 +//@[uniq]compile-flags: -Zmiri-unique-is-unique + +#![feature(ptr_internals)] + +#[path = "../../utils/mod.rs"] +mod utils; +use utils::macros::*; + +use core::ptr::Unique; + +// Check general handling of Unique + +fn main() { + unsafe { + let base = &mut 5u8; + let alloc_id = alloc_id!(base); + name!(base); + + let raw = &mut *base as *mut u8; + name!(raw); + + // We create a `Unique` and expect it to have a fresh tag + // and uninitialized permissions. + let uniq = Unique::new_unchecked(raw); + + // With `-Zmiri-unique-is-unique`, `Unique::as_ptr` (which is called by + // `Vec::as_ptr`) generates pointers with a fresh tag, so to name the actual + // `base` pointer we care about we have to walk up the tree a bit. + // + // We care about naming this specific parent tag because it is the one + // that stays `Active` during the entire execution, unlike the leaves + // that will be invalidated the next time `as_ptr` is called. + // + // (We name it twice so that we have an indicator in the output of + // whether we got the distance correct: + // If the output shows + // + // |- + // '- + // + // then `nth_parent` is not big enough. + // The correct value for `nth_parent` should be the minimum + // integer for which the output shows + // + // '- + // ) + // + // Ultimately we want pointers obtained through independent + // calls of `as_ptr` to be able to alias, which will probably involve + // a new permission that allows aliasing when there is no protector. + let nth_parent = if cfg!(uniq) { 2 } else { 0 }; + name!(uniq.as_ptr()=>nth_parent, "uniq"); + name!(uniq.as_ptr()=>nth_parent, "uniq"); + print_state!(alloc_id); + + // We can activate the Unique and use it mutably. + *uniq.as_ptr() = 42; + print_state!(alloc_id); + + // Write through the raw parent disables the Unique + *raw = 42; + print_state!(alloc_id); + } +} diff --git a/tests/pass/tree_borrows/unique.uniq.stderr b/tests/pass/tree_borrows/unique.uniq.stderr new file mode 100644 index 0000000000..5008b66741 --- /dev/null +++ b/tests/pass/tree_borrows/unique.uniq.stderr @@ -0,0 +1,24 @@ +────────────────────────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 1 +| Act| └─┬── +| Res| └─┬── +| Res| └─┬── +|----| └──── +────────────────────────────────────────────────────────────────────── +────────────────────────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 1 +| Act| └─┬── +| Act| └─┬── +| Act| └─┬── +| Act| └──── +────────────────────────────────────────────────────────────────────── +────────────────────────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 1 +| Act| └─┬── +| Act| └─┬── +| Act| └─┬── +| Dis| └──── +────────────────────────────────────────────────────────────────────── diff --git a/tests/pass/tree_borrows/vec_unique.default.stderr b/tests/pass/tree_borrows/vec_unique.default.stderr new file mode 100644 index 0000000000..f1af1ea3d8 --- /dev/null +++ b/tests/pass/tree_borrows/vec_unique.default.stderr @@ -0,0 +1,6 @@ +───────────────────────────────────────────────────────────────────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 2 +| Act| └─┬── +| Res| └──── +───────────────────────────────────────────────────────────────────────────────────────────────────────────────── diff --git a/tests/pass/tree_borrows/vec_unique.rs b/tests/pass/tree_borrows/vec_unique.rs new file mode 100644 index 0000000000..3516f8d2eb --- /dev/null +++ b/tests/pass/tree_borrows/vec_unique.rs @@ -0,0 +1,68 @@ +//@revisions: default uniq +//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0 +//@[uniq]compile-flags: -Zmiri-unique-is-unique + +#![feature(vec_into_raw_parts)] + +#[path = "../../utils/mod.rs"] +mod utils; +use utils::macros::*; + +// Check general handling of `Unique`: +// there is no *explicit* `Unique` being used here, but there is one +// hidden a few layers inside `Vec` that should be reflected in the tree structure. + +fn main() { + unsafe { + let base = vec![0u8, 1]; + let alloc_id = alloc_id!(base.as_ptr()); + + // With `-Zmiri-unique-is-unique`, `Unique::as_ptr` (which is called by + // `Vec::as_ptr`) generates pointers with a fresh tag, so to name the actual + // `base` pointer we care about we have to walk up the tree a bit. + // + // We care about naming this specific parent tag because it is the one + // that stays `Active` during the entire execution, unlike the leaves + // that will be invalidated the next time `as_ptr` is called. + // + // (We name it twice so that we have an indicator in the output of + // whether we got the distance correct: + // If the output shows + // + // |- + // '- + // + // then `nth_parent` is not big enough. + // The correct value for `nth_parent` should be the minimum + // integer for which the output shows + // + // '- + // ) + // + // Ultimately we want pointers obtained through independent + // calls of `as_ptr` to be able to alias, which will probably involve + // a new permission that allows aliasing when there is no protector. + let nth_parent = if cfg!(uniq) { 2 } else { 0 }; + name!(base.as_ptr()=>nth_parent); + name!(base.as_ptr()=>nth_parent); + + // Destruct the `Vec` + let (ptr, len, cap) = base.into_raw_parts(); + + // Expect this to be again the same pointer as the one obtained from `as_ptr`. + // Under `-Zmiri-unique-is-unique`, this will be a strict child. + name!(ptr, "raw_parts.0"); + + // This is where the presence of `Unique` has implications, + // because there will be a reborrow here iff the exclusivity of `Unique` + // is enforced. + let reconstructed = Vec::from_raw_parts(ptr, len, cap); + + // The `as_ptr` here (twice for the same reason as above) return either + // the same pointer once more (default) or a strict child (uniq). + name!(reconstructed.as_ptr()=>nth_parent); + name!(reconstructed.as_ptr()=>nth_parent); + + print_state!(alloc_id, false); + } +} diff --git a/tests/pass/tree_borrows/vec_unique.uniq.stderr b/tests/pass/tree_borrows/vec_unique.uniq.stderr new file mode 100644 index 0000000000..00ff1ee00e --- /dev/null +++ b/tests/pass/tree_borrows/vec_unique.uniq.stderr @@ -0,0 +1,8 @@ +────────────────────────────────────────────────────────────────────────── +Warning: this tree is indicative only. Some tags may have been hidden. +0.. 2 +| Act| └─┬── +|----| └─┬── +|----| └─┬── +|----| └──── +────────────────────────────────────────────────────────────────────────── diff --git a/tests/pass/vec.rs b/tests/pass/vec.rs index 06ec2f9917..048f7d1c35 100644 --- a/tests/pass/vec.rs +++ b/tests/pass/vec.rs @@ -1,6 +1,7 @@ -//@revisions: stack tree -//@[tree]compile-flags: -Zmiri-tree-borrows +//@revisions: stack tree tree_uniq //@compile-flags: -Zmiri-strict-provenance +//@[tree]compile-flags: -Zmiri-tree-borrows +//@[tree_uniq]compile-flags: -Zmiri-tree-borrows -Zmiri-unique-is-unique #![feature(iter_advance_by, iter_next_chunk)] // Gather all references from a mutable iterator and make sure Miri notices if diff --git a/tests/pass/vecdeque.rs b/tests/pass/vecdeque.rs index dfbf9bb83c..ccecf3d30a 100644 --- a/tests/pass/vecdeque.rs +++ b/tests/pass/vecdeque.rs @@ -1,6 +1,8 @@ -//@revisions: stack tree -//@[tree]compile-flags: -Zmiri-tree-borrows +//@revisions: stack tree tree_uniq //@compile-flags: -Zmiri-strict-provenance +//@[tree]compile-flags: -Zmiri-tree-borrows +//@[tree_uniq]compile-flags: -Zmiri-tree-borrows -Zmiri-unique-is-unique + use std::collections::VecDeque; fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator) { diff --git a/tests/pass/vecdeque.tree_uniq.stdout b/tests/pass/vecdeque.tree_uniq.stdout new file mode 100644 index 0000000000..63de960ee2 --- /dev/null +++ b/tests/pass/vecdeque.tree_uniq.stdout @@ -0,0 +1,2 @@ +[2, 2] Iter([2, 2], []) +Iter([], []) diff --git a/tests/utils/macros.rs b/tests/utils/macros.rs index de223410fb..28b4095430 100644 --- a/tests/utils/macros.rs +++ b/tests/utils/macros.rs @@ -47,12 +47,12 @@ macro_rules! name { ($ptr:expr) => { crate::utils::macros::name!($ptr => 0, stringify!($ptr)); }; - ($ptr:expr => $nb:expr) => { - crate::utils::macros::name!($ptr => $nb, stringify!($ptr)); + ($ptr:expr => $nth_parent:expr) => { + crate::utils::macros::name!($ptr => $nth_parent, stringify!($ptr)); }; - ($ptr:expr => $nb:expr, $name:expr) => { + ($ptr:expr => $nth_parent:expr, $name:expr) => { let name = $name.as_bytes(); - crate::utils::miri_extern::miri_pointer_name($ptr as *const u8 as *const (), $nb, name); + crate::utils::miri_extern::miri_pointer_name($ptr as *const u8 as *const (), $nth_parent, name); }; } diff --git a/tests/utils/miri_extern.rs b/tests/utils/miri_extern.rs index 6c4298c613..55f3c1cc33 100644 --- a/tests/utils/miri_extern.rs +++ b/tests/utils/miri_extern.rs @@ -65,7 +65,7 @@ extern "Rust" { /// /// This is only useful as an input to `miri_print_borrow_stacks`, and it is a separate call because /// getting a pointer to an allocation at runtime can change the borrow stacks in the allocation. - /// This function should be considered unstable. It exists only to support `miri_print_borrow_stacks` and so + /// This function should be considered unstable. It exists only to support `miri_print_borrow_state` and so /// inherits all of its instability. pub fn miri_get_alloc_id(ptr: *const ()) -> u64; From fea7ddf9de38dc3687dc85684aeb4217c504829c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 29 Jun 2023 08:42:56 +0200 Subject: [PATCH 31/32] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 3e024086a6..6790454bdb 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -08fd6f719ee764cf62659ddf481e22dbfe4b8894 +75726cae37317c7262b69d3e9fd11a3496a88d04 From 6e3da5fc288888e5fb95c16003a9aef7cdf8575e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 29 Jun 2023 10:16:13 +0000 Subject: [PATCH 32/32] Use a valid `target` directory in miri ui tests --- tests/compiletest.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index d43dc6b92a..73671e716e 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -88,6 +88,7 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> mode, program, output_conflict_handling, + out_dir: PathBuf::from(std::env::var_os("CARGO_TARGET_DIR").unwrap()).join("ui"), edition: Some("2021".into()), ..Config::rustc(path.into()) };