1
Fork 0

Auto merge of #87224 - RalfJung:miri-ptr-oob, r=oli-obk

miri: better ptr-out-of-bounds errors

For offsets larger than `isize::MAX`, display them as negative offsets.

r? `@oli-obk`
This commit is contained in:
bors 2021-07-20 08:15:15 +00:00
commit 718d53b0cb
5 changed files with 65 additions and 28 deletions

View file

@ -240,12 +240,13 @@ pub enum UndefinedBehaviorInfo<'tcx> {
/// Dereferencing a dangling pointer after it got freed.
PointerUseAfterFree(AllocId),
/// Used a pointer outside the bounds it is valid for.
/// (If `ptr_size > 0`, determines the size of the memory range that was expected to be in-bounds.)
PointerOutOfBounds {
alloc_id: AllocId,
offset: Size,
size: Size,
alloc_size: Size,
ptr_offset: i64,
ptr_size: Size,
msg: CheckInAllocMsg,
allocation_size: Size,
},
/// Using an integer as a pointer in the wrong way.
DanglingIntPointer(u64, CheckInAllocMsg),
@ -318,24 +319,25 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
PointerUseAfterFree(a) => {
write!(f, "pointer to {} was dereferenced after this allocation got freed", a)
}
PointerOutOfBounds { alloc_id, offset, size: Size::ZERO, msg, allocation_size } => {
PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size: Size::ZERO, msg } => {
write!(
f,
"{}{} has size {}, so pointer at offset {} is out-of-bounds",
"{}{alloc_id} has size {alloc_size}, so pointer at offset {ptr_offset} is out-of-bounds",
msg,
alloc_id,
allocation_size.bytes(),
offset.bytes(),
alloc_id = alloc_id,
alloc_size = alloc_size.bytes(),
ptr_offset = ptr_offset,
)
}
PointerOutOfBounds { alloc_id, offset, size, msg, allocation_size } => write!(
PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => write!(
f,
"{}{} has size {}, so pointer to {} bytes starting at offset {} is out-of-bounds",
"{}{alloc_id} has size {alloc_size}, so pointer to {ptr_size} byte{ptr_size_p} starting at offset {ptr_offset} is out-of-bounds",
msg,
alloc_id,
allocation_size.bytes(),
size.bytes(),
offset.bytes(),
alloc_id = alloc_id,
alloc_size = alloc_size.bytes(),
ptr_size = ptr_size.bytes(),
ptr_size_p = pluralize!(ptr_size.bytes()),
ptr_offset = ptr_offset,
),
DanglingIntPointer(0, CheckInAllocMsg::InboundsTest) => {
write!(f, "null pointer is not a valid pointer for this operation")

View file

@ -36,6 +36,20 @@ pub trait PointerArithmetic: HasDataLayout {
i64::try_from(max_isize_plus_1 - 1).unwrap()
}
#[inline]
fn machine_usize_to_isize(&self, val: u64) -> i64 {
let val = val as i64;
// Now clamp into the machine_isize range.
if val > self.machine_isize_max() {
// This can only happen the the ptr size is < 64, so we know max_usize_plus_1 fits into
// i64.
let max_usize_plus_1 = 1u128 << self.pointer_size().bits();
val - i64::try_from(max_usize_plus_1).unwrap()
} else {
val
}
}
/// Helper function: truncate given value-"overflowed flag" pair to pointer size and
/// update "overflowed flag" if there was an overflow.
/// This should be called by all the other methods before returning!

View file

@ -372,7 +372,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
)
}
/// Check if the given pointerpoints to live memory of given `size` and `align`
/// Check if the given pointer points to live memory of given `size` and `align`
/// (ignoring `M::enforce_alignment`). The caller can control the error message for the
/// out-of-bounds case.
#[inline(always)]
@ -451,11 +451,17 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
None
}
Ok((alloc_id, offset, ptr)) => {
let (allocation_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, ptr)?;
let (alloc_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, ptr)?;
// Test bounds. This also ensures non-null.
// 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 > allocation_size) {
throw_ub!(PointerOutOfBounds { alloc_id, offset, size, allocation_size, msg })
if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) {
throw_ub!(PointerOutOfBounds {
alloc_id,
alloc_size,
ptr_offset: self.machine_usize_to_isize(offset.bytes()),
ptr_size: size,
msg,
})
}
// Test align. Check this last; if both bounds and alignment are violated
// we want the error to be about the bounds.