Skip to content

Commit 00f25a8

Browse files
committed
interpret: better error message for out-of-bounds pointer arithmetic and accesses
1 parent 427288b commit 00f25a8

File tree

81 files changed

+202
-213
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+202
-213
lines changed

compiler/rustc_const_eval/messages.ftl

+35-35
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,27 @@ const_eval_already_reported =
1212
const_eval_assume_false =
1313
`assume` called with `false`
1414
15+
const_eval_bad_pointer_op = {$operation ->
16+
[MemoryAccess] memory access failed
17+
[InboundsPointerArithmetic] in-bounds pointer arithmetic failed
18+
*[Dereferenceable] pointer not dereferenceable
19+
}
20+
const_eval_bad_pointer_op_attempting = {const_eval_bad_pointer_op}: {$operation ->
21+
[MemoryAccess] attempting to access {$inbounds_size ->
22+
[1] 1 byte
23+
*[x] {$inbounds_size} bytes
24+
}
25+
[InboundsPointerArithmetic] attempting to offset pointer by {$inbounds_size ->
26+
[1] 1 byte
27+
*[x] {$inbounds_size} bytes
28+
}
29+
*[Dereferenceable] pointer must {$inbounds_size ->
30+
[0] point to some allocation
31+
[1] be dereferenceable for 1 byte
32+
*[x] be dereferenceable for {$inbounds_size} bytes
33+
}
34+
}
35+
1536
const_eval_bounds_check_failed =
1637
indexing out of bounds: the len is {$len} but the index is {$index}
1738
const_eval_call_nonzero_intrinsic =
@@ -39,9 +60,9 @@ const_eval_copy_nonoverlapping_overlapping =
3960
`copy_nonoverlapping` called on overlapping ranges
4061
4162
const_eval_dangling_int_pointer =
42-
{$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got {$pointer} which is a dangling pointer (it has no provenance)
63+
{const_eval_bad_pointer_op_attempting}, but got {$pointer} which is a dangling pointer (it has no provenance)
4364
const_eval_dangling_null_pointer =
44-
{$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got a null pointer
65+
{const_eval_bad_pointer_op_attempting}, but got null pointer
4566
4667
const_eval_dangling_ptr_in_final = encountered dangling pointer in final value of {const_eval_intern_kind}
4768
const_eval_dead_local =
@@ -77,21 +98,6 @@ const_eval_error = {$error_kind ->
7798
const_eval_exact_div_has_remainder =
7899
exact_div: {$a} cannot be divided by {$b} without remainder
79100
80-
const_eval_expected_inbounds_pointer =
81-
expected a pointer to {$inbounds_size_abs ->
82-
[0] some allocation
83-
*[x] {$inbounds_size_is_neg ->
84-
[false] {$inbounds_size_abs ->
85-
[1] 1 byte of memory
86-
*[x] {$inbounds_size_abs} bytes of memory
87-
}
88-
*[true] the end of {$inbounds_size_abs ->
89-
[1] 1 byte of memory
90-
*[x] {$inbounds_size_abs} bytes of memory
91-
}
92-
}
93-
}
94-
95101
const_eval_extern_static =
96102
cannot access extern static `{$did}`
97103
const_eval_extern_type_field = `extern type` field does not have a known offset
@@ -111,7 +117,6 @@ const_eval_frame_note_inner = inside {$where_ ->
111117
112118
const_eval_frame_note_last = the failure occurred here
113119
114-
const_eval_in_bounds_test = out-of-bounds pointer use
115120
const_eval_incompatible_calling_conventions =
116121
calling a function with calling convention {$callee_conv} using calling convention {$caller_conv}
117122
@@ -206,7 +211,6 @@ const_eval_long_running =
206211
207212
const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id}
208213
209-
const_eval_memory_access_test = memory access failed
210214
const_eval_memory_exhausted =
211215
tried to allocate more memory than available to compiler
212216
@@ -287,8 +291,6 @@ const_eval_offset_from_out_of_bounds =
287291
`{$name}` called on two different pointers where the memory range between them is not in-bounds of an allocation
288292
const_eval_offset_from_overflow =
289293
`{$name}` called when first pointer is too far ahead of second
290-
const_eval_offset_from_test =
291-
out-of-bounds `offset_from` origin
292294
const_eval_offset_from_underflow =
293295
`{$name}` called when first pointer is too far before second
294296
const_eval_offset_from_unsigned_overflow =
@@ -312,27 +314,25 @@ const_eval_partial_pointer_overwrite =
312314
unable to overwrite parts of a pointer in memory at {$ptr}
313315
const_eval_pointer_arithmetic_overflow =
314316
overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`
315-
const_eval_pointer_arithmetic_test = out-of-bounds pointer arithmetic
317+
316318
const_eval_pointer_out_of_bounds =
317-
{$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got {$pointer} {$ptr_offset_is_neg ->
318-
[true] which points to before the beginning of the allocation
319-
*[false] {$inbounds_size_is_neg ->
320-
[true] {$ptr_offset_abs ->
321-
[0] which is at the beginning of the allocation
322-
*[other] which does not have enough space to the beginning of the allocation
323-
}
324-
*[false] {$alloc_size_minus_ptr_offset ->
325-
[0] which is at or beyond the end of the allocation of size {$alloc_size ->
319+
{const_eval_bad_pointer_op_attempting}, but got {$pointer} which {$inbounds_size_is_neg ->
320+
[false] {$alloc_size_minus_ptr_offset ->
321+
[0] is at or beyond the end of the allocation of size {$alloc_size ->
326322
[1] 1 byte
327323
*[x] {$alloc_size} bytes
328324
}
329-
[1] which is only 1 byte from the end of the allocation
330-
*[x] which is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation
325+
[1] is only 1 byte from the end of the allocation
326+
*[x] is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation
327+
}
328+
*[true] {$ptr_offset_abs ->
329+
[0] is at the beginning of the allocation
330+
*[other] is only {$ptr_offset_abs} bytes from the beginning of the allocation
331331
}
332-
}
333332
}
333+
334334
const_eval_pointer_use_after_free =
335-
{$bad_pointer_message}: {$alloc_id} has been freed, so this pointer is dangling
335+
{const_eval_bad_pointer_op}: {$alloc_id} has been freed, so this pointer is dangling
336336
const_eval_ptr_as_bytes_1 =
337337
this code performed an operation that depends on the underlying bytes representing a pointer
338338
const_eval_ptr_as_bytes_2 =

compiler/rustc_const_eval/src/errors.rs

+10-23
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@ use either::Either;
55
use rustc_abi::WrappingRange;
66
use rustc_errors::codes::*;
77
use rustc_errors::{
8-
Diag, DiagArgValue, DiagCtxtHandle, DiagMessage, Diagnostic, EmissionGuarantee, Level,
9-
MultiSpan, Subdiagnostic,
8+
Diag, DiagArgValue, DiagMessage, Diagnostic, EmissionGuarantee, Level, MultiSpan, Subdiagnostic,
109
};
1110
use rustc_hir::ConstContext;
1211
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
1312
use rustc_middle::mir::interpret::{
14-
CheckInAllocMsg, CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind,
15-
InvalidProgramInfo, Misalignment, Pointer, PointerKind, ResourceExhaustionInfo,
16-
UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
13+
CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind, InvalidProgramInfo,
14+
Misalignment, Pointer, PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo,
15+
UnsupportedOpInfo, ValidationErrorInfo,
1716
};
1817
use rustc_middle::ty::{self, Mutability, Ty};
1918
use rustc_span::{Span, Symbol};
@@ -498,19 +497,6 @@ pub trait ReportErrorExt {
498497
}
499498
}
500499

501-
fn bad_pointer_message(msg: CheckInAllocMsg, dcx: DiagCtxtHandle<'_>) -> String {
502-
use crate::fluent_generated::*;
503-
504-
let msg = match msg {
505-
CheckInAllocMsg::MemoryAccessTest => const_eval_memory_access_test,
506-
CheckInAllocMsg::PointerArithmeticTest => const_eval_pointer_arithmetic_test,
507-
CheckInAllocMsg::OffsetFromTest => const_eval_offset_from_test,
508-
CheckInAllocMsg::InboundsTest => const_eval_in_bounds_test,
509-
};
510-
511-
dcx.eagerly_translate_to_string(msg, [].into_iter())
512-
}
513-
514500
impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
515501
fn diagnostic_message(&self) -> DiagMessage {
516502
use UndefinedBehaviorInfo::*;
@@ -564,7 +550,6 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
564550

565551
fn add_args<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
566552
use UndefinedBehaviorInfo::*;
567-
let dcx = diag.dcx;
568553
match self {
569554
Ub(_) => {}
570555
Custom(custom) => {
@@ -612,12 +597,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
612597
diag.arg("vtable_dyn_type", vtable_dyn_type.to_string());
613598
}
614599
PointerUseAfterFree(alloc_id, msg) => {
615-
diag.arg("alloc_id", alloc_id)
616-
.arg("bad_pointer_message", bad_pointer_message(msg, dcx));
600+
diag.arg("alloc_id", alloc_id).arg("operation", format!("{:?}", msg));
617601
}
618602
PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, inbounds_size, msg } => {
619603
diag.arg("alloc_size", alloc_size.bytes());
620-
diag.arg("bad_pointer_message", bad_pointer_message(msg, dcx));
621604
diag.arg("pointer", {
622605
let mut out = format!("{:?}", alloc_id);
623606
if ptr_offset > 0 {
@@ -627,14 +610,17 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
627610
}
628611
out
629612
});
613+
diag.arg("inbounds_size", inbounds_size);
630614
diag.arg("inbounds_size_is_neg", inbounds_size < 0);
631615
diag.arg("inbounds_size_abs", inbounds_size.unsigned_abs());
616+
diag.arg("ptr_offset", ptr_offset);
632617
diag.arg("ptr_offset_is_neg", ptr_offset < 0);
633618
diag.arg("ptr_offset_abs", ptr_offset.unsigned_abs());
634619
diag.arg(
635620
"alloc_size_minus_ptr_offset",
636621
alloc_size.bytes().saturating_sub(ptr_offset as u64),
637622
);
623+
diag.arg("operation", format!("{:?}", msg));
638624
}
639625
DanglingIntPointer { addr, inbounds_size, msg } => {
640626
if addr != 0 {
@@ -644,9 +630,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
644630
);
645631
}
646632

633+
diag.arg("inbounds_size", inbounds_size);
647634
diag.arg("inbounds_size_is_neg", inbounds_size < 0);
648635
diag.arg("inbounds_size_abs", inbounds_size.unsigned_abs());
649-
diag.arg("bad_pointer_message", bad_pointer_message(msg, dcx));
636+
diag.arg("operation", format!("{:?}", msg));
650637
}
651638
AlignmentCheckFailed(Misalignment { required, has }, msg) => {
652639
diag.arg("required", required.bytes());

compiler/rustc_const_eval/src/interpret/intrinsics.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
349349

350350
// Check that the memory between them is dereferenceable at all, starting from the
351351
// origin pointer: `dist` is `a - b`, so it is based on `b`.
352-
self.check_ptr_access_signed(b, dist, CheckInAllocMsg::OffsetFromTest)
352+
self.check_ptr_access_signed(b, dist, CheckInAllocMsg::Dereferenceable)
353353
.map_err_kind(|_| {
354354
// This could mean they point to different allocations, or they point to the same allocation
355355
// but not the entire range between the pointers is in-bounds.
@@ -373,7 +373,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
373373
self.check_ptr_access_signed(
374374
a,
375375
dist.checked_neg().unwrap(), // i64::MIN is impossible as no allocation can be that large
376-
CheckInAllocMsg::OffsetFromTest,
376+
CheckInAllocMsg::Dereferenceable,
377377
)
378378
.map_err_kind(|_| {
379379
// Make the error more specific.
@@ -652,7 +652,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
652652
offset_bytes: i64,
653653
) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
654654
// The offset must be in bounds starting from `ptr`.
655-
self.check_ptr_access_signed(ptr, offset_bytes, CheckInAllocMsg::PointerArithmeticTest)?;
655+
self.check_ptr_access_signed(
656+
ptr,
657+
offset_bytes,
658+
CheckInAllocMsg::InboundsPointerArithmetic,
659+
)?;
656660
// This also implies that there is no overflow, so we are done.
657661
interp_ok(ptr.wrapping_signed_offset(offset_bytes, self))
658662
}

compiler/rustc_const_eval/src/interpret/memory.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
351351
kind = "static_mem"
352352
)
353353
}
354-
None => err_ub!(PointerUseAfterFree(alloc_id, CheckInAllocMsg::MemoryAccessTest)),
354+
None => err_ub!(PointerUseAfterFree(alloc_id, CheckInAllocMsg::MemoryAccess)),
355355
})
356356
.into();
357357
};
@@ -414,10 +414,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
414414
self,
415415
ptr,
416416
size,
417-
CheckInAllocMsg::MemoryAccessTest,
417+
CheckInAllocMsg::MemoryAccess,
418418
|this, alloc_id, offset, prov| {
419-
let (size, align) = this
420-
.get_live_alloc_size_and_align(alloc_id, CheckInAllocMsg::MemoryAccessTest)?;
419+
let (size, align) =
420+
this.get_live_alloc_size_and_align(alloc_id, CheckInAllocMsg::MemoryAccess)?;
421421
interp_ok((size, align, (alloc_id, offset, prov)))
422422
},
423423
)
@@ -613,7 +613,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
613613
}
614614
Some(GlobalAlloc::Function { .. }) => throw_ub!(DerefFunctionPointer(id)),
615615
Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)),
616-
None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccessTest)),
616+
None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccess)),
617617
Some(GlobalAlloc::Static(def_id)) => {
618618
assert!(self.tcx.is_static(def_id));
619619
// Thread-local statics do not have a constant address. They *must* be accessed via
@@ -707,7 +707,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
707707
self,
708708
ptr,
709709
size_i64,
710-
CheckInAllocMsg::MemoryAccessTest,
710+
CheckInAllocMsg::MemoryAccess,
711711
|this, alloc_id, offset, prov| {
712712
let alloc = this.get_alloc_raw(alloc_id)?;
713713
interp_ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc)))
@@ -809,7 +809,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
809809
self,
810810
ptr,
811811
size_i64,
812-
CheckInAllocMsg::MemoryAccessTest,
812+
CheckInAllocMsg::MemoryAccess,
813813
|this, alloc_id, offset, prov| {
814814
let (alloc, machine) = this.get_alloc_raw_mut(alloc_id)?;
815815
interp_ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc, machine)))
@@ -1615,7 +1615,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
16151615
err_ub!(DanglingIntPointer {
16161616
addr: offset,
16171617
inbounds_size: size,
1618-
msg: CheckInAllocMsg::InboundsTest
1618+
msg: CheckInAllocMsg::Dereferenceable
16191619
})
16201620
})
16211621
.into()

compiler/rustc_const_eval/src/interpret/validity.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
510510
self.ecx.check_ptr_access(
511511
place.ptr(),
512512
size,
513-
CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
513+
CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message
514514
),
515515
self.path,
516516
Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind },

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

+3-5
Original file line numberDiff line numberDiff line change
@@ -221,13 +221,11 @@ pub enum InvalidProgramInfo<'tcx> {
221221
#[derive(Debug, Copy, Clone)]
222222
pub enum CheckInAllocMsg {
223223
/// We are access memory.
224-
MemoryAccessTest,
224+
MemoryAccess,
225225
/// We are doing pointer arithmetic.
226-
PointerArithmeticTest,
227-
/// We are doing pointer offset_from.
228-
OffsetFromTest,
226+
InboundsPointerArithmetic,
229227
/// None of the above -- generic/unspecific inbounds test.
230-
InboundsTest,
228+
Dereferenceable,
231229
}
232230

233231
/// Details of which pointer is not aligned.

src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
594594
) -> InterpResult<'tcx, Option<Provenance>> {
595595
let this = self.eval_context_mut();
596596
// Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
597-
this.check_ptr_access(place.ptr(), size, CheckInAllocMsg::InboundsTest)?;
597+
this.check_ptr_access(place.ptr(), size, CheckInAllocMsg::Dereferenceable)?;
598598

599599
// It is crucial that this gets called on all code paths, to ensure we track tag creation.
600600
let log_creation = |this: &MiriInterpCx<'tcx>,

src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
197197
// Make sure the new permission makes sense as the initial permission of a fresh tag.
198198
assert!(new_perm.initial_state.is_initial());
199199
// Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
200-
this.check_ptr_access(place.ptr(), ptr_size, CheckInAllocMsg::InboundsTest)?;
200+
this.check_ptr_access(place.ptr(), ptr_size, CheckInAllocMsg::Dereferenceable)?;
201201

202202
// It is crucial that this gets called on all code paths, to ensure we track tag creation.
203203
let log_creation = |this: &MiriInterpCx<'tcx>,

src/tools/miri/src/shims/unix/android/thread.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub fn prctl<'tcx>(
4242
ecx.check_ptr_access(
4343
name.to_pointer(ecx)?,
4444
Size::from_bytes(TASK_COMM_LEN),
45-
CheckInAllocMsg::MemoryAccessTest,
45+
CheckInAllocMsg::MemoryAccess,
4646
)?;
4747
let res = ecx.pthread_getname_np(thread, name, len, /* truncate*/ false)?;
4848
assert_eq!(res, ThreadNameResult::Ok);

src/tools/miri/src/shims/unix/fd.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
226226
trace!("Reading from FD {}, size {}", fd_num, count);
227227

228228
// Check that the *entire* buffer is actually valid memory.
229-
this.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccessTest)?;
229+
this.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccess)?;
230230

231231
// We cap the number of read bytes to the largest value that we are able to fit in both the
232232
// host's and target's `isize`. This saves us from having to handle overflows later.
@@ -292,7 +292,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
292292
// Isolation check is done via `FileDescription` trait.
293293

294294
// Check that the *entire* buffer is actually valid memory.
295-
this.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccessTest)?;
295+
this.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccess)?;
296296

297297
// We cap the number of written bytes to the largest value that we are able to fit in both the
298298
// host's and target's `isize`. This saves us from having to handle overflows later.

src/tools/miri/tests/fail-dep/libc/affinity.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: memory access failed: expected a pointer to 129 bytes of memory, but got ALLOC which is only 128 bytes from the end of the allocation
1+
error: Undefined Behavior: memory access failed: attempting to access 129 bytes, but got ALLOC which is only 128 bytes from the end of the allocation
22
--> tests/fail-dep/libc/affinity.rs:LL:CC
33
|
44
LL | let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>() + 1, &cpuset) };
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 129 bytes of memory, but got ALLOC which is only 128 bytes from the end of the allocation
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 129 bytes, but got ALLOC which is only 128 bytes from the end of the allocation
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

0 commit comments

Comments
 (0)