Skip to content

Commit 718d53b

Browse files
committed
Auto merge of rust-lang#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`
2 parents a72c360 + bed3b96 commit 718d53b

File tree

5 files changed

+65
-28
lines changed

5 files changed

+65
-28
lines changed

compiler/rustc_middle/src/mir/interpret/error.rs

+16-14
Original file line numberDiff line numberDiff line change
@@ -240,12 +240,13 @@ pub enum UndefinedBehaviorInfo<'tcx> {
240240
/// Dereferencing a dangling pointer after it got freed.
241241
PointerUseAfterFree(AllocId),
242242
/// Used a pointer outside the bounds it is valid for.
243+
/// (If `ptr_size > 0`, determines the size of the memory range that was expected to be in-bounds.)
243244
PointerOutOfBounds {
244245
alloc_id: AllocId,
245-
offset: Size,
246-
size: Size,
246+
alloc_size: Size,
247+
ptr_offset: i64,
248+
ptr_size: Size,
247249
msg: CheckInAllocMsg,
248-
allocation_size: Size,
249250
},
250251
/// Using an integer as a pointer in the wrong way.
251252
DanglingIntPointer(u64, CheckInAllocMsg),
@@ -318,24 +319,25 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
318319
PointerUseAfterFree(a) => {
319320
write!(f, "pointer to {} was dereferenced after this allocation got freed", a)
320321
}
321-
PointerOutOfBounds { alloc_id, offset, size: Size::ZERO, msg, allocation_size } => {
322+
PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size: Size::ZERO, msg } => {
322323
write!(
323324
f,
324-
"{}{} has size {}, so pointer at offset {} is out-of-bounds",
325+
"{}{alloc_id} has size {alloc_size}, so pointer at offset {ptr_offset} is out-of-bounds",
325326
msg,
326-
alloc_id,
327-
allocation_size.bytes(),
328-
offset.bytes(),
327+
alloc_id = alloc_id,
328+
alloc_size = alloc_size.bytes(),
329+
ptr_offset = ptr_offset,
329330
)
330331
}
331-
PointerOutOfBounds { alloc_id, offset, size, msg, allocation_size } => write!(
332+
PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => write!(
332333
f,
333-
"{}{} has size {}, so pointer to {} bytes starting at offset {} is out-of-bounds",
334+
"{}{alloc_id} has size {alloc_size}, so pointer to {ptr_size} byte{ptr_size_p} starting at offset {ptr_offset} is out-of-bounds",
334335
msg,
335-
alloc_id,
336-
allocation_size.bytes(),
337-
size.bytes(),
338-
offset.bytes(),
336+
alloc_id = alloc_id,
337+
alloc_size = alloc_size.bytes(),
338+
ptr_size = ptr_size.bytes(),
339+
ptr_size_p = pluralize!(ptr_size.bytes()),
340+
ptr_offset = ptr_offset,
339341
),
340342
DanglingIntPointer(0, CheckInAllocMsg::InboundsTest) => {
341343
write!(f, "null pointer is not a valid pointer for this operation")

compiler/rustc_middle/src/mir/interpret/pointer.rs

+14
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,20 @@ pub trait PointerArithmetic: HasDataLayout {
3636
i64::try_from(max_isize_plus_1 - 1).unwrap()
3737
}
3838

39+
#[inline]
40+
fn machine_usize_to_isize(&self, val: u64) -> i64 {
41+
let val = val as i64;
42+
// Now clamp into the machine_isize range.
43+
if val > self.machine_isize_max() {
44+
// This can only happen the the ptr size is < 64, so we know max_usize_plus_1 fits into
45+
// i64.
46+
let max_usize_plus_1 = 1u128 << self.pointer_size().bits();
47+
val - i64::try_from(max_usize_plus_1).unwrap()
48+
} else {
49+
val
50+
}
51+
}
52+
3953
/// Helper function: truncate given value-"overflowed flag" pair to pointer size and
4054
/// update "overflowed flag" if there was an overflow.
4155
/// This should be called by all the other methods before returning!

compiler/rustc_mir/src/interpret/memory.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
372372
)
373373
}
374374

375-
/// Check if the given pointerpoints to live memory of given `size` and `align`
375+
/// Check if the given pointer points to live memory of given `size` and `align`
376376
/// (ignoring `M::enforce_alignment`). The caller can control the error message for the
377377
/// out-of-bounds case.
378378
#[inline(always)]
@@ -451,11 +451,17 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
451451
None
452452
}
453453
Ok((alloc_id, offset, ptr)) => {
454-
let (allocation_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, ptr)?;
454+
let (alloc_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, ptr)?;
455455
// Test bounds. This also ensures non-null.
456456
// It is sufficient to check this for the end pointer. Also check for overflow!
457-
if offset.checked_add(size, &self.tcx).map_or(true, |end| end > allocation_size) {
458-
throw_ub!(PointerOutOfBounds { alloc_id, offset, size, allocation_size, msg })
457+
if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) {
458+
throw_ub!(PointerOutOfBounds {
459+
alloc_id,
460+
alloc_size,
461+
ptr_offset: self.machine_usize_to_isize(offset.bytes()),
462+
ptr_size: size,
463+
msg,
464+
})
459465
}
460466
// Test align. Check this last; if both bounds and alignment are violated
461467
// we want the error to be about the bounds.

src/test/ui/consts/offset_ub.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub const OVERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MAX)
1313
pub const UNDERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MIN) }; //~NOTE
1414
pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *const u8).offset(2) }; //~NOTE
1515
pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) }; //~NOTE
16+
pub const NEGATIVE_OFFSET: *const u8 = unsafe { [0u8; 1].as_ptr().wrapping_offset(-2).offset(-2) }; //~NOTE
1617

1718
pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; //~NOTE
1819
pub const DANGLING: *const u8 = unsafe { ptr::NonNull::<u8>::dangling().as_ptr().offset(4) }; //~NOTE

src/test/ui/consts/offset_ub.stderr

+24-10
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,27 @@ error[E0080]: evaluation of constant value failed
102102
LL | unsafe { intrinsics::offset(self, count) }
103103
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
104104
| |
105-
| pointer arithmetic failed: allocN has size 0, so pointer to 1 bytes starting at offset 0 is out-of-bounds
105+
| pointer arithmetic failed: allocN has size 1, so pointer to 2 bytes starting at offset -4 is out-of-bounds
106106
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
107107
|
108-
::: $DIR/offset_ub.rs:17:50
108+
::: $DIR/offset_ub.rs:16:49
109+
|
110+
LL | pub const NEGATIVE_OFFSET: *const u8 = unsafe { [0u8; 1].as_ptr().wrapping_offset(-2).offset(-2) };
111+
| ------------------------------------------------ inside `NEGATIVE_OFFSET` at $DIR/offset_ub.rs:16:49
112+
113+
error[E0080]: evaluation of constant value failed
114+
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
115+
|
116+
LL | unsafe { intrinsics::offset(self, count) }
117+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
118+
| |
119+
| pointer arithmetic failed: allocN has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds
120+
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
121+
|
122+
::: $DIR/offset_ub.rs:18:50
109123
|
110124
LL | pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) };
111-
| --------------------------- inside `ZERO_SIZED_ALLOC` at $DIR/offset_ub.rs:17:50
125+
| --------------------------- inside `ZERO_SIZED_ALLOC` at $DIR/offset_ub.rs:18:50
112126

113127
error[E0080]: evaluation of constant value failed
114128
--> $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL
@@ -119,10 +133,10 @@ LL | unsafe { intrinsics::offset(self, count) as *mut T }
119133
| 0x1 is not a valid pointer
120134
| inside `ptr::mut_ptr::<impl *mut u8>::offset` at $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL
121135
|
122-
::: $DIR/offset_ub.rs:18:42
136+
::: $DIR/offset_ub.rs:19:42
123137
|
124138
LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::<u8>::dangling().as_ptr().offset(4) };
125-
| ------------------------------------------------- inside `DANGLING` at $DIR/offset_ub.rs:18:42
139+
| ------------------------------------------------- inside `DANGLING` at $DIR/offset_ub.rs:19:42
126140

127141
error[E0080]: evaluation of constant value failed
128142
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -133,10 +147,10 @@ LL | unsafe { intrinsics::offset(self, count) }
133147
| pointer arithmetic failed: 0x0 is not a valid pointer
134148
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
135149
|
136-
::: $DIR/offset_ub.rs:21:50
150+
::: $DIR/offset_ub.rs:22:50
137151
|
138152
LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::<u8>().offset(0) };
139-
| --------------------------- inside `NULL_OFFSET_ZERO` at $DIR/offset_ub.rs:21:50
153+
| --------------------------- inside `NULL_OFFSET_ZERO` at $DIR/offset_ub.rs:22:50
140154

141155
error[E0080]: evaluation of constant value failed
142156
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -147,11 +161,11 @@ LL | unsafe { intrinsics::offset(self, count) }
147161
| 0x7f..f is not a valid pointer
148162
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
149163
|
150-
::: $DIR/offset_ub.rs:24:47
164+
::: $DIR/offset_ub.rs:25:47
151165
|
152166
LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) };
153-
| -------------------------------------------- inside `UNDERFLOW_ABS` at $DIR/offset_ub.rs:24:47
167+
| -------------------------------------------- inside `UNDERFLOW_ABS` at $DIR/offset_ub.rs:25:47
154168

155-
error: aborting due to 11 previous errors
169+
error: aborting due to 12 previous errors
156170

157171
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)