const-eval: disable pointer fragment support
This commit is contained in:
parent
ebdf2abea4
commit
aed0ed4c93
10 changed files with 119 additions and 9 deletions
|
|
@ -1501,8 +1501,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// `get_bytes_mut` will clear the provenance, which is correct,
|
||||
// since we don't want to keep any provenance at the target.
|
||||
// This will also error if copying partial provenance is not supported.
|
||||
let provenance =
|
||||
src_alloc.provenance().prepare_copy(src_range, dest_offset, num_copies, self);
|
||||
let provenance = src_alloc
|
||||
.provenance()
|
||||
.prepare_copy(src_range, dest_offset, num_copies, self)
|
||||
.map_err(|e| e.to_interp_error(src_alloc_id))?;
|
||||
// Prepare a copy of the initialization mask.
|
||||
let init = src_alloc.init_mask().prepare_copy(src_range);
|
||||
|
||||
|
|
|
|||
|
|
@ -724,6 +724,11 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
|
|||
}
|
||||
// If we get here, we have to check per-byte provenance, and join them together.
|
||||
let prov = 'prov: {
|
||||
if !Prov::OFFSET_IS_ADDR {
|
||||
// FIXME(#146291): We need to ensure that we don't mix different pointers with
|
||||
// the same provenance.
|
||||
return Err(AllocError::ReadPartialPointer(range.start));
|
||||
}
|
||||
// Initialize with first fragment. Must have index 0.
|
||||
let Some((mut joint_prov, 0)) = self.provenance.get_byte(range.start, cx) else {
|
||||
break 'prov None;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
|
|||
use tracing::trace;
|
||||
|
||||
use super::{AllocRange, CtfeProvenance, Provenance, alloc_range};
|
||||
use crate::mir::interpret::{AllocError, AllocResult};
|
||||
|
||||
/// Stores the provenance information of pointers stored in memory.
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
|
|
@ -137,6 +138,11 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
|
|||
let Some(bytes) = self.bytes.as_deref_mut() else {
|
||||
return true;
|
||||
};
|
||||
if !Prov::OFFSET_IS_ADDR {
|
||||
// FIXME(#146291): We need to ensure that we don't mix different pointers with
|
||||
// the same provenance.
|
||||
return false;
|
||||
}
|
||||
let ptr_size = cx.data_layout().pointer_size();
|
||||
while let Some((offset, (prov, _))) = bytes.iter().next().copied() {
|
||||
// Check if this fragment starts a pointer.
|
||||
|
|
@ -285,7 +291,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
|
|||
dest: Size,
|
||||
count: u64,
|
||||
cx: &impl HasDataLayout,
|
||||
) -> ProvenanceCopy<Prov> {
|
||||
) -> AllocResult<ProvenanceCopy<Prov>> {
|
||||
let shift_offset = move |idx, offset| {
|
||||
// compute offset for current repetition
|
||||
let dest_offset = dest + src.size * idx; // `Size` operations
|
||||
|
|
@ -363,6 +369,12 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
|
|||
}
|
||||
trace!("byte provenances: {bytes:?}");
|
||||
|
||||
if !bytes.is_empty() && !Prov::OFFSET_IS_ADDR {
|
||||
// FIXME(#146291): We need to ensure that we don't mix different pointers with
|
||||
// the same provenance.
|
||||
return Err(AllocError::ReadPartialPointer(src.start));
|
||||
}
|
||||
|
||||
// And again a buffer for the new list on the target side.
|
||||
let mut dest_bytes = Vec::with_capacity(bytes.len() * (count as usize));
|
||||
for i in 0..count {
|
||||
|
|
@ -373,7 +385,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
|
|||
dest_bytes_box = Some(dest_bytes.into_boxed_slice());
|
||||
}
|
||||
|
||||
ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box }
|
||||
Ok(ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box })
|
||||
}
|
||||
|
||||
/// Applies a provenance copy.
|
||||
|
|
|
|||
|
|
@ -1348,6 +1348,40 @@ pub const unsafe fn swap<T>(x: *mut T, y: *mut T) {
|
|||
/// assert_eq!(x, [7, 8, 3, 4]);
|
||||
/// assert_eq!(y, [1, 2, 9]);
|
||||
/// ```
|
||||
///
|
||||
/// # Const evaluation limitations
|
||||
///
|
||||
/// If this function is invoked during const-evaluation, the current implementation has a small (and
|
||||
/// rarely relevant) limitation: if `count` is at least 2 and the data pointed to by `x` or `y`
|
||||
/// contains a pointer that crosses the boundary of two `T`-sized chunks of memory, the function may
|
||||
/// fail to evaluate (similar to a panic during const-evaluation). This behavior may change in the
|
||||
/// future.
|
||||
///
|
||||
/// The limitation is illustrated by the following example:
|
||||
///
|
||||
/// ```
|
||||
/// use std::mem::size_of;
|
||||
/// use std::ptr;
|
||||
///
|
||||
/// const { unsafe {
|
||||
/// const PTR_SIZE: usize = size_of::<*const i32>();
|
||||
/// let mut data1 = [0u8; PTR_SIZE];
|
||||
/// let mut data2 = [0u8; PTR_SIZE];
|
||||
/// // Store a pointer in `data1`.
|
||||
/// data1.as_mut_ptr().cast::<*const i32>().write_unaligned(&42);
|
||||
/// // Swap the contents of `data1` and `data2` by swapping `PTR_SIZE` many `u8`-sized chunks.
|
||||
/// // This call will fail, because the pointer in `data1` crosses the boundary
|
||||
/// // between several of the 1-byte chunks that are being swapped here.
|
||||
/// //ptr::swap_nonoverlapping(data1.as_mut_ptr(), data2.as_mut_ptr(), PTR_SIZE);
|
||||
/// // Swap the contents of `data1` and `data2` by swapping a single chunk of size
|
||||
/// // `[u8; PTR_SIZE]`. That works, as there is no pointer crossing the boundary between
|
||||
/// // two chunks.
|
||||
/// ptr::swap_nonoverlapping(&mut data1, &mut data2, 1);
|
||||
/// // Read the pointer from `data2` and dereference it.
|
||||
/// let ptr = data2.as_ptr().cast::<*const i32>().read_unaligned();
|
||||
/// assert!(*ptr == 42);
|
||||
/// } }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "swap_nonoverlapping", since = "1.27.0")]
|
||||
#[rustc_const_stable(feature = "const_swap_nonoverlapping", since = "1.88.0")]
|
||||
|
|
@ -1376,7 +1410,9 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
|
|||
const_eval_select!(
|
||||
@capture[T] { x: *mut T, y: *mut T, count: usize }:
|
||||
if const {
|
||||
// At compile-time we don't need all the special code below.
|
||||
// At compile-time we want to always copy this in chunks of `T`, to ensure that if there
|
||||
// are pointers inside `T` we will copy them in one go rather than trying to copy a part
|
||||
// of a pointer (which would not work).
|
||||
// SAFETY: Same preconditions as this function
|
||||
unsafe { swap_nonoverlapping_const(x, y, count) }
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -936,12 +936,13 @@ fn test_const_swap_ptr() {
|
|||
assert!(*s1.0.ptr == 666);
|
||||
assert!(*s2.0.ptr == 1);
|
||||
|
||||
// Swap them back, byte-for-byte
|
||||
// Swap them back, again as an array.
|
||||
// FIXME(#146291): we should be swapping back at type `u8` but that currently does not work.
|
||||
unsafe {
|
||||
ptr::swap_nonoverlapping(
|
||||
ptr::from_mut(&mut s1).cast::<u8>(),
|
||||
ptr::from_mut(&mut s2).cast::<u8>(),
|
||||
size_of::<A>(),
|
||||
ptr::from_mut(&mut s1).cast::<T>(),
|
||||
ptr::from_mut(&mut s2).cast::<T>(),
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//! Test that various operations involving pointer fragments work as expected.
|
||||
//@ run-pass
|
||||
//@ ignore-test: disabled due to <https://github.com/rust-lang/rust/issues/146291>
|
||||
|
||||
use std::mem::{self, MaybeUninit, transmute};
|
||||
use std::ptr;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
//! Test that we properly error when there is a pointer fragment in the final value.
|
||||
//@ ignore-test: disabled due to <https://github.com/rust-lang/rust/issues/146291>
|
||||
|
||||
use std::{mem::{self, MaybeUninit}, ptr};
|
||||
|
||||
|
|
|
|||
28
tests/ui/consts/const-eval/ptr_fragments_mixed.rs
Normal file
28
tests/ui/consts/const-eval/ptr_fragments_mixed.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
//! This mixes fragments from different pointers to the same allocarion, in a way
|
||||
//! that we should not accept. See <https://github.com/rust-lang/rust/issues/146291>.
|
||||
static A: u8 = 123;
|
||||
|
||||
const HALF_PTR: usize = std::mem::size_of::<*const ()>() / 2;
|
||||
|
||||
const fn mix_ptr() -> *const u8 {
|
||||
unsafe {
|
||||
let x: *const u8 = &raw const A;
|
||||
let mut y = x.wrapping_add(usize::MAX / 4);
|
||||
core::ptr::copy_nonoverlapping(
|
||||
(&raw const x).cast::<u8>(),
|
||||
(&raw mut y).cast::<u8>(),
|
||||
HALF_PTR,
|
||||
);
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
const APTR: *const u8 = mix_ptr(); //~ERROR: unable to read parts of a pointer
|
||||
|
||||
fn main() {
|
||||
let a = APTR;
|
||||
println!("{a:p}");
|
||||
let b = mix_ptr();
|
||||
println!("{b:p}");
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
23
tests/ui/consts/const-eval/ptr_fragments_mixed.stderr
Normal file
23
tests/ui/consts/const-eval/ptr_fragments_mixed.stderr
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
error[E0080]: unable to read parts of a pointer from memory at ALLOC0
|
||||
--> $DIR/ptr_fragments_mixed.rs:20:25
|
||||
|
|
||||
LL | const APTR: *const u8 = mix_ptr();
|
||||
| ^^^^^^^^^ evaluation of `APTR` failed inside this call
|
||||
|
|
||||
= help: this code performed an operation that depends on the underlying bytes representing a pointer
|
||||
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
|
||||
note: inside `mix_ptr`
|
||||
--> $DIR/ptr_fragments_mixed.rs:11:9
|
||||
|
|
||||
LL | / core::ptr::copy_nonoverlapping(
|
||||
LL | | (&raw const x).cast::<u8>(),
|
||||
LL | | (&raw mut y).cast::<u8>(),
|
||||
LL | | HALF_PTR,
|
||||
LL | | );
|
||||
| |_________^
|
||||
note: inside `std::ptr::copy_nonoverlapping::<u8>`
|
||||
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
//! Ensure we error when trying to load from a pointer whose provenance has been messed with.
|
||||
//@ ignore-test: disabled due to <https://github.com/rust-lang/rust/issues/146291>
|
||||
|
||||
const PARTIAL_OVERWRITE: () = {
|
||||
let mut p = &42;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue