Skip to content

Commit 63ed8e4

Browse files
committed
adjust offset_from logic: check that both pointers are in-bounds
1 parent 458262b commit 63ed8e4

File tree

6 files changed

+101
-47
lines changed

6 files changed

+101
-47
lines changed

compiler/rustc_const_eval/src/interpret/intrinsics.rs

+42-38
Original file line numberDiff line numberDiff line change
@@ -307,53 +307,57 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
307307
self.write_pointer(offset_ptr, dest)?;
308308
}
309309
sym::ptr_offset_from => {
310-
let a = self.read_immediate(&args[0])?.to_scalar()?;
311-
let b = self.read_immediate(&args[1])?.to_scalar()?;
310+
let a = self.read_pointer(&args[0])?;
311+
let b = self.read_pointer(&args[1])?;
312312

313313
// Special case: if both scalars are *equal integers*
314314
// and not null, we pretend there is an allocation of size 0 right there,
315315
// and their offset is 0. (There's never a valid object at null, making it an
316316
// exception from the exception.)
317317
// This is the dual to the special exception for offset-by-0
318-
// in the inbounds pointer offset operation (see the Miri code, `src/operator.rs`).
319-
//
320-
// Control flow is weird because we cannot early-return (to reach the
321-
// `go_to_block` at the end).
322-
let done = if let (Ok(a), Ok(b)) = (a.try_to_int(), b.try_to_int()) {
323-
let a = a.try_to_machine_usize(*self.tcx).unwrap();
324-
let b = b.try_to_machine_usize(*self.tcx).unwrap();
325-
if a == b && a != 0 {
318+
// in the inbounds pointer offset operation (see `ptr_offset_inbounds` below).
319+
match (self.memory.ptr_try_get_alloc(a), self.memory.ptr_try_get_alloc(b)) {
320+
(Err(a), Err(b)) if a == b && a != 0 => {
321+
// Both are the same non-null integer.
326322
self.write_scalar(Scalar::from_machine_isize(0, self), dest)?;
327-
true
328-
} else {
329-
false
330323
}
331-
} else {
332-
false
333-
};
334-
335-
if !done {
336-
// General case: we need two pointers.
337-
let a = self.scalar_to_ptr(a);
338-
let b = self.scalar_to_ptr(b);
339-
let (a_alloc_id, a_offset, _) = self.memory.ptr_get_alloc(a)?;
340-
let (b_alloc_id, b_offset, _) = self.memory.ptr_get_alloc(b)?;
341-
if a_alloc_id != b_alloc_id {
342-
throw_ub_format!(
343-
"ptr_offset_from cannot compute offset of pointers into different \
344-
allocations.",
345-
);
324+
(Err(offset), _) | (_, Err(offset)) => {
325+
throw_ub!(DanglingIntPointer(offset, CheckInAllocMsg::OffsetFromTest));
326+
}
327+
(Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _))) => {
328+
// Both are pointers. They must be into the same allocation.
329+
if a_alloc_id != b_alloc_id {
330+
throw_ub_format!(
331+
"ptr_offset_from cannot compute offset of pointers into different \
332+
allocations.",
333+
);
334+
}
335+
// And they must both be valid for zero-sized accesses ("in-bounds or one past the end").
336+
self.memory.check_ptr_access_align(
337+
a,
338+
Size::ZERO,
339+
Align::ONE,
340+
CheckInAllocMsg::OffsetFromTest,
341+
)?;
342+
self.memory.check_ptr_access_align(
343+
b,
344+
Size::ZERO,
345+
Align::ONE,
346+
CheckInAllocMsg::OffsetFromTest,
347+
)?;
348+
349+
// Compute offset.
350+
let usize_layout = self.layout_of(self.tcx.types.usize)?;
351+
let isize_layout = self.layout_of(self.tcx.types.isize)?;
352+
let a_offset = ImmTy::from_uint(a_offset.bytes(), usize_layout);
353+
let b_offset = ImmTy::from_uint(b_offset.bytes(), usize_layout);
354+
let (val, _overflowed, _ty) =
355+
self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?;
356+
let pointee_layout = self.layout_of(substs.type_at(0))?;
357+
let val = ImmTy::from_scalar(val, isize_layout);
358+
let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout);
359+
self.exact_div(&val, &size, dest)?;
346360
}
347-
let usize_layout = self.layout_of(self.tcx.types.usize)?;
348-
let isize_layout = self.layout_of(self.tcx.types.isize)?;
349-
let a_offset = ImmTy::from_uint(a_offset.bytes(), usize_layout);
350-
let b_offset = ImmTy::from_uint(b_offset.bytes(), usize_layout);
351-
let (val, _overflowed, _ty) =
352-
self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?;
353-
let pointee_layout = self.layout_of(substs.type_at(0))?;
354-
let val = ImmTy::from_scalar(val, isize_layout);
355-
let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout);
356-
self.exact_div(&val, &size, dest)?;
357361
}
358362
}
359363

compiler/rustc_const_eval/src/interpret/memory.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -385,9 +385,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
385385
CheckInAllocMsg::DerefTest | CheckInAllocMsg::MemoryAccessTest => {
386386
AllocCheck::Dereferenceable
387387
}
388-
CheckInAllocMsg::PointerArithmeticTest | CheckInAllocMsg::InboundsTest => {
389-
AllocCheck::Live
390-
}
388+
CheckInAllocMsg::PointerArithmeticTest
389+
| CheckInAllocMsg::OffsetFromTest
390+
| CheckInAllocMsg::InboundsTest => AllocCheck::Live,
391391
};
392392
let (size, align) = self.get_size_and_align(alloc_id, check)?;
393393
Ok((size, align, ()))

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

+6
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ pub enum CheckInAllocMsg {
184184
MemoryAccessTest,
185185
/// We are doing pointer arithmetic.
186186
PointerArithmeticTest,
187+
/// We are doing pointer offset_from.
188+
OffsetFromTest,
187189
/// None of the above -- generic/unspecific inbounds test.
188190
InboundsTest,
189191
}
@@ -199,6 +201,7 @@ impl fmt::Display for CheckInAllocMsg {
199201
CheckInAllocMsg::DerefTest => "dereferencing pointer failed: ",
200202
CheckInAllocMsg::MemoryAccessTest => "memory access failed: ",
201203
CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic failed: ",
204+
CheckInAllocMsg::OffsetFromTest => "out-of-bounds offset_from: ",
202205
CheckInAllocMsg::InboundsTest => "",
203206
}
204207
)
@@ -358,6 +361,9 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
358361
DanglingIntPointer(0, CheckInAllocMsg::InboundsTest) => {
359362
write!(f, "null pointer is not a valid pointer for this operation")
360363
}
364+
DanglingIntPointer(0, msg) => {
365+
write!(f, "{}null pointer is not a valid pointer", msg)
366+
}
361367
DanglingIntPointer(i, msg) => {
362368
write!(f, "{}0x{:x} is not a valid pointer", msg, i)
363369
}

src/test/ui/consts/offset_from_ub.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![feature(const_ptr_offset_from)]
1+
#![feature(const_ptr_offset_from, const_ptr_offset)]
22
#![feature(core_intrinsics)]
33

44
use std::intrinsics::ptr_offset_from;
@@ -44,4 +44,30 @@ pub const DIFFERENT_INT: isize = { // offset_from with two different integers: l
4444
//~| 0x10 is not a valid pointer
4545
};
4646

47+
const OUT_OF_BOUNDS_1: isize = {
48+
let start_ptr = &4 as *const _ as *const u8;
49+
let length = 10;
50+
let end_ptr = (start_ptr).wrapping_add(length);
51+
// First ptr is out of bounds
52+
unsafe { ptr_offset_from(end_ptr, start_ptr) } //~ERROR evaluation of constant value failed
53+
//~| pointer at offset 10 is out-of-bounds
54+
};
55+
56+
const OUT_OF_BOUNDS_2: isize = {
57+
let start_ptr = &4 as *const _ as *const u8;
58+
let length = 10;
59+
let end_ptr = (start_ptr).wrapping_add(length);
60+
// Second ptr is out of bounds
61+
unsafe { ptr_offset_from(start_ptr, end_ptr) } //~ERROR evaluation of constant value failed
62+
//~| pointer at offset 10 is out-of-bounds
63+
};
64+
65+
const OUT_OF_BOUNDS_SAME: isize = {
66+
let start_ptr = &4 as *const _ as *const u8;
67+
let length = 10;
68+
let end_ptr = (start_ptr).wrapping_add(length);
69+
unsafe { ptr_offset_from(end_ptr, end_ptr) } //~ERROR evaluation of constant value failed
70+
//~| pointer at offset 10 is out-of-bounds
71+
};
72+
4773
fn main() {}

src/test/ui/consts/offset_from_ub.stderr

+22-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ error[E0080]: evaluation of constant value failed
1010
LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1212
| |
13-
| 0x2a is not a valid pointer
13+
| out-of-bounds offset_from: 0x2a is not a valid pointer
1414
| inside `ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
1515
|
1616
::: $DIR/offset_from_ub.rs:23:14
@@ -28,14 +28,32 @@ error[E0080]: evaluation of constant value failed
2828
--> $DIR/offset_from_ub.rs:36:14
2929
|
3030
LL | unsafe { ptr_offset_from(ptr, ptr) }
31-
| ^^^^^^^^^^^^^^^^^^^^^^^^^ null pointer is not a valid pointer for this operation
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: null pointer is not a valid pointer
3232

3333
error[E0080]: evaluation of constant value failed
3434
--> $DIR/offset_from_ub.rs:43:14
3535
|
3636
LL | unsafe { ptr_offset_from(ptr2, ptr1) }
37-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 0x10 is not a valid pointer
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: 0x10 is not a valid pointer
3838

39-
error: aborting due to 5 previous errors
39+
error[E0080]: evaluation of constant value failed
40+
--> $DIR/offset_from_ub.rs:52:14
41+
|
42+
LL | unsafe { ptr_offset_from(end_ptr, start_ptr) }
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc18 has size 4, so pointer at offset 10 is out-of-bounds
44+
45+
error[E0080]: evaluation of constant value failed
46+
--> $DIR/offset_from_ub.rs:61:14
47+
|
48+
LL | unsafe { ptr_offset_from(start_ptr, end_ptr) }
49+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc21 has size 4, so pointer at offset 10 is out-of-bounds
50+
51+
error[E0080]: evaluation of constant value failed
52+
--> $DIR/offset_from_ub.rs:69:14
53+
|
54+
LL | unsafe { ptr_offset_from(end_ptr, end_ptr) }
55+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc24 has size 4, so pointer at offset 10 is out-of-bounds
56+
57+
error: aborting due to 8 previous errors
4058

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

src/test/ui/consts/offset_ub.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ error[E0080]: evaluation of constant value failed
144144
LL | unsafe { intrinsics::offset(self, count) }
145145
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
146146
| |
147-
| pointer arithmetic failed: 0x0 is not a valid pointer
147+
| pointer arithmetic failed: null pointer is not a valid pointer
148148
| inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
149149
|
150150
::: $DIR/offset_ub.rs:22:50

0 commit comments

Comments
 (0)