about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2025-04-30 16:52:25 +0200
committerRalf Jung <post@ralfj.de>2025-04-30 18:45:41 +0200
commit00f25a8e1cf7d749946a6a9fb7327bf4710c931f (patch)
tree7155b6d1dd2211aba2ebabd7ffb010843c0c0d4e /compiler
parent427288b3ce2d574847fdb41cc3184c893750e09a (diff)
downloadrust-00f25a8e1cf7d749946a6a9fb7327bf4710c931f.tar.gz
rust-00f25a8e1cf7d749946a6a9fb7327bf4710c931f.zip
interpret: better error message for out-of-bounds pointer arithmetic and accesses
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_const_eval/messages.ftl70
-rw-r--r--compiler/rustc_const_eval/src/errors.rs33
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs10
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs16
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs2
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs8
6 files changed, 64 insertions, 75 deletions
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index dd481e04abb..f4defd2aa13 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -12,6 +12,27 @@ const_eval_already_reported =
 const_eval_assume_false =
     `assume` called with `false`
 
+const_eval_bad_pointer_op = {$operation ->
+  [MemoryAccess] memory access failed
+  [InboundsPointerArithmetic] in-bounds pointer arithmetic failed
+  *[Dereferenceable] pointer not dereferenceable
+}
+const_eval_bad_pointer_op_attempting = {const_eval_bad_pointer_op}: {$operation ->
+    [MemoryAccess] attempting to access {$inbounds_size ->
+            [1] 1 byte
+            *[x] {$inbounds_size} bytes
+        }
+    [InboundsPointerArithmetic] attempting to offset pointer by {$inbounds_size ->
+            [1] 1 byte
+            *[x] {$inbounds_size} bytes
+        }
+    *[Dereferenceable] pointer must {$inbounds_size ->
+            [0] point to some allocation
+            [1] be dereferenceable for 1 byte
+            *[x] be dereferenceable for {$inbounds_size} bytes
+        }
+    }
+
 const_eval_bounds_check_failed =
     indexing out of bounds: the len is {$len} but the index is {$index}
 const_eval_call_nonzero_intrinsic =
@@ -39,9 +60,9 @@ const_eval_copy_nonoverlapping_overlapping =
     `copy_nonoverlapping` called on overlapping ranges
 
 const_eval_dangling_int_pointer =
-    {$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got {$pointer} which is a dangling pointer (it has no provenance)
+    {const_eval_bad_pointer_op_attempting}, but got {$pointer} which is a dangling pointer (it has no provenance)
 const_eval_dangling_null_pointer =
-    {$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got a null pointer
+    {const_eval_bad_pointer_op_attempting}, but got null pointer
 
 const_eval_dangling_ptr_in_final = encountered dangling pointer in final value of {const_eval_intern_kind}
 const_eval_dead_local =
@@ -77,21 +98,6 @@ const_eval_error = {$error_kind ->
 const_eval_exact_div_has_remainder =
     exact_div: {$a} cannot be divided by {$b} without remainder
 
-const_eval_expected_inbounds_pointer =
-    expected a pointer to {$inbounds_size_abs ->
-        [0] some allocation
-        *[x] {$inbounds_size_is_neg ->
-            [false] {$inbounds_size_abs ->
-                    [1] 1 byte of memory
-                    *[x] {$inbounds_size_abs} bytes of memory
-                }
-            *[true] the end of {$inbounds_size_abs ->
-                    [1] 1 byte of memory
-                    *[x] {$inbounds_size_abs} bytes of memory
-                }
-        }
-    }
-
 const_eval_extern_static =
     cannot access extern static `{$did}`
 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_ ->
 
 const_eval_frame_note_last = the failure occurred here
 
-const_eval_in_bounds_test = out-of-bounds pointer use
 const_eval_incompatible_calling_conventions =
     calling a function with calling convention {$callee_conv} using calling convention {$caller_conv}
 
@@ -206,7 +211,6 @@ const_eval_long_running =
 
 const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id}
 
-const_eval_memory_access_test = memory access failed
 const_eval_memory_exhausted =
     tried to allocate more memory than available to compiler
 
@@ -287,8 +291,6 @@ const_eval_offset_from_out_of_bounds =
     `{$name}` called on two different pointers where the memory range between them is not in-bounds of an allocation
 const_eval_offset_from_overflow =
     `{$name}` called when first pointer is too far ahead of second
-const_eval_offset_from_test =
-    out-of-bounds `offset_from` origin
 const_eval_offset_from_underflow =
     `{$name}` called when first pointer is too far before second
 const_eval_offset_from_unsigned_overflow =
@@ -312,27 +314,25 @@ const_eval_partial_pointer_overwrite =
     unable to overwrite parts of a pointer in memory at {$ptr}
 const_eval_pointer_arithmetic_overflow =
     overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`
-const_eval_pointer_arithmetic_test = out-of-bounds pointer arithmetic
+
 const_eval_pointer_out_of_bounds =
-    {$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got {$pointer} {$ptr_offset_is_neg ->
-        [true] which points to before the beginning of the allocation
-        *[false] {$inbounds_size_is_neg ->
-            [true] {$ptr_offset_abs ->
-                [0] which is at the beginning of the allocation
-                *[other] which does not have enough space to the beginning of the allocation
-            }
-            *[false] {$alloc_size_minus_ptr_offset ->
-                [0] which is at or beyond the end of the allocation of size {$alloc_size ->
+    {const_eval_bad_pointer_op_attempting}, but got {$pointer} which {$inbounds_size_is_neg ->
+        [false] {$alloc_size_minus_ptr_offset ->
+                [0] is at or beyond the end of the allocation of size {$alloc_size ->
                     [1] 1 byte
                     *[x] {$alloc_size} bytes
                 }
-                [1] which is only 1 byte from the end of the allocation
-                *[x] which is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation
+                [1] is only 1 byte from the end of the allocation
+                *[x] is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation
+            }
+        *[true] {$ptr_offset_abs ->
+                [0] is at the beginning of the allocation
+                *[other] is only {$ptr_offset_abs} bytes from the beginning of the allocation
             }
-        }
     }
+
 const_eval_pointer_use_after_free =
-    {$bad_pointer_message}: {$alloc_id} has been freed, so this pointer is dangling
+    {const_eval_bad_pointer_op}: {$alloc_id} has been freed, so this pointer is dangling
 const_eval_ptr_as_bytes_1 =
     this code performed an operation that depends on the underlying bytes representing a pointer
 const_eval_ptr_as_bytes_2 =
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 6472aaa5758..826ea0e58ec 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -5,15 +5,14 @@ use either::Either;
 use rustc_abi::WrappingRange;
 use rustc_errors::codes::*;
 use rustc_errors::{
-    Diag, DiagArgValue, DiagCtxtHandle, DiagMessage, Diagnostic, EmissionGuarantee, Level,
-    MultiSpan, Subdiagnostic,
+    Diag, DiagArgValue, DiagMessage, Diagnostic, EmissionGuarantee, Level, MultiSpan, Subdiagnostic,
 };
 use rustc_hir::ConstContext;
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::mir::interpret::{
-    CheckInAllocMsg, CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind,
-    InvalidProgramInfo, Misalignment, Pointer, PointerKind, ResourceExhaustionInfo,
-    UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
+    CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind, InvalidProgramInfo,
+    Misalignment, Pointer, PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo,
+    UnsupportedOpInfo, ValidationErrorInfo,
 };
 use rustc_middle::ty::{self, Mutability, Ty};
 use rustc_span::{Span, Symbol};
@@ -498,19 +497,6 @@ pub trait ReportErrorExt {
     }
 }
 
-fn bad_pointer_message(msg: CheckInAllocMsg, dcx: DiagCtxtHandle<'_>) -> String {
-    use crate::fluent_generated::*;
-
-    let msg = match msg {
-        CheckInAllocMsg::MemoryAccessTest => const_eval_memory_access_test,
-        CheckInAllocMsg::PointerArithmeticTest => const_eval_pointer_arithmetic_test,
-        CheckInAllocMsg::OffsetFromTest => const_eval_offset_from_test,
-        CheckInAllocMsg::InboundsTest => const_eval_in_bounds_test,
-    };
-
-    dcx.eagerly_translate_to_string(msg, [].into_iter())
-}
-
 impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
     fn diagnostic_message(&self) -> DiagMessage {
         use UndefinedBehaviorInfo::*;
@@ -564,7 +550,6 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
 
     fn add_args<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
         use UndefinedBehaviorInfo::*;
-        let dcx = diag.dcx;
         match self {
             Ub(_) => {}
             Custom(custom) => {
@@ -612,12 +597,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
                 diag.arg("vtable_dyn_type", vtable_dyn_type.to_string());
             }
             PointerUseAfterFree(alloc_id, msg) => {
-                diag.arg("alloc_id", alloc_id)
-                    .arg("bad_pointer_message", bad_pointer_message(msg, dcx));
+                diag.arg("alloc_id", alloc_id).arg("operation", format!("{:?}", msg));
             }
             PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, inbounds_size, msg } => {
                 diag.arg("alloc_size", alloc_size.bytes());
-                diag.arg("bad_pointer_message", bad_pointer_message(msg, dcx));
                 diag.arg("pointer", {
                     let mut out = format!("{:?}", alloc_id);
                     if ptr_offset > 0 {
@@ -627,14 +610,17 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
                     }
                     out
                 });
+                diag.arg("inbounds_size", inbounds_size);
                 diag.arg("inbounds_size_is_neg", inbounds_size < 0);
                 diag.arg("inbounds_size_abs", inbounds_size.unsigned_abs());
+                diag.arg("ptr_offset", ptr_offset);
                 diag.arg("ptr_offset_is_neg", ptr_offset < 0);
                 diag.arg("ptr_offset_abs", ptr_offset.unsigned_abs());
                 diag.arg(
                     "alloc_size_minus_ptr_offset",
                     alloc_size.bytes().saturating_sub(ptr_offset as u64),
                 );
+                diag.arg("operation", format!("{:?}", msg));
             }
             DanglingIntPointer { addr, inbounds_size, msg } => {
                 if addr != 0 {
@@ -644,9 +630,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
                     );
                 }
 
+                diag.arg("inbounds_size", inbounds_size);
                 diag.arg("inbounds_size_is_neg", inbounds_size < 0);
                 diag.arg("inbounds_size_abs", inbounds_size.unsigned_abs());
-                diag.arg("bad_pointer_message", bad_pointer_message(msg, dcx));
+                diag.arg("operation", format!("{:?}", msg));
             }
             AlignmentCheckFailed(Misalignment { required, has }, msg) => {
                 diag.arg("required", required.bytes());
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 97d066ffe3f..3f9619a79a8 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -349,7 +349,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
 
                 // Check that the memory between them is dereferenceable at all, starting from the
                 // origin pointer: `dist` is `a - b`, so it is based on `b`.
-                self.check_ptr_access_signed(b, dist, CheckInAllocMsg::OffsetFromTest)
+                self.check_ptr_access_signed(b, dist, CheckInAllocMsg::Dereferenceable)
                     .map_err_kind(|_| {
                         // This could mean they point to different allocations, or they point to the same allocation
                         // but not the entire range between the pointers is in-bounds.
@@ -373,7 +373,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 self.check_ptr_access_signed(
                     a,
                     dist.checked_neg().unwrap(), // i64::MIN is impossible as no allocation can be that large
-                    CheckInAllocMsg::OffsetFromTest,
+                    CheckInAllocMsg::Dereferenceable,
                 )
                 .map_err_kind(|_| {
                     // Make the error more specific.
@@ -652,7 +652,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         offset_bytes: i64,
     ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
         // The offset must be in bounds starting from `ptr`.
-        self.check_ptr_access_signed(ptr, offset_bytes, CheckInAllocMsg::PointerArithmeticTest)?;
+        self.check_ptr_access_signed(
+            ptr,
+            offset_bytes,
+            CheckInAllocMsg::InboundsPointerArithmetic,
+        )?;
         // This also implies that there is no overflow, so we are done.
         interp_ok(ptr.wrapping_signed_offset(offset_bytes, self))
     }
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 9d8130661b0..43bf48a9b96 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -351,7 +351,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                         kind = "static_mem"
                     )
                 }
-                None => err_ub!(PointerUseAfterFree(alloc_id, CheckInAllocMsg::MemoryAccessTest)),
+                None => err_ub!(PointerUseAfterFree(alloc_id, CheckInAllocMsg::MemoryAccess)),
             })
             .into();
         };
@@ -414,10 +414,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             self,
             ptr,
             size,
-            CheckInAllocMsg::MemoryAccessTest,
+            CheckInAllocMsg::MemoryAccess,
             |this, alloc_id, offset, prov| {
-                let (size, align) = this
-                    .get_live_alloc_size_and_align(alloc_id, CheckInAllocMsg::MemoryAccessTest)?;
+                let (size, align) =
+                    this.get_live_alloc_size_and_align(alloc_id, CheckInAllocMsg::MemoryAccess)?;
                 interp_ok((size, align, (alloc_id, offset, prov)))
             },
         )
@@ -613,7 +613,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             }
             Some(GlobalAlloc::Function { .. }) => throw_ub!(DerefFunctionPointer(id)),
             Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)),
-            None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccessTest)),
+            None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccess)),
             Some(GlobalAlloc::Static(def_id)) => {
                 assert!(self.tcx.is_static(def_id));
                 // 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> {
             self,
             ptr,
             size_i64,
-            CheckInAllocMsg::MemoryAccessTest,
+            CheckInAllocMsg::MemoryAccess,
             |this, alloc_id, offset, prov| {
                 let alloc = this.get_alloc_raw(alloc_id)?;
                 interp_ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc)))
@@ -809,7 +809,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             self,
             ptr,
             size_i64,
-            CheckInAllocMsg::MemoryAccessTest,
+            CheckInAllocMsg::MemoryAccess,
             |this, alloc_id, offset, prov| {
                 let (alloc, machine) = this.get_alloc_raw_mut(alloc_id)?;
                 interp_ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc, machine)))
@@ -1615,7 +1615,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 err_ub!(DanglingIntPointer {
                     addr: offset,
                     inbounds_size: size,
-                    msg: CheckInAllocMsg::InboundsTest
+                    msg: CheckInAllocMsg::Dereferenceable
                 })
             })
             .into()
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index c86af5a9a4b..8f39afa642a 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -510,7 +510,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
             self.ecx.check_ptr_access(
                 place.ptr(),
                 size,
-                CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
+                CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message
             ),
             self.path,
             Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind },
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 890756a17ca..6ff3cac049b 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -221,13 +221,11 @@ pub enum InvalidProgramInfo<'tcx> {
 #[derive(Debug, Copy, Clone)]
 pub enum CheckInAllocMsg {
     /// We are access memory.
-    MemoryAccessTest,
+    MemoryAccess,
     /// We are doing pointer arithmetic.
-    PointerArithmeticTest,
-    /// We are doing pointer offset_from.
-    OffsetFromTest,
+    InboundsPointerArithmetic,
     /// None of the above -- generic/unspecific inbounds test.
-    InboundsTest,
+    Dereferenceable,
 }
 
 /// Details of which pointer is not aligned.