offset, offset_from: allow zero-byte offset on arbitrary pointers

This commit is contained in:
Ralf Jung 2024-05-09 12:35:11 +02:00
parent ba956ef4b0
commit 5c33a5690d
48 changed files with 200 additions and 475 deletions

View file

@ -25,9 +25,9 @@
use crate::errors::{LongRunning, LongRunningWarn};
use crate::fluent_generated as fluent;
use crate::interpret::{
self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom,
self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, throw_unsup,
throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, FnVal, Frame,
ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar,
GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar,
};
use super::error::*;
@ -759,11 +759,21 @@ fn before_alloc_read(
ecx: &InterpCx<'mir, 'tcx, Self>,
alloc_id: AllocId,
) -> InterpResult<'tcx> {
// Check if this is the currently evaluated static.
if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) {
Err(ConstEvalErrKind::RecursiveStatic.into())
} else {
Ok(())
return Err(ConstEvalErrKind::RecursiveStatic.into());
}
// If this is another static, make sure we fire off the query to detect cycles.
// But only do that when checks for static recursion are enabled.
if ecx.machine.static_root_ids.is_some() {
if let Some(GlobalAlloc::Static(def_id)) = ecx.tcx.try_get_global_alloc(alloc_id) {
if ecx.tcx.is_foreign_item(def_id) {
throw_unsup!(ExternStatic(def_id));
}
ecx.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
}
}
Ok(())
}
}

View file

@ -255,6 +255,7 @@ pub fn emulate_intrinsic(
name = intrinsic_name,
);
}
// This will always return 0.
(a, b)
}
(Err(_), _) | (_, Err(_)) => {

View file

@ -413,6 +413,8 @@ pub fn check_ptr_access(
/// to the allocation it points to. Supports both shared and mutable references, as the actual
/// checking is offloaded to a helper closure.
///
/// `alloc_size` will only get called for non-zero-sized accesses.
///
/// Returns `None` if and only if the size is 0.
fn check_and_deref_ptr<T>(
&self,
@ -425,18 +427,19 @@ fn check_and_deref_ptr<T>(
M::ProvenanceExtra,
) -> InterpResult<'tcx, (Size, Align, T)>,
) -> InterpResult<'tcx, Option<T>> {
// Everything is okay with size 0.
if size.bytes() == 0 {
return Ok(None);
}
Ok(match self.ptr_try_get_alloc_id(ptr) {
Err(addr) => {
// We couldn't get a proper allocation. This is only okay if the access size is 0,
// and the address is not null.
if size.bytes() > 0 || addr == 0 {
throw_ub!(DanglingIntPointer(addr, msg));
}
None
// We couldn't get a proper allocation.
throw_ub!(DanglingIntPointer(addr, msg));
}
Ok((alloc_id, offset, prov)) => {
let (alloc_size, _alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?;
// Test bounds. This also ensures non-null.
// Test bounds.
// It is sufficient to check this for the end pointer. Also check for overflow!
if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) {
throw_ub!(PointerOutOfBounds {
@ -447,14 +450,8 @@ fn check_and_deref_ptr<T>(
msg,
})
}
// Ensure we never consider the null pointer dereferenceable.
if M::Provenance::OFFSET_IS_ADDR {
assert_ne!(ptr.addr(), Size::ZERO);
}
// We can still be zero-sized in this branch, in which case we have to
// return `None`.
if size.bytes() == 0 { None } else { Some(ret_val) }
Some(ret_val)
}
})
}
@ -641,16 +638,18 @@ pub fn get_ptr_alloc<'a>(
size,
CheckInAllocMsg::MemoryAccessTest,
|alloc_id, offset, prov| {
if !self.memory.validation_in_progress.get() {
// We want to call the hook on *all* accesses that involve an AllocId,
// including zero-sized accesses. That means we have to do it here
// rather than below in the `Some` branch.
M::before_alloc_read(self, alloc_id)?;
}
let alloc = self.get_alloc_raw(alloc_id)?;
Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc)))
},
)?;
// We want to call the hook on *all* accesses that involve an AllocId, including zero-sized
// accesses. That means we cannot rely on the closure above or the `Some` branch below. We
// do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked.
if !self.memory.validation_in_progress.get() {
if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr) {
M::before_alloc_read(self, alloc_id)?;
}
}
if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc {
let range = alloc_range(offset, size);

View file

@ -434,6 +434,10 @@ fn check_safe_pointer(
found_bytes: has.bytes()
},
);
// Make sure this is non-null. (ZST references can be dereferenceable and null.)
if self.ecx.scalar_may_be_null(Scalar::from_maybe_pointer(place.ptr(), self.ecx))? {
throw_validation_failure!(self.path, NullPtr { ptr_kind })
}
// Do not allow pointers to uninhabited types.
if place.layout.abi.is_uninhabited() {
let ty = place.layout.ty;
@ -456,8 +460,8 @@ fn check_safe_pointer(
// `!` is a ZST and we want to validate it.
if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) {
let mut skip_recursive_check = false;
let alloc_actual_mutbl = mutability(self.ecx, alloc_id);
if let GlobalAlloc::Static(did) = self.ecx.tcx.global_alloc(alloc_id) {
if let Some(GlobalAlloc::Static(did)) = self.ecx.tcx.try_get_global_alloc(alloc_id)
{
let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { bug!() };
// Special handling for pointers to statics (irrespective of their type).
assert!(!self.ecx.tcx.is_thread_local_static(did));
@ -495,6 +499,7 @@ fn check_safe_pointer(
// If this allocation has size zero, there is no actual mutability here.
let (size, _align, _alloc_kind) = self.ecx.get_alloc_info(alloc_id);
if size != Size::ZERO {
let alloc_actual_mutbl = mutability(self.ecx, alloc_id);
// Mutable pointer to immutable memory is no good.
if ptr_expected_mutbl == Mutability::Mut
&& alloc_actual_mutbl == Mutability::Not
@ -831,6 +836,8 @@ fn visit_value(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx>
trace!("visit_value: {:?}, {:?}", *op, op.layout);
// Check primitive types -- the leaves of our recursive descent.
// We assume that the Scalar validity range does not restrict these values
// any further than `try_visit_primitive` does!
if self.try_visit_primitive(op)? {
return Ok(());
}

View file

@ -1483,10 +1483,10 @@ pub const fn unlikely(b: bool) -> bool {
///
/// # Safety
///
/// Both the starting and resulting pointer must be either in bounds or one
/// byte past the end of an allocated object. If either pointer is out of
/// bounds or arithmetic overflow occurs then any further use of the
/// returned value will result in undefined behavior.
/// If the computed offset is non-zero, then both the starting and resulting pointer must be
/// either in bounds or one byte past the end of an allocated object. If either pointer is out
/// of bounds or arithmetic overflow occurs then any further use of the returned value will
/// result in undefined behavior.
///
/// The stabilized version of this intrinsic is [`pointer::offset`].
#[must_use = "returns a new pointer rather than modifying its argument"]

View file

@ -465,8 +465,9 @@ pub const fn to_raw_parts(self) -> (*const (), <T as super::Pointee>::Metadata)
/// If any of the following conditions are violated, the result is Undefined
/// Behavior:
///
/// * Both the starting and resulting pointer must be either in bounds or one
/// byte past the end of the same [allocated object].
/// * If the computed offset is non-zero, then both the starting and resulting pointer must be
/// either in bounds or one byte past the end of the same [allocated object].
/// (If it is zero, then the function is always well-defined.)
///
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
///
@ -676,11 +677,11 @@ pub fn mask(self, mask: usize) -> *const T {
/// If any of the following conditions are violated, the result is Undefined
/// Behavior:
///
/// * Both `self` and `origin` must be either in bounds or one
/// byte past the end of the same [allocated object].
/// * `self` and `origin` must either
///
/// * Both pointers must be *derived from* a pointer to the same object.
/// (See below for an example.)
/// * both be *derived from* a pointer to the same [allocated object], and the memory range between
/// the two pointers must be either empty or in bounds of that object. (See below for an example.)
/// * or both be derived from an integer literal/constant, and point to the same address.
///
/// * The distance between the pointers, in bytes, must be an exact multiple
/// of the size of `T`.
@ -951,8 +952,9 @@ pub const fn guaranteed_ne(self, other: *const T) -> Option<bool>
/// If any of the following conditions are violated, the result is Undefined
/// Behavior:
///
/// * Both the starting and resulting pointer must be either in bounds or one
/// byte past the end of the same [allocated object].
/// * If the computed offset is non-zero, then both the starting and resulting pointer must be
/// either in bounds or one byte past the end of the same [allocated object].
/// (If it is zero, then the function is always well-defined.)
///
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
///
@ -1035,8 +1037,9 @@ pub const fn guaranteed_ne(self, other: *const T) -> Option<bool>
/// If any of the following conditions are violated, the result is Undefined
/// Behavior:
///
/// * Both the starting and resulting pointer must be either in bounds or one
/// byte past the end of the same [allocated object].
/// * If the computed offset is non-zero, then both the starting and resulting pointer must be
/// either in bounds or one byte past the end of the same [allocated object].
/// (If it is zero, then the function is always well-defined.)
///
/// * The computed offset cannot exceed `isize::MAX` **bytes**.
///

View file

@ -15,18 +15,13 @@
//! The precise rules for validity are not determined yet. The guarantees that are
//! provided at this point are very minimal:
//!
//! * A [null] pointer is *never* valid, not even for accesses of [size zero][zst].
//! * For operations of [size zero][zst], *every* pointer is valid, including the [null] pointer.
//! The following points are only concerned with non-zero-sized accesses.
//! * A [null] pointer is *never* valid.
//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer
//! be *dereferenceable*: the memory range of the given size starting at the pointer must all be
//! within the bounds of a single allocated object. Note that in Rust,
//! every (stack-allocated) variable is considered a separate allocated object.
//! * Even for operations of [size zero][zst], the pointer must not be pointing to deallocated
//! memory, i.e., deallocation makes pointers invalid even for zero-sized operations. However,
//! casting any non-zero integer *literal* to a pointer is valid for zero-sized accesses, even if
//! some memory happens to exist at that address and gets deallocated. This corresponds to writing
//! your own allocator: allocating zero-sized objects is not very hard. The canonical way to
//! obtain a pointer that is valid for zero-sized accesses is [`NonNull::dangling`].
//FIXME: mention `ptr::dangling` above, once it is stable.
//! * All accesses performed by functions in this module are *non-atomic* in the sense
//! of [atomic operations] used to synchronize between threads. This means it is
//! undefined behavior to perform two concurrent accesses to the same location from different

View file

@ -480,8 +480,9 @@ pub const fn to_raw_parts(self) -> (*mut (), <T as super::Pointee>::Metadata) {
/// If any of the following conditions are violated, the result is Undefined
/// Behavior:
///
/// * Both the starting and resulting pointer must be either in bounds or one
/// byte past the end of the same [allocated object].
/// * If the computed offset is non-zero, then both the starting and resulting pointer must be
/// either in bounds or one byte past the end of the same [allocated object].
/// (If it is zero, then the function is always well-defined.)
///
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
///
@ -904,11 +905,11 @@ pub const fn guaranteed_ne(self, other: *mut T) -> Option<bool>
/// If any of the following conditions are violated, the result is Undefined
/// Behavior:
///
/// * Both `self` and `origin` must be either in bounds or one
/// byte past the end of the same [allocated object].
/// * `self` and `origin` must either
///
/// * Both pointers must be *derived from* a pointer to the same object.
/// (See below for an example.)
/// * both be *derived from* a pointer to the same [allocated object], and the memory range between
/// the two pointers must be either empty or in bounds of that object. (See below for an example.)
/// * or both be derived from an integer literal/constant, and point to the same address.
///
/// * The distance between the pointers, in bytes, must be an exact multiple
/// of the size of `T`.
@ -1095,8 +1096,9 @@ pub const fn guaranteed_ne(self, other: *mut T) -> Option<bool>
/// If any of the following conditions are violated, the result is Undefined
/// Behavior:
///
/// * Both the starting and resulting pointer must be either in bounds or one
/// byte past the end of the same [allocated object].
/// * If the computed offset is non-zero, then both the starting and resulting pointer must be
/// either in bounds or one byte past the end of the same [allocated object].
/// (If it is zero, then the function is always well-defined.)
///
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
///
@ -1179,8 +1181,9 @@ pub const fn guaranteed_ne(self, other: *mut T) -> Option<bool>
/// If any of the following conditions are violated, the result is Undefined
/// Behavior:
///
/// * Both the starting and resulting pointer must be either in bounds or one
/// byte past the end of the same [allocated object].
/// * If the computed offset is non-zero, then both the starting and resulting pointer must be
/// either in bounds or one byte past the end of the same [allocated object].
/// (If it is zero, then the function is always well-defined.)
///
/// * The computed offset cannot exceed `isize::MAX` **bytes**.
///

View file

@ -1,10 +0,0 @@
// Make sure we find these even with many checks disabled.
//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation
fn main() {
let p = {
let b = Box::new(42);
&*b as *const i32 as *const ()
};
let _x = unsafe { *p }; //~ ERROR: has been freed
}

View file

@ -1,25 +0,0 @@
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
--> $DIR/dangling_zst_deref.rs:LL:CC
|
LL | let _x = unsafe { *p };
| ^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
= 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
help: ALLOC was allocated here:
--> $DIR/dangling_zst_deref.rs:LL:CC
|
LL | let b = Box::new(42);
| ^^^^^^^^^^^^
help: ALLOC was deallocated here:
--> $DIR/dangling_zst_deref.rs:LL:CC
|
LL | };
| ^
= note: BACKTRACE (of the first span):
= note: inside `main` at $DIR/dangling_zst_deref.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -1,5 +0,0 @@
fn main() {
// This pointer *could* be NULL so we cannot load from it, not even at ZST
let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *const ();
let _x: () = unsafe { *ptr }; //~ ERROR: out-of-bounds
}

View file

@ -1,15 +0,0 @@
error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
--> $DIR/maybe_null_pointer_deref_zst.rs:LL:CC
|
LL | let _x: () = unsafe { *ptr };
| ^^^^ memory access failed: ALLOC has size 1, so pointer at offset -2048 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 `main` at $DIR/maybe_null_pointer_deref_zst.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -1,8 +0,0 @@
fn main() {
// This pointer *could* be NULL so we cannot load from it, not even at ZST.
// Not using the () type here, as writes of that type do not even have MIR generated.
// Also not assigning directly as that's array initialization, not assignment.
let zst_val = [1u8; 0];
let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *mut [u8; 0];
unsafe { *ptr = zst_val }; //~ ERROR: out-of-bounds
}

View file

@ -1,15 +0,0 @@
error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
--> $DIR/maybe_null_pointer_write_zst.rs:LL:CC
|
LL | unsafe { *ptr = zst_val };
| ^^^^^^^^^^^^^^ memory access failed: ALLOC has size 1, so pointer at offset -2048 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 `main` at $DIR/maybe_null_pointer_write_zst.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -1,5 +0,0 @@
#[allow(deref_nullptr)]
fn main() {
let x: () = unsafe { *std::ptr::null() }; //~ ERROR: memory access failed: null pointer is a dangling pointer
panic!("this should never print: {:?}", x);
}

View file

@ -1,15 +0,0 @@
error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
--> $DIR/null_pointer_deref_zst.rs:LL:CC
|
LL | let x: () = unsafe { *std::ptr::null() };
| ^^^^^^^^^^^^^^^^^ memory access failed: null pointer 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 `main` at $DIR/null_pointer_deref_zst.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -1,8 +0,0 @@
#[allow(deref_nullptr)]
fn main() {
// Not using the () type here, as writes of that type do not even have MIR generated.
// Also not assigning directly as that's array initialization, not assignment.
let zst_val = [1u8; 0];
unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) };
//~^ERROR: memory access failed: null pointer is a dangling pointer
}

View file

@ -1,15 +0,0 @@
error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
--> $DIR/null_pointer_write_zst.rs:LL:CC
|
LL | unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer 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 `main` at $DIR/null_pointer_write_zst.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -1,15 +0,0 @@
#![feature(intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
}
fn main() {
let mut data = [0u16; 4];
let ptr = &mut data[0] as *mut u16;
// Even copying 0 elements from NULL should error.
unsafe {
copy_nonoverlapping(std::ptr::null(), ptr, 0); //~ ERROR: memory access failed: null pointer is a dangling pointer
}
}

View file

@ -1,15 +0,0 @@
error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
--> $DIR/copy_null.rs:LL:CC
|
LL | copy_nonoverlapping(std::ptr::null(), ptr, 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer 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 `main` at $DIR/copy_null.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -1,9 +0,0 @@
//@compile-flags: -Zmiri-permissive-provenance
#[rustfmt::skip] // fails with "left behind trailing whitespace"
fn main() {
let x = 0 as *mut i32;
let _x = x.wrapping_offset(8); // ok, this has no inbounds tag
let _x = unsafe { x.offset(0) }; // UB despite offset 0, NULL is never inbounds
//~^ERROR: null pointer is a dangling pointer
}

View file

@ -1,15 +0,0 @@
error: Undefined Behavior: out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance)
--> $DIR/ptr_offset_0_plus_0.rs:LL:CC
|
LL | let _x = unsafe { x.offset(0) }; // UB despite offset 0, NULL is never inbounds
| ^^^^^^^^^^^ out-of-bounds pointer arithmetic: null pointer 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 `main` at $DIR/ptr_offset_0_plus_0.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -1,7 +0,0 @@
fn main() {
let start_ptr = &4 as *const _ as *const u8;
let length = 10;
let end_ptr = start_ptr.wrapping_add(length);
// Even if the offset is 0, a dangling OOB pointer is not allowed.
unsafe { end_ptr.offset_from(end_ptr) }; //~ERROR: pointer at offset 10 is out-of-bounds
}

View file

@ -1,15 +0,0 @@
error: Undefined Behavior: out-of-bounds `offset_from`: ALLOC has size 4, so pointer at offset 10 is out-of-bounds
--> $DIR/ptr_offset_from_oob.rs:LL:CC
|
LL | unsafe { end_ptr.offset_from(end_ptr) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC has size 4, so pointer at offset 10 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 `main` at $DIR/ptr_offset_from_oob.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -1,7 +0,0 @@
#[rustfmt::skip] // fails with "left behind trailing whitespace"
fn main() {
let x = Box::into_raw(Box::new(0u32));
let x = x.wrapping_offset(8); // ok, this has no inbounds tag
let _x = unsafe { x.offset(0) }; // UB despite offset 0, the pointer is not inbounds of the only object it can point to
//~^ERROR: pointer at offset 32 is out-of-bounds
}

View file

@ -1,20 +0,0 @@
error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer at offset 32 is out-of-bounds
--> $DIR/ptr_offset_ptr_plus_0.rs:LL:CC
|
LL | let _x = unsafe { x.offset(0) }; // UB despite offset 0, the pointer is not inbounds of the only object it can point to
| ^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer at offset 32 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
help: ALLOC was allocated here:
--> $DIR/ptr_offset_ptr_plus_0.rs:LL:CC
|
LL | let x = Box::into_raw(Box::new(0u32));
| ^^^^^^^^^^^^^^
= note: BACKTRACE (of the first span):
= note: inside `main` at $DIR/ptr_offset_ptr_plus_0.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -1,10 +0,0 @@
#![feature(intrinsics)]
// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
}
fn main() {
unsafe { write_bytes::<u8>(std::ptr::null_mut(), 0, 0) }; //~ ERROR: memory access failed: null pointer is a dangling pointer
}

View file

@ -1,15 +0,0 @@
error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
--> $DIR/write_bytes_null.rs:LL:CC
|
LL | unsafe { write_bytes::<u8>(std::ptr::null_mut(), 0, 0) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer 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 `main` at $DIR/write_bytes_null.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -1,12 +0,0 @@
fn main() {
// Not using the () type here, as writes of that type do not even have MIR generated.
// Also not assigning directly as that's array initialization, not assignment.
let zst_val = [1u8; 0];
// make sure ZST accesses are checked against being "truly" dangling pointers
// (into deallocated allocations).
let mut x_box = Box::new(1u8);
let x = &mut *x_box as *mut _ as *mut [u8; 0];
drop(x_box);
unsafe { *x = zst_val }; //~ ERROR: has been freed
}

View file

@ -1,25 +0,0 @@
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
--> $DIR/zst2.rs:LL:CC
|
LL | unsafe { *x = zst_val };
| ^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
= 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
help: ALLOC was allocated here:
--> $DIR/zst2.rs:LL:CC
|
LL | let mut x_box = Box::new(1u8);
| ^^^^^^^^^^^^^
help: ALLOC was deallocated here:
--> $DIR/zst2.rs:LL:CC
|
LL | drop(x_box);
| ^^^^^^^^^^^
= note: BACKTRACE (of the first span):
= note: inside `main` at $DIR/zst2.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -1,15 +0,0 @@
fn main() {
// Not using the () type here, as writes of that type do not even have MIR generated.
// Also not assigning directly as that's array initialization, not assignment.
let zst_val = [1u8; 0];
// make sure ZST accesses are checked against being "truly" dangling pointers
// (that are out-of-bounds).
let mut x_box = Box::new(1u8);
let x = (&mut *x_box as *mut u8).wrapping_offset(1);
// This one is just "at the edge", but still okay
unsafe { *(x as *mut [u8; 0]) = zst_val };
// One byte further is OOB.
let x = x.wrapping_offset(1);
unsafe { *(x as *mut [u8; 0]) = zst_val }; //~ ERROR: out-of-bounds
}

View file

@ -1,20 +0,0 @@
error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset 2 is out-of-bounds
--> $DIR/zst3.rs:LL:CC
|
LL | unsafe { *(x as *mut [u8; 0]) = zst_val };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has size 1, so pointer at offset 2 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
help: ALLOC was allocated here:
--> $DIR/zst3.rs:LL:CC
|
LL | let mut x_box = Box::new(1u8);
| ^^^^^^^^^^^^^
= note: BACKTRACE (of the first span):
= note: inside `main` at $DIR/zst3.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -1,5 +1,5 @@
error: Undefined Behavior: memory access failed: ALLOC has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds
--> $DIR/zst1.rs:LL:CC
--> $DIR/zst_local_oob.rs:LL:CC
|
LL | let _val = unsafe { *x };
| ^^ memory access failed: ALLOC has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds
@ -7,7 +7,7 @@ LL | let _val = unsafe { *x };
= 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/zst1.rs:LL:CC
= note: inside `main` at $DIR/zst_local_oob.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View file

@ -118,10 +118,9 @@ fn vtable() {
let parts: (*const (), *const u8) = unsafe { mem::transmute(ptr) };
let vtable = parts.1;
let offset = vtable.align_offset(mem::align_of::<TWOPTR>());
let _vtable_aligned = vtable.wrapping_add(offset) as *const [TWOPTR; 0];
// FIXME: we can't actually do the access since vtable pointers act like zero-sized allocations.
// Enable the next line once https://github.com/rust-lang/rust/issues/117945 is implemented.
//let _place = unsafe { &*vtable_aligned };
let vtable_aligned = vtable.wrapping_add(offset) as *const [TWOPTR; 0];
// Zero-sized deref, so no in-bounds requirement.
let _place = unsafe { &*vtable_aligned };
}
fn main() {

View file

@ -0,0 +1,59 @@
//! Tests specific for <https://github.com/rust-lang/rust/issues/117945>: zero-sized operations.
#![feature(strict_provenance)]
use std::ptr;
fn main() {
// Null.
test_ptr(ptr::null_mut::<()>());
// No provenance.
test_ptr(ptr::without_provenance_mut::<()>(1));
// Out-of-bounds.
let mut b = Box::new(0i32);
let ptr = ptr::addr_of_mut!(*b) as *mut ();
test_ptr(ptr.wrapping_byte_add(2));
// Dangling (use-after-free).
drop(b);
test_ptr(ptr);
}
fn test_ptr(ptr: *mut ()) {
unsafe {
// Reads and writes.
let mut val = *ptr;
*ptr = val;
ptr.read();
ptr.write(());
// Memory access intrinsics.
// - memcpy (1st and 2nd argument)
ptr.copy_from_nonoverlapping(&(), 1);
ptr.copy_to_nonoverlapping(&mut val, 1);
// - memmove (1st and 2nd argument)
ptr.copy_from(&(), 1);
ptr.copy_to(&mut val, 1);
// - memset
ptr.write_bytes(0u8, 1);
// Offset.
let _ = ptr.offset(0);
let _ = ptr.offset(1); // this is still 0 bytes
// Distance.
let ptr = ptr.cast::<i32>();
ptr.offset_from(ptr);
/*
FIXME: this is disabled for now as these cases are not yet allowed.
// Distance from other "bad" pointers that have the same address, but different provenance. Some
// of this is library UB, but we don't want it to be language UB since that would violate
// provenance monotonicity: if we allow computing the distance between two ptrs with no
// provenance, we have to allow computing it between two ptrs with arbitrary provenance.
// - Distance from "no provenance"
ptr.offset_from(ptr::without_provenance_mut(ptr.addr()));
// - Distance from out-of-bounds pointer
let mut b = Box::new(0i32);
let other_ptr = ptr::addr_of_mut!(*b);
ptr.offset_from(other_ptr.with_addr(ptr.addr()));
// - Distance from use-after-free pointer
drop(b);
ptr.offset_from(other_ptr.with_addr(ptr.addr()));
*/
}
}

View file

@ -12,7 +12,7 @@
slice::{from_ptr_range, from_raw_parts},
};
// Null is never valid for reads
// Null is never valid for references
pub static S0: &[u32] = unsafe { from_raw_parts(ptr::null(), 0) };
//~^ ERROR: it is undefined behavior to use this value
pub static S1: &[()] = unsafe { from_raw_parts(ptr::null(), 0) };
@ -46,10 +46,11 @@
};
pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
//~^ ERROR it is undefined behavior to use this value
pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore
pub static R2: &[u32] = unsafe {
let ptr = &D0 as *const u32;
from_ptr_range(ptr..ptr.add(2))
from_ptr_range(ptr..ptr.add(2)) // errors inside libcore
};
pub static R4: &[u8] = unsafe {
//~^ ERROR: it is undefined behavior to use this value

View file

@ -88,20 +88,16 @@ LL | pub static S8: &[u64] = unsafe {
HEX_DUMP
}
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
= note: out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance)
|
note: inside `std::ptr::const_ptr::<impl *const u32>::sub_ptr`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
note: inside `from_ptr_range::<'_, u32>`
--> $SRC_DIR/core/src/slice/raw.rs:LL:COL
note: inside `R0`
--> $DIR/forbidden_slices.rs:48:34
error[E0080]: it is undefined behavior to use this value
--> $DIR/forbidden_slices.rs:48:1
|
LL | pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null reference
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
HEX_DUMP
}
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@ -113,9 +109,9 @@ note: inside `std::ptr::const_ptr::<impl *const ()>::sub_ptr`
note: inside `from_ptr_range::<'_, ()>`
--> $SRC_DIR/core/src/slice/raw.rs:LL:COL
note: inside `R1`
--> $DIR/forbidden_slices.rs:49:33
--> $DIR/forbidden_slices.rs:50:33
|
LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
@ -127,13 +123,13 @@ error[E0080]: could not evaluate static initializer
note: inside `std::ptr::const_ptr::<impl *const u32>::add`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
note: inside `R2`
--> $DIR/forbidden_slices.rs:52:25
--> $DIR/forbidden_slices.rs:53:25
|
LL | from_ptr_range(ptr..ptr.add(2))
LL | from_ptr_range(ptr..ptr.add(2)) // errors inside libcore
| ^^^^^^^^^^
error[E0080]: it is undefined behavior to use this value
--> $DIR/forbidden_slices.rs:54:1
--> $DIR/forbidden_slices.rs:55:1
|
LL | pub static R4: &[u8] = unsafe {
| ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>[0]: encountered uninitialized memory, but expected an integer
@ -144,7 +140,7 @@ LL | pub static R4: &[u8] = unsafe {
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/forbidden_slices.rs:59:1
--> $DIR/forbidden_slices.rs:60:1
|
LL | pub static R5: &[u8] = unsafe {
| ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>[0]: encountered a pointer, but expected an integer
@ -157,7 +153,7 @@ LL | pub static R5: &[u8] = unsafe {
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value
--> $DIR/forbidden_slices.rs:64:1
--> $DIR/forbidden_slices.rs:65:1
|
LL | pub static R6: &[bool] = unsafe {
| ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>[0]: encountered 0x11, but expected a boolean
@ -168,7 +164,7 @@ LL | pub static R6: &[bool] = unsafe {
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/forbidden_slices.rs:69:1
--> $DIR/forbidden_slices.rs:70:1
|
LL | pub static R7: &[u16] = unsafe {
| ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1)
@ -186,7 +182,7 @@ error[E0080]: could not evaluate static initializer
note: inside `std::ptr::const_ptr::<impl *const u64>::add`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
note: inside `R8`
--> $DIR/forbidden_slices.rs:76:25
--> $DIR/forbidden_slices.rs:77:25
|
LL | from_ptr_range(ptr..ptr.add(1))
| ^^^^^^^^^^
@ -201,7 +197,7 @@ note: inside `std::ptr::const_ptr::<impl *const u32>::sub_ptr`
note: inside `from_ptr_range::<'_, u32>`
--> $SRC_DIR/core/src/slice/raw.rs:LL:COL
note: inside `R9`
--> $DIR/forbidden_slices.rs:81:34
--> $DIR/forbidden_slices.rs:82:34
|
LL | pub static R9: &[u32] = unsafe { from_ptr_range(&D0..(&D0 as *const u32).add(1)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -216,7 +212,7 @@ note: inside `std::ptr::const_ptr::<impl *const u32>::sub_ptr`
note: inside `from_ptr_range::<'_, u32>`
--> $SRC_DIR/core/src/slice/raw.rs:LL:COL
note: inside `R10`
--> $DIR/forbidden_slices.rs:82:35
--> $DIR/forbidden_slices.rs:83:35
|
LL | pub static R10: &[u32] = unsafe { from_ptr_range(&D0..&D0) };
| ^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -7,11 +7,11 @@
fn main() {
const LHS_NULL: i32 = unsafe {
compare_bytes(0 as *const u8, 2 as *const u8, 0)
compare_bytes(0 as *const u8, 2 as *const u8, 1)
//~^ ERROR evaluation of constant value failed
};
const RHS_NULL: i32 = unsafe {
compare_bytes(1 as *const u8, 0 as *const u8, 0)
compare_bytes(1 as *const u8, 0 as *const u8, 1)
//~^ ERROR evaluation of constant value failed
};
const DANGLING_PTR_NON_ZERO_LENGTH: i32 = unsafe {

View file

@ -1,14 +1,14 @@
error[E0080]: evaluation of constant value failed
--> $DIR/const-compare-bytes-ub.rs:10:9
|
LL | compare_bytes(0 as *const u8, 2 as *const u8, 0)
LL | compare_bytes(0 as *const u8, 2 as *const u8, 1)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
error[E0080]: evaluation of constant value failed
--> $DIR/const-compare-bytes-ub.rs:14:9
|
LL | compare_bytes(1 as *const u8, 0 as *const u8, 0)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
LL | compare_bytes(1 as *const u8, 0 as *const u8, 1)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: 0x1[noalloc] is a dangling pointer (it has no provenance)
error[E0080]: evaluation of constant value failed
--> $DIR/const-compare-bytes-ub.rs:18:9

View file

@ -23,16 +23,20 @@
const COPY_OOB_1: () = unsafe {
let mut x = 0i32;
let dangle = (&mut x as *mut i32).wrapping_add(10);
// Even if the first ptr is an int ptr and this is a ZST copy, we should detect dangling 2nd ptrs.
copy_nonoverlapping(0x100 as *const i32, dangle, 0); //~ ERROR evaluation of constant value failed [E0080]
//~| pointer at offset 40 is out-of-bounds
// Zero-sized copy is fine.
copy_nonoverlapping(0x100 as *const i32, dangle, 0);
// Non-zero-sized copy is not.
copy_nonoverlapping(0x100 as *const i32, dangle, 1); //~ ERROR evaluation of constant value failed [E0080]
//~| 0x100[noalloc] is a dangling pointer
};
const COPY_OOB_2: () = unsafe {
let x = 0i32;
let dangle = (&x as *const i32).wrapping_add(10);
// Even if the second ptr is an int ptr and this is a ZST copy, we should detect dangling 1st ptrs.
copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); //~ ERROR evaluation of constant value failed [E0080]
//~| pointer at offset 40 is out-of-bounds
// Zero-sized copy is fine.
copy_nonoverlapping(dangle, 0x100 as *mut i32, 0);
// Non-zero-sized copy is not.
copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); //~ ERROR evaluation of constant value failed [E0080]
//~| offset 40 is out-of-bounds
};
const COPY_SIZE_OVERFLOW: () = unsafe {

View file

@ -1,23 +1,23 @@
error[E0080]: evaluation of constant value failed
--> $DIR/copy-intrinsic.rs:27:5
--> $DIR/copy-intrinsic.rs:29:5
|
LL | copy_nonoverlapping(0x100 as *const i32, dangle, 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC0 has size 4, so pointer at offset 40 is out-of-bounds
LL | copy_nonoverlapping(0x100 as *const i32, dangle, 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: 0x100[noalloc] is a dangling pointer (it has no provenance)
error[E0080]: evaluation of constant value failed
--> $DIR/copy-intrinsic.rs:34:5
--> $DIR/copy-intrinsic.rs:38:5
|
LL | copy_nonoverlapping(dangle, 0x100 as *mut i32, 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC1 has size 4, so pointer at offset 40 is out-of-bounds
LL | copy_nonoverlapping(dangle, 0x100 as *mut i32, 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC0 has size 4, so pointer to 4 bytes starting at offset 40 is out-of-bounds
error[E0080]: evaluation of constant value failed
--> $DIR/copy-intrinsic.rs:41:5
--> $DIR/copy-intrinsic.rs:45:5
|
LL | copy(&x, &mut y, 1usize << (mem::size_of::<usize>() * 8 - 1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy`
error[E0080]: evaluation of constant value failed
--> $DIR/copy-intrinsic.rs:47:5
--> $DIR/copy-intrinsic.rs:51:5
|
LL | copy_nonoverlapping(&x, &mut y, 1usize << (mem::size_of::<usize>() * 8 - 1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy_nonoverlapping`

View file

@ -10,7 +10,7 @@ union Foo<'a> {
}
const FOO: &() = {
//~^ ERROR it is undefined behavior to use this value
//~^ ERROR encountered dangling pointer
let y = ();
unsafe { Foo { y: &y }.long_live_the_unit }
};

View file

@ -1,14 +1,8 @@
error[E0080]: it is undefined behavior to use this value
error: encountered dangling pointer in final value of constant
--> $DIR/dangling-alloc-id-ice.rs:12:1
|
LL | const FOO: &() = {
| ^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (use-after-free)
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
HEX_DUMP
}
| ^^^^^^^^^^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0080`.

View file

@ -34,8 +34,8 @@ struct Struct {
pub const OFFSET_FROM_NULL: isize = {
let ptr = 0 as *const u8;
unsafe { ptr_offset_from(ptr, ptr) } //~ERROR evaluation of constant value failed
//~| null pointer is a dangling pointer
// Null isn't special for zero-sized "accesses" (i.e., the range between the two pointers)
unsafe { ptr_offset_from(ptr, ptr) }
};
pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC
@ -67,8 +67,8 @@ struct Struct {
let start_ptr = &4 as *const _ as *const u8;
let length = 10;
let end_ptr = (start_ptr).wrapping_add(length);
unsafe { ptr_offset_from(end_ptr, end_ptr) } //~ERROR evaluation of constant value failed
//~| pointer at offset 10 is out-of-bounds
// Out-of-bounds is fine as long as the range between the pointers is empty.
unsafe { ptr_offset_from(end_ptr, end_ptr) }
};
pub const DIFFERENT_ALLOC_UNSIGNED: usize = {

View file

@ -23,12 +23,6 @@ error[E0080]: evaluation of constant value failed
LL | unsafe { ptr_offset_from(field_ptr, base_ptr as *const u16) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ exact_div: 1_isize cannot be divided by 2_isize without remainder
error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:37:14
|
LL | unsafe { ptr_offset_from(ptr, ptr) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance)
error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:44:14
|
@ -47,12 +41,6 @@ error[E0080]: evaluation of constant value failed
LL | unsafe { ptr_offset_from(start_ptr, end_ptr) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC1 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds
error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:70:14
|
LL | unsafe { ptr_offset_from(end_ptr, end_ptr) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC2 has size 4, so pointer at offset 10 is out-of-bounds
error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:79:14
|
@ -109,6 +97,6 @@ note: inside `OFFSET_VERY_FAR2`
LL | unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 15 previous errors
error: aborting due to 13 previous errors
For more information about this error, try `rustc --explain E0080`.

View file

@ -17,10 +17,10 @@
pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; //~NOTE
pub const DANGLING: *const u8 = unsafe { ptr::NonNull::<u8>::dangling().as_ptr().offset(4) }; //~NOTE
// Right now, a zero offset from null is UB
pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::<u8>().offset(0) }; //~NOTE
// Make sure that we don't panic when computing abs(offset*size_of::<T>())
pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; //~NOTE
// Offset-by-zero is allowed.
pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::<u8>().offset(0) };
fn main() {}

View file

@ -128,19 +128,6 @@ note: inside `DANGLING`
LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::<u8>::dangling().as_ptr().offset(4) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
= note: out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance)
|
note: inside `std::ptr::const_ptr::<impl *const u8>::offset`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
note: inside `NULL_OFFSET_ZERO`
--> $DIR/offset_ub.rs:21:50
|
LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::<u8>().offset(0) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
@ -149,11 +136,11 @@ error[E0080]: evaluation of constant value failed
note: inside `std::ptr::const_ptr::<impl *const u8>::offset`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
note: inside `UNDERFLOW_ABS`
--> $DIR/offset_ub.rs:24:47
--> $DIR/offset_ub.rs:21:47
|
LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 12 previous errors
error: aborting due to 11 previous errors
For more information about this error, try `rustc --explain E0080`.