diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 9006e4cfaf7bb..2d97fecf8a766 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1314,6 +1314,7 @@ extern "rust-intrinsic" { /// The stabilized version of this intrinsic is /// [`std::pointer::offset`](../../std/primitive.pointer.html#method.offset). #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] pub fn offset(dst: *const T, offset: isize) -> *const T; /// Calculates the offset from a pointer, potentially wrapping. @@ -1331,6 +1332,7 @@ extern "rust-intrinsic" { /// The stabilized version of this intrinsic is /// [`std::pointer::wrapping_offset`](../../std/primitive.pointer.html#method.wrapping_offset). #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] pub fn arith_offset(dst: *const T, offset: isize) -> *const T; /// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index ca13433caec8d..7d21f9a9a66d0 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -85,6 +85,7 @@ #![feature(const_panic)] #![feature(const_fn_union)] #![feature(const_generics)] +#![feature(const_ptr_offset)] #![feature(const_ptr_offset_from)] #![feature(const_result)] #![feature(const_slice_from_raw_parts)] diff --git a/src/libcore/ptr/const_ptr.rs b/src/libcore/ptr/const_ptr.rs index 85ba5fc0638ea..835183d171a79 100644 --- a/src/libcore/ptr/const_ptr.rs +++ b/src/libcore/ptr/const_ptr.rs @@ -151,8 +151,9 @@ impl *const T { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn offset(self, count: isize) -> *const T + pub const unsafe fn offset(self, count: isize) -> *const T where T: Sized, { @@ -210,8 +211,9 @@ impl *const T { /// ``` #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_offset(self, count: isize) -> *const T + pub const fn wrapping_offset(self, count: isize) -> *const T where T: Sized, { @@ -393,8 +395,9 @@ impl *const T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn add(self, count: usize) -> Self + pub const unsafe fn add(self, count: usize) -> Self where T: Sized, { @@ -455,8 +458,9 @@ impl *const T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn sub(self, count: usize) -> Self + pub const unsafe fn sub(self, count: usize) -> Self where T: Sized, { @@ -511,8 +515,9 @@ impl *const T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_add(self, count: usize) -> Self + pub const fn wrapping_add(self, count: usize) -> Self where T: Sized, { @@ -567,8 +572,9 @@ impl *const T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_sub(self, count: usize) -> Self + pub const fn wrapping_sub(self, count: usize) -> Self where T: Sized, { diff --git a/src/libcore/ptr/mut_ptr.rs b/src/libcore/ptr/mut_ptr.rs index 0781d7e6cac45..40b5e4e22340e 100644 --- a/src/libcore/ptr/mut_ptr.rs +++ b/src/libcore/ptr/mut_ptr.rs @@ -145,8 +145,9 @@ impl *mut T { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn offset(self, count: isize) -> *mut T + pub const unsafe fn offset(self, count: isize) -> *mut T where T: Sized, { @@ -203,8 +204,9 @@ impl *mut T { /// ``` #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_offset(self, count: isize) -> *mut T + pub const fn wrapping_offset(self, count: isize) -> *mut T where T: Sized, { @@ -439,8 +441,9 @@ impl *mut T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn add(self, count: usize) -> Self + pub const unsafe fn add(self, count: usize) -> Self where T: Sized, { @@ -501,8 +504,9 @@ impl *mut T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn sub(self, count: usize) -> Self + pub const unsafe fn sub(self, count: usize) -> Self where T: Sized, { @@ -557,8 +561,9 @@ impl *mut T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_add(self, count: usize) -> Self + pub const fn wrapping_add(self, count: usize) -> Self where T: Sized, { @@ -613,8 +618,9 @@ impl *mut T { /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_sub(self, count: usize) -> Self + pub const fn wrapping_sub(self, count: usize) -> Self where T: Sized, { diff --git a/src/librustc_middle/mir/interpret/mod.rs b/src/librustc_middle/mir/interpret/mod.rs index d9e52af89007c..061bc9750e1c2 100644 --- a/src/librustc_middle/mir/interpret/mod.rs +++ b/src/librustc_middle/mir/interpret/mod.rs @@ -598,3 +598,12 @@ pub fn truncate(value: u128, size: Size) -> u128 { // Truncate (shift left to drop out leftover values, shift right to fill with zeroes). (value << shift) >> shift } + +/// Computes the unsigned absolute value without wrapping or panicking. +#[inline] +pub fn uabs(value: i64) -> u64 { + // The only tricky part here is if value == i64::MIN. In that case, + // wrapping_abs() returns i64::MIN == -2^63. Casting this value to a u64 + // gives 2^63, the correct value. + value.wrapping_abs() as u64 +} diff --git a/src/librustc_middle/mir/interpret/pointer.rs b/src/librustc_middle/mir/interpret/pointer.rs index 70cc546199b79..ccad4f0a135a1 100644 --- a/src/librustc_middle/mir/interpret/pointer.rs +++ b/src/librustc_middle/mir/interpret/pointer.rs @@ -1,4 +1,4 @@ -use super::{AllocId, InterpResult}; +use super::{uabs, AllocId, InterpResult}; use rustc_macros::HashStable; use rustc_target::abi::{HasDataLayout, Size}; @@ -24,6 +24,12 @@ pub trait PointerArithmetic: HasDataLayout { u64::try_from(max_usize_plus_1 - 1).unwrap() } + #[inline] + fn machine_isize_min(&self) -> i64 { + let max_isize_plus_1 = 1i128 << (self.pointer_size().bits() - 1); + i64::try_from(-max_isize_plus_1).unwrap() + } + #[inline] fn machine_isize_max(&self) -> i64 { let max_isize_plus_1 = 1u128 << (self.pointer_size().bits() - 1); @@ -42,21 +48,23 @@ pub trait PointerArithmetic: HasDataLayout { #[inline] fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) { + // We do not need to check if i fits in a machine usize. If it doesn't, + // either the wrapping_add will wrap or res will not fit in a pointer. let res = val.overflowing_add(i); self.truncate_to_ptr(res) } #[inline] fn overflowing_signed_offset(&self, val: u64, i: i64) -> (u64, bool) { - if i < 0 { - // Trickery to ensure that `i64::MIN` works fine: compute `n = -i`. - // This formula only works for true negative values; it overflows for zero! - let n = u64::MAX - (i as u64) + 1; - let res = val.overflowing_sub(n); - self.truncate_to_ptr(res) + // We need to make sure that i fits in a machine isize. + let n = uabs(i); + if i >= 0 { + let (val, over) = self.overflowing_offset(val, n); + (val, over || i > self.machine_isize_max()) } else { - // `i >= 0`, so the cast is safe. - self.overflowing_offset(val, i as u64) + let res = val.overflowing_sub(n); + let (val, over) = self.truncate_to_ptr(res); + (val, over || i < self.machine_isize_min()) } } diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index fc4be82ad90ad..55f254f573261 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -2,19 +2,21 @@ //! looking at their MIR. Intrinsics/functions supported here are shared by CTFE //! and miri. +use std::convert::TryFrom; + use rustc_hir::def_id::DefId; use rustc_middle::mir::{ self, - interpret::{ConstValue, GlobalId, InterpResult, Scalar}, + interpret::{uabs, ConstValue, GlobalId, InterpResult, Scalar}, BinOp, }; use rustc_middle::ty; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::{Abi, LayoutOf as _, Primitive, Size}; -use super::{ImmTy, InterpCx, Machine, OpTy, PlaceTy}; +use super::{CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy}; mod caller_location; mod type_name; @@ -279,7 +281,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let result = Scalar::from_uint(truncated_bits, layout.size); self.write_scalar(result, dest)?; } + sym::offset => { + let ptr = self.read_scalar(args[0])?.not_undef()?; + let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?; + let pointee_ty = substs.type_at(0); + + let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?; + self.write_scalar(offset_ptr, dest)?; + } + sym::arith_offset => { + let ptr = self.read_scalar(args[0])?.not_undef()?; + let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?; + let pointee_ty = substs.type_at(0); + let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); + let offset_bytes = offset_count.wrapping_mul(pointee_size); + let offset_ptr = ptr.ptr_wrapping_signed_offset(offset_bytes, self); + self.write_scalar(offset_ptr, dest)?; + } sym::ptr_offset_from => { let a = self.read_immediate(args[0])?.to_scalar()?; let b = self.read_immediate(args[1])?.to_scalar()?; @@ -409,4 +428,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // `Rem` says this is all right, so we can let `Div` do its job. self.binop_ignore_overflow(BinOp::Div, a, b, dest) } + + /// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its + /// allocation. For integer pointers, we consider each of them their own tiny allocation of size + /// 0, so offset-by-0 (and only 0) is okay -- except that NULL cannot be offset by _any_ value. + pub fn ptr_offset_inbounds( + &self, + ptr: Scalar, + pointee_ty: Ty<'tcx>, + offset_count: i64, + ) -> InterpResult<'tcx, Scalar> { + // We cannot overflow i64 as a type's size must be <= isize::MAX. + let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); + // The computed offset, in bytes, cannot overflow an isize. + let offset_bytes = + offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?; + // The offset being in bounds cannot rely on "wrapping around" the address space. + // So, first rule out overflows in the pointer arithmetic. + let offset_ptr = ptr.ptr_signed_offset(offset_bytes, self)?; + // ptr and offset_ptr must be in bounds of the same allocated object. This means all of the + // memory between these pointers must be accessible. Note that we do not require the + // pointers to be properly aligned (unlike a read/write operation). + let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr }; + let size: u64 = uabs(offset_bytes); + // This call handles checking for integer/NULL pointers. + self.memory.check_ptr_access_align( + min_ptr, + Size::from_bytes(size), + None, + CheckInAllocMsg::InboundsTest, + )?; + Ok(offset_ptr) + } } diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index 6a6098710e828..9b055beb99dfd 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -147,6 +147,7 @@ symbols! { Arc, Arguments, ArgumentV1, + arith_offset, arm_target_feature, asm, assert, @@ -516,6 +517,7 @@ symbols! { not, note, object_safe_for_dispatch, + offset, Ok, omit_gdb_pretty_printer_section, on, diff --git a/src/test/ui/consts/miri_unleashed/ptr_arith.rs b/src/test/ui/consts/miri_unleashed/ptr_arith.rs index 81985f9f625a5..65fc49c0b27a6 100644 --- a/src/test/ui/consts/miri_unleashed/ptr_arith.rs +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.rs @@ -2,8 +2,7 @@ #![feature(core_intrinsics)] #![allow(const_err)] -// A test demonstrating that we prevent doing even trivial -// pointer arithmetic or comparison during CTFE. +// During CTFE, we prevent pointer comparison and pointer-to-int casts. static CMP: () = { let x = &0 as *const _; @@ -19,11 +18,4 @@ static INT_PTR_ARITH: () = unsafe { //~| NOTE pointer-to-integer cast }; -static PTR_ARITH: () = unsafe { - let x = &0 as *const _; - let _v = core::intrinsics::offset(x, 0); - //~^ ERROR could not evaluate static initializer - //~| NOTE calling intrinsic `offset` -}; - fn main() {} diff --git a/src/test/ui/consts/miri_unleashed/ptr_arith.stderr b/src/test/ui/consts/miri_unleashed/ptr_arith.stderr index 5bd534a16b863..805ba9c6b0307 100644 --- a/src/test/ui/consts/miri_unleashed/ptr_arith.stderr +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.stderr @@ -1,39 +1,28 @@ error[E0080]: could not evaluate static initializer - --> $DIR/ptr_arith.rs:10:14 + --> $DIR/ptr_arith.rs:9:14 | LL | let _v = x == x; | ^^^^^^ "pointer arithmetic or comparison" needs an rfc before being allowed inside constants error[E0080]: could not evaluate static initializer - --> $DIR/ptr_arith.rs:17:14 + --> $DIR/ptr_arith.rs:16:14 | LL | let _v = x + 0; | ^^^^^ "pointer-to-integer cast" needs an rfc before being allowed inside constants -error[E0080]: could not evaluate static initializer - --> $DIR/ptr_arith.rs:24:14 - | -LL | let _v = core::intrinsics::offset(x, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "calling intrinsic `offset`" needs an rfc before being allowed inside constants - warning: skipping const checks | help: skipping check for `const_compare_raw_pointers` feature - --> $DIR/ptr_arith.rs:10:14 + --> $DIR/ptr_arith.rs:9:14 | LL | let _v = x == x; | ^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/ptr_arith.rs:16:20 + --> $DIR/ptr_arith.rs:15:20 | LL | let x: usize = std::mem::transmute(&0); | ^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check that does not even have a feature gate - --> $DIR/ptr_arith.rs:24:14 - | -LL | let _v = core::intrinsics::offset(x, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/offset.rs b/src/test/ui/consts/offset.rs new file mode 100644 index 0000000000000..f64242d568e31 --- /dev/null +++ b/src/test/ui/consts/offset.rs @@ -0,0 +1,115 @@ +// run-pass +#![feature(const_ptr_offset)] +#![feature(const_ptr_offset_from)] +#![feature(ptr_offset_from)] +use std::ptr; + +#[repr(C)] +struct Struct { + a: u32, + b: u32, + c: u32, +} +static S: Struct = Struct { a: 0, b: 0, c: 0 }; + +// For these tests we use offset_from to check that two pointers are equal. +// Rust doesn't currently support comparing pointers in const fn. + +static OFFSET_NO_CHANGE: bool = unsafe { + let p1 = &S.b as *const u32; + let p2 = p1.offset(2).offset(-2); + p1.offset_from(p2) == 0 +}; +static OFFSET_MIDDLE: bool = unsafe { + let p1 = (&S.a as *const u32).offset(1); + let p2 = (&S.c as *const u32).offset(-1); + p1.offset_from(p2) == 0 +}; +// Pointing to the end of the allocation is OK +static OFFSET_END: bool = unsafe { + let p1 = (&S.a as *const u32).offset(3); + let p2 = (&S.c as *const u32).offset(1); + p1.offset_from(p2) == 0 +}; +// Casting though a differently sized type is OK +static OFFSET_U8_PTR: bool = unsafe { + let p1 = (&S.a as *const u32 as *const u8).offset(5); + let p2 = (&S.c as *const u32 as *const u8).offset(-3); + p1.offset_from(p2) == 0 +}; +// Any offset with a ZST does nothing +const OFFSET_ZST: bool = unsafe { + let pz = &() as *const (); + // offset_from can't work with ZSTs, so cast to u8 ptr + let p1 = pz.offset(5) as *const u8; + let p2 = pz.offset(isize::MIN) as *const u8; + p1.offset_from(p2) == 0 +}; +const OFFSET_ZERO: bool = unsafe { + let p = [0u8; 0].as_ptr(); + p.offset(0).offset_from(p) == 0 +}; +const OFFSET_ONE: bool = unsafe { + let p = &42u32 as *const u32; + p.offset(1).offset_from(p) == 1 +}; +const OFFSET_DANGLING: bool = unsafe { + let p = ptr::NonNull::::dangling().as_ptr(); + p.offset(0).offset_from(p) == 0 +}; +const OFFSET_UNALIGNED: bool = unsafe { + let arr = [0u8; 32]; + let p1 = arr.as_ptr(); + let p2 = (p1.offset(2) as *const u32).offset(1); + (p2 as *const u8).offset_from(p1) == 6 +}; + +const WRAP_OFFSET_NO_CHANGE: bool = unsafe { + let p1 = &42u32 as *const u32; + let p2 = p1.wrapping_offset(1000).wrapping_offset(-1000); + let p3 = p1.wrapping_offset(-1000).wrapping_offset(1000); + (p1.offset_from(p2) == 0) & (p1.offset_from(p3) == 0) +}; +const WRAP_ADDRESS_SPACE: bool = unsafe { + let p1 = &42u8 as *const u8; + let p2 = p1.wrapping_offset(isize::MIN).wrapping_offset(isize::MIN); + p1.offset_from(p2) == 0 +}; +// Wrap on the count*size_of::() calculation. +const WRAP_SIZE_OF: bool = unsafe { + // Make sure that if p1 moves backwards, we are still in range + let arr = [0u32; 2]; + let p = &arr[1] as *const u32; + // With wrapping arithmetic, isize::MAX * 4 == -4 + let wrapped = p.wrapping_offset(isize::MAX); + let backward = p.wrapping_offset(-1); + wrapped.offset_from(backward) == 0 +}; +const WRAP_INTEGER_POINTER: bool = unsafe { + let p1 = (0x42 as *const u32).wrapping_offset(4); + let p2 = 0x52 as *const u32; + p1.offset_from(p2) == 0 +}; +const WRAP_NULL: bool = unsafe { + let p1 = ptr::null::().wrapping_offset(1); + let p2 = 0x4 as *const u32; + p1.offset_from(p2) == 0 +}; + +fn main() { + assert!(OFFSET_NO_CHANGE); + assert!(OFFSET_MIDDLE); + assert!(OFFSET_END); + assert!(OFFSET_U8_PTR); + assert!(OFFSET_ZST); + assert!(OFFSET_ZERO); + assert!(OFFSET_ONE); + assert!(OFFSET_DANGLING); + assert!(OFFSET_UNALIGNED); + + assert!(WRAP_OFFSET_NO_CHANGE); + assert!(WRAP_ADDRESS_SPACE); + assert!(WRAP_SIZE_OF); + assert!(WRAP_INTEGER_POINTER); + assert!(WRAP_NULL); +} diff --git a/src/test/ui/consts/offset_ub.rs b/src/test/ui/consts/offset_ub.rs new file mode 100644 index 0000000000000..4f943ed9ad194 --- /dev/null +++ b/src/test/ui/consts/offset_ub.rs @@ -0,0 +1,25 @@ +// ignore-tidy-linelength +#![feature(const_ptr_offset)] +use std::ptr; + +// normalize-stderr-test "alloc\d+" -> "allocN" + +pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) }; //~NOTE +pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) }; //~NOTE +pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) }; //~NOTE + +pub const OVERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MAX) }; //~NOTE +pub const UNDERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MIN) }; //~NOTE +pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *const u8).offset(2) }; //~NOTE +pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) }; //~NOTE + +pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; //~NOTE +pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::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::().offset(0) }; //~NOTE + +// Make sure that we don't panic when computing abs(offset*size_of::()) +pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; //~NOTE + +fn main() {} diff --git a/src/test/ui/consts/offset_ub.stderr b/src/test/ui/consts/offset_ub.stderr new file mode 100644 index 0000000000000..0ab81cc0c5b31 --- /dev/null +++ b/src/test/ui/consts/offset_ub.stderr @@ -0,0 +1,169 @@ +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | overflowing in-bounds pointer arithmetic + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `BEFORE_START` at $DIR/offset_ub.rs:7:46 + | + ::: $DIR/offset_ub.rs:7:1 + | +LL | pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) }; + | ------------------------------------------------------------------------------ + | + = note: `#[deny(const_err)]` on by default + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds test failed: pointer must be in-bounds at offset 2, but is outside bounds of allocN which has size 1 + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `AFTER_END` at $DIR/offset_ub.rs:8:43 + | + ::: $DIR/offset_ub.rs:8:1 + | +LL | pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) }; + | -------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds test failed: pointer must be in-bounds at offset 101, but is outside bounds of allocN which has size 100 + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `AFTER_ARRAY` at $DIR/offset_ub.rs:9:45 + | + ::: $DIR/offset_ub.rs:9:1 + | +LL | pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) }; + | ------------------------------------------------------------------------------ + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | overflowing in-bounds pointer arithmetic + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `OVERFLOW` at $DIR/offset_ub.rs:11:43 + | + ::: $DIR/offset_ub.rs:11:1 + | +LL | pub const OVERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MAX) }; + | ---------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | overflowing in-bounds pointer arithmetic + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `UNDERFLOW` at $DIR/offset_ub.rs:12:44 + | + ::: $DIR/offset_ub.rs:12:1 + | +LL | pub const UNDERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MIN) }; + | ----------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | overflowing in-bounds pointer arithmetic + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `OVERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:13:56 + | + ::: $DIR/offset_ub.rs:13:1 + | +LL | pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *const u8).offset(2) }; + | --------------------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | overflowing in-bounds pointer arithmetic + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `UNDERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:14:57 + | + ::: $DIR/offset_ub.rs:14:1 + | +LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) }; + | -------------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds test failed: pointer must be in-bounds at offset 1, but is outside bounds of allocN which has size 0 + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `ZERO_SIZED_ALLOC` at $DIR/offset_ub.rs:16:50 + | + ::: $DIR/offset_ub.rs:16:1 + | +LL | pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; + | ------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/mut_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) as *mut T + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | unable to turn bytes into a pointer + | inside `std::ptr::mut_ptr::::offset` at $SRC_DIR/libcore/ptr/mut_ptr.rs:LL:COL + | inside `DANGLING` at $DIR/offset_ub.rs:17:42 + | + ::: $DIR/offset_ub.rs:17:1 + | +LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr().offset(4) }; + | --------------------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds test failed: 0x0 is not a valid pointer + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `NULL_OFFSET_ZERO` at $DIR/offset_ub.rs:20:50 + | + ::: $DIR/offset_ub.rs:20:1 + | +LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; + | ------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | unable to turn bytes into a pointer + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `UNDERFLOW_ABS` at $DIR/offset_ub.rs:23:47 + | + ::: $DIR/offset_ub.rs:23:1 + | +LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; + | --------------------------------------------------------------------------------------------- + +error: aborting due to 11 previous errors +