adjust offset_from logic: check that both pointers are in-bounds
This commit is contained in:
parent
458262b131
commit
63ed8e41ce
6 changed files with 100 additions and 46 deletions
|
@ -307,53 +307,57 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
self.write_pointer(offset_ptr, dest)?;
|
self.write_pointer(offset_ptr, dest)?;
|
||||||
}
|
}
|
||||||
sym::ptr_offset_from => {
|
sym::ptr_offset_from => {
|
||||||
let a = self.read_immediate(&args[0])?.to_scalar()?;
|
let a = self.read_pointer(&args[0])?;
|
||||||
let b = self.read_immediate(&args[1])?.to_scalar()?;
|
let b = self.read_pointer(&args[1])?;
|
||||||
|
|
||||||
// Special case: if both scalars are *equal integers*
|
// Special case: if both scalars are *equal integers*
|
||||||
// and not null, we pretend there is an allocation of size 0 right there,
|
// and not null, we pretend there is an allocation of size 0 right there,
|
||||||
// and their offset is 0. (There's never a valid object at null, making it an
|
// and their offset is 0. (There's never a valid object at null, making it an
|
||||||
// exception from the exception.)
|
// exception from the exception.)
|
||||||
// This is the dual to the special exception for offset-by-0
|
// This is the dual to the special exception for offset-by-0
|
||||||
// in the inbounds pointer offset operation (see the Miri code, `src/operator.rs`).
|
// in the inbounds pointer offset operation (see `ptr_offset_inbounds` below).
|
||||||
//
|
match (self.memory.ptr_try_get_alloc(a), self.memory.ptr_try_get_alloc(b)) {
|
||||||
// Control flow is weird because we cannot early-return (to reach the
|
(Err(a), Err(b)) if a == b && a != 0 => {
|
||||||
// `go_to_block` at the end).
|
// Both are the same non-null integer.
|
||||||
let done = if let (Ok(a), Ok(b)) = (a.try_to_int(), b.try_to_int()) {
|
|
||||||
let a = a.try_to_machine_usize(*self.tcx).unwrap();
|
|
||||||
let b = b.try_to_machine_usize(*self.tcx).unwrap();
|
|
||||||
if a == b && a != 0 {
|
|
||||||
self.write_scalar(Scalar::from_machine_isize(0, self), dest)?;
|
self.write_scalar(Scalar::from_machine_isize(0, self), dest)?;
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
} else {
|
(Err(offset), _) | (_, Err(offset)) => {
|
||||||
false
|
throw_ub!(DanglingIntPointer(offset, CheckInAllocMsg::OffsetFromTest));
|
||||||
};
|
}
|
||||||
|
(Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _))) => {
|
||||||
|
// Both are pointers. They must be into the same allocation.
|
||||||
|
if a_alloc_id != b_alloc_id {
|
||||||
|
throw_ub_format!(
|
||||||
|
"ptr_offset_from cannot compute offset of pointers into different \
|
||||||
|
allocations.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// And they must both be valid for zero-sized accesses ("in-bounds or one past the end").
|
||||||
|
self.memory.check_ptr_access_align(
|
||||||
|
a,
|
||||||
|
Size::ZERO,
|
||||||
|
Align::ONE,
|
||||||
|
CheckInAllocMsg::OffsetFromTest,
|
||||||
|
)?;
|
||||||
|
self.memory.check_ptr_access_align(
|
||||||
|
b,
|
||||||
|
Size::ZERO,
|
||||||
|
Align::ONE,
|
||||||
|
CheckInAllocMsg::OffsetFromTest,
|
||||||
|
)?;
|
||||||
|
|
||||||
if !done {
|
// Compute offset.
|
||||||
// General case: we need two pointers.
|
let usize_layout = self.layout_of(self.tcx.types.usize)?;
|
||||||
let a = self.scalar_to_ptr(a);
|
let isize_layout = self.layout_of(self.tcx.types.isize)?;
|
||||||
let b = self.scalar_to_ptr(b);
|
let a_offset = ImmTy::from_uint(a_offset.bytes(), usize_layout);
|
||||||
let (a_alloc_id, a_offset, _) = self.memory.ptr_get_alloc(a)?;
|
let b_offset = ImmTy::from_uint(b_offset.bytes(), usize_layout);
|
||||||
let (b_alloc_id, b_offset, _) = self.memory.ptr_get_alloc(b)?;
|
let (val, _overflowed, _ty) =
|
||||||
if a_alloc_id != b_alloc_id {
|
self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?;
|
||||||
throw_ub_format!(
|
let pointee_layout = self.layout_of(substs.type_at(0))?;
|
||||||
"ptr_offset_from cannot compute offset of pointers into different \
|
let val = ImmTy::from_scalar(val, isize_layout);
|
||||||
allocations.",
|
let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout);
|
||||||
);
|
self.exact_div(&val, &size, dest)?;
|
||||||
}
|
}
|
||||||
let usize_layout = self.layout_of(self.tcx.types.usize)?;
|
|
||||||
let isize_layout = self.layout_of(self.tcx.types.isize)?;
|
|
||||||
let a_offset = ImmTy::from_uint(a_offset.bytes(), usize_layout);
|
|
||||||
let b_offset = ImmTy::from_uint(b_offset.bytes(), usize_layout);
|
|
||||||
let (val, _overflowed, _ty) =
|
|
||||||
self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?;
|
|
||||||
let pointee_layout = self.layout_of(substs.type_at(0))?;
|
|
||||||
let val = ImmTy::from_scalar(val, isize_layout);
|
|
||||||
let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout);
|
|
||||||
self.exact_div(&val, &size, dest)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -385,9 +385,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
|
||||||
CheckInAllocMsg::DerefTest | CheckInAllocMsg::MemoryAccessTest => {
|
CheckInAllocMsg::DerefTest | CheckInAllocMsg::MemoryAccessTest => {
|
||||||
AllocCheck::Dereferenceable
|
AllocCheck::Dereferenceable
|
||||||
}
|
}
|
||||||
CheckInAllocMsg::PointerArithmeticTest | CheckInAllocMsg::InboundsTest => {
|
CheckInAllocMsg::PointerArithmeticTest
|
||||||
AllocCheck::Live
|
| CheckInAllocMsg::OffsetFromTest
|
||||||
}
|
| CheckInAllocMsg::InboundsTest => AllocCheck::Live,
|
||||||
};
|
};
|
||||||
let (size, align) = self.get_size_and_align(alloc_id, check)?;
|
let (size, align) = self.get_size_and_align(alloc_id, check)?;
|
||||||
Ok((size, align, ()))
|
Ok((size, align, ()))
|
||||||
|
|
|
@ -184,6 +184,8 @@ pub enum CheckInAllocMsg {
|
||||||
MemoryAccessTest,
|
MemoryAccessTest,
|
||||||
/// We are doing pointer arithmetic.
|
/// We are doing pointer arithmetic.
|
||||||
PointerArithmeticTest,
|
PointerArithmeticTest,
|
||||||
|
/// We are doing pointer offset_from.
|
||||||
|
OffsetFromTest,
|
||||||
/// None of the above -- generic/unspecific inbounds test.
|
/// None of the above -- generic/unspecific inbounds test.
|
||||||
InboundsTest,
|
InboundsTest,
|
||||||
}
|
}
|
||||||
|
@ -199,6 +201,7 @@ impl fmt::Display for CheckInAllocMsg {
|
||||||
CheckInAllocMsg::DerefTest => "dereferencing pointer failed: ",
|
CheckInAllocMsg::DerefTest => "dereferencing pointer failed: ",
|
||||||
CheckInAllocMsg::MemoryAccessTest => "memory access failed: ",
|
CheckInAllocMsg::MemoryAccessTest => "memory access failed: ",
|
||||||
CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic failed: ",
|
CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic failed: ",
|
||||||
|
CheckInAllocMsg::OffsetFromTest => "out-of-bounds offset_from: ",
|
||||||
CheckInAllocMsg::InboundsTest => "",
|
CheckInAllocMsg::InboundsTest => "",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -358,6 +361,9 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
|
||||||
DanglingIntPointer(0, CheckInAllocMsg::InboundsTest) => {
|
DanglingIntPointer(0, CheckInAllocMsg::InboundsTest) => {
|
||||||
write!(f, "null pointer is not a valid pointer for this operation")
|
write!(f, "null pointer is not a valid pointer for this operation")
|
||||||
}
|
}
|
||||||
|
DanglingIntPointer(0, msg) => {
|
||||||
|
write!(f, "{}null pointer is not a valid pointer", msg)
|
||||||
|
}
|
||||||
DanglingIntPointer(i, msg) => {
|
DanglingIntPointer(i, msg) => {
|
||||||
write!(f, "{}0x{:x} is not a valid pointer", msg, i)
|
write!(f, "{}0x{:x} is not a valid pointer", msg, i)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#![feature(const_ptr_offset_from)]
|
#![feature(const_ptr_offset_from, const_ptr_offset)]
|
||||||
#![feature(core_intrinsics)]
|
#![feature(core_intrinsics)]
|
||||||
|
|
||||||
use std::intrinsics::ptr_offset_from;
|
use std::intrinsics::ptr_offset_from;
|
||||||
|
@ -44,4 +44,30 @@ pub const DIFFERENT_INT: isize = { // offset_from with two different integers: l
|
||||||
//~| 0x10 is not a valid pointer
|
//~| 0x10 is not a valid pointer
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const OUT_OF_BOUNDS_1: isize = {
|
||||||
|
let start_ptr = &4 as *const _ as *const u8;
|
||||||
|
let length = 10;
|
||||||
|
let end_ptr = (start_ptr).wrapping_add(length);
|
||||||
|
// First ptr is out of bounds
|
||||||
|
unsafe { ptr_offset_from(end_ptr, start_ptr) } //~ERROR evaluation of constant value failed
|
||||||
|
//~| pointer at offset 10 is out-of-bounds
|
||||||
|
};
|
||||||
|
|
||||||
|
const OUT_OF_BOUNDS_2: isize = {
|
||||||
|
let start_ptr = &4 as *const _ as *const u8;
|
||||||
|
let length = 10;
|
||||||
|
let end_ptr = (start_ptr).wrapping_add(length);
|
||||||
|
// Second ptr is out of bounds
|
||||||
|
unsafe { ptr_offset_from(start_ptr, end_ptr) } //~ERROR evaluation of constant value failed
|
||||||
|
//~| pointer at offset 10 is out-of-bounds
|
||||||
|
};
|
||||||
|
|
||||||
|
const OUT_OF_BOUNDS_SAME: isize = {
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -10,7 +10,7 @@ error[E0080]: evaluation of constant value failed
|
||||||
LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
|
LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
| |
|
| |
|
||||||
| 0x2a is not a valid pointer
|
| out-of-bounds offset_from: 0x2a is not a valid pointer
|
||||||
| inside `ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
| inside `ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
||||||
|
|
|
|
||||||
::: $DIR/offset_from_ub.rs:23:14
|
::: $DIR/offset_from_ub.rs:23:14
|
||||||
|
@ -28,14 +28,32 @@ error[E0080]: evaluation of constant value failed
|
||||||
--> $DIR/offset_from_ub.rs:36:14
|
--> $DIR/offset_from_ub.rs:36:14
|
||||||
|
|
|
|
||||||
LL | unsafe { ptr_offset_from(ptr, ptr) }
|
LL | unsafe { ptr_offset_from(ptr, ptr) }
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ null pointer is not a valid pointer for this operation
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: null pointer is not a valid pointer
|
||||||
|
|
||||||
error[E0080]: evaluation of constant value failed
|
error[E0080]: evaluation of constant value failed
|
||||||
--> $DIR/offset_from_ub.rs:43:14
|
--> $DIR/offset_from_ub.rs:43:14
|
||||||
|
|
|
|
||||||
LL | unsafe { ptr_offset_from(ptr2, ptr1) }
|
LL | unsafe { ptr_offset_from(ptr2, ptr1) }
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 0x10 is not a valid pointer
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: 0x10 is not a valid pointer
|
||||||
|
|
||||||
error: aborting due to 5 previous errors
|
error[E0080]: evaluation of constant value failed
|
||||||
|
--> $DIR/offset_from_ub.rs:52:14
|
||||||
|
|
|
||||||
|
LL | unsafe { ptr_offset_from(end_ptr, start_ptr) }
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc18 has size 4, so pointer at offset 10 is out-of-bounds
|
||||||
|
|
||||||
|
error[E0080]: evaluation of constant value failed
|
||||||
|
--> $DIR/offset_from_ub.rs:61:14
|
||||||
|
|
|
||||||
|
LL | unsafe { ptr_offset_from(start_ptr, end_ptr) }
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc21 has size 4, so pointer at offset 10 is out-of-bounds
|
||||||
|
|
||||||
|
error[E0080]: evaluation of constant value failed
|
||||||
|
--> $DIR/offset_from_ub.rs:69:14
|
||||||
|
|
|
||||||
|
LL | unsafe { ptr_offset_from(end_ptr, end_ptr) }
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc24 has size 4, so pointer at offset 10 is out-of-bounds
|
||||||
|
|
||||||
|
error: aborting due to 8 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0080`.
|
For more information about this error, try `rustc --explain E0080`.
|
||||||
|
|
|
@ -144,7 +144,7 @@ error[E0080]: evaluation of constant value failed
|
||||||
LL | unsafe { intrinsics::offset(self, count) }
|
LL | unsafe { intrinsics::offset(self, count) }
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
| |
|
| |
|
||||||
| pointer arithmetic failed: 0x0 is not a valid pointer
|
| pointer arithmetic failed: null pointer is not a valid pointer
|
||||||
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
||||||
|
|
|
|
||||||
::: $DIR/offset_ub.rs:22:50
|
::: $DIR/offset_ub.rs:22:50
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue