Skip to content

Commit 3b806d3

Browse files
committed
interpret: fix dealing with overflow during slice indexing
1 parent 0307e40 commit 3b806d3

5 files changed

Lines changed: 47 additions & 7 deletions

File tree

‎compiler/rustc_const_eval/src/interpret/intrinsics.rs‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -599,9 +599,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
599599
let count = self.read_target_usize(count)?;
600600
let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap())?;
601601
let(size, align) = (layout.size, layout.align.abi);
602-
// `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
603-
// but no actual allocation can be big enough for the difference to be noticeable.
604-
let size = size.checked_mul(count,self).ok_or_else(|| {
602+
603+
let size = self.compute_size_in_bytes(size, count).ok_or_else(|| {
605604
err_ub_custom!(
606605
fluent::const_eval_size_overflow,
607606
name = if nonoverlapping {"copy_nonoverlapping"} else {"copy"}
@@ -649,7 +648,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
649648

650649
// `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
651650
// but no actual allocation can be big enough for the difference to be noticeable.
652-
let len = layout.size.checked_mul(count,self).ok_or_else(|| {
651+
let len = self.compute_size_in_bytes(layout.size, count).ok_or_else(|| {
653652
err_ub_custom!(fluent::const_eval_size_overflow, name = "write_bytes")
654653
})?;
655654

‎compiler/rustc_const_eval/src/interpret/operator.rs‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use either::Either;
22
use rustc_apfloat::{Float,FloatConvert};
3-
use rustc_middle::mir::interpret::{InterpResult,Scalar};
3+
use rustc_middle::mir::interpret::{InterpResult,PointerArithmetic,Scalar};
44
use rustc_middle::mir::NullOp;
55
use rustc_middle::ty::layout::{LayoutOf,TyAndLayout};
66
use rustc_middle::ty::{self,FloatTy,ScalarInt,Ty};
77
use rustc_middle::{bug, mir, span_bug};
88
use rustc_span::symbol::sym;
9+
use rustc_target::abi::Size;
910
use tracing::trace;
1011

1112
usesuper::{throw_ub,ImmTy,InterpCx,Machine,MemPlaceMeta};
@@ -287,6 +288,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
287288
})
288289
}
289290

291+
/// Computes the total size of this access, `count * elem_size`,
292+
/// checking for overflow beyond isize::MAX.
293+
pub(super)fncompute_size_in_bytes(&self,elem_size:Size,count:u64) -> Option<Size>{
294+
// `checked_mul` applies `u64` limits independent of the target pointer size... but the
295+
// subsequent check for `max_size_of_val` means we also handle 32bit targets correctly.
296+
// (We cannot use `Size::checked_mul` as that enforces `obj_size_bound` as the limit, which
297+
// would be wrong here.)
298+
elem_size
299+
.bytes()
300+
.checked_mul(count)
301+
.map(Size::from_bytes)
302+
.filter(|&total| total <= self.max_size_of_val())
303+
}
304+
290305
fnbinary_ptr_op(
291306
&self,
292307
bin_op: mir::BinOp,

‎compiler/rustc_const_eval/src/interpret/projection.rs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_target::abi::{self, Size, VariantIdx};
1717
use tracing::{debug, instrument};
1818

1919
usesuper::{
20-
throw_ub, throw_unsup,InterpCx,InterpResult,MPlaceTy,Machine,MemPlaceMeta,OpTy,
20+
err_ub,throw_ub, throw_unsup,InterpCx,InterpResult,MPlaceTy,Machine,MemPlaceMeta,OpTy,
2121
Provenance,Scalar,
2222
};
2323

@@ -229,7 +229,11 @@ where
229229
// This can only be reached in ConstProp and non-rustc-MIR.
230230
throw_ub!(BoundsCheckFailed{ len, index });
231231
}
232-
let offset = stride * index;// `Size` multiplication
232+
// With raw slices, `len` can be so big that this *can* overflow.
233+
let offset = self
234+
.compute_size_in_bytes(stride, index)
235+
.ok_or_else(|| err_ub!(PointerArithOverflow))?;
236+
233237
// All fields have the same layout.
234238
let field_layout = base.layout().field(self,0);
235239
(offset, field_layout)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
constC:() = {
2+
let value = [1,2];
3+
let ptr = value.as_ptr().wrapping_add(2);
4+
let fat = std::ptr::slice_from_raw_parts(ptr, usize::MAX);
5+
unsafe{
6+
// This used to ICE, but it should just report UB.
7+
let _ice = (*fat)[usize::MAX - 1];
8+
//~^ERROR: constant value failed
9+
//~| overflow
10+
}
11+
};
12+
13+
fnmain(){}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0080]: evaluation of constant value failed
2+
--> $DIR/slice-index-overflow-issue-130284.rs:7:20
3+
|
4+
LL | let _ice = (*fat)[usize::MAX - 1];
5+
| ^^^^^^^^^^^^^^^^^^^^^^ overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`
6+
7+
error: aborting due to 1 previous error
8+
9+
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)