Skip to content

Commit 4e86576

Browse files
committed
Rollup merge of rust-lang#55645 - RalfJung:validity-range-inclusive, r=oli-obk
do not print wrapping ranges like normal ranges in validity diagnostics
2 parents 9d9146a + 3ec8974 commit 4e86576

27 files changed

+898
-483
lines changed

src/librustc/mir/interpret/error.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use ty::{Ty, layout};
1515
use ty::layout::{Size, Align, LayoutError};
1616
use rustc_target::spec::abi::Abi;
1717

18-
use super::Pointer;
18+
use super::{Pointer, Scalar};
1919

2020
use backtrace::Backtrace;
2121

@@ -240,7 +240,7 @@ pub enum EvalErrorKind<'tcx, O> {
240240
InvalidMemoryAccess,
241241
InvalidFunctionPointer,
242242
InvalidBool,
243-
InvalidDiscriminant(u128),
243+
InvalidDiscriminant(Scalar),
244244
PointerOutOfBounds {
245245
ptr: Pointer,
246246
access: bool,

src/librustc/mir/interpret/mod.rs

+43-36
Original file line numberDiff line numberDiff line change
@@ -91,42 +91,43 @@ pub trait PointerArithmetic: layout::HasDataLayout {
9191
}
9292

9393
//// Trunace the given value to the pointer size; also return whether there was an overflow
94+
#[inline]
9495
fn truncate_to_ptr(&self, val: u128) -> (u64, bool) {
9596
let max_ptr_plus_1 = 1u128 << self.pointer_size().bits();
9697
((val % max_ptr_plus_1) as u64, val >= max_ptr_plus_1)
9798
}
9899

99-
// Overflow checking only works properly on the range from -u64 to +u64.
100-
fn overflowing_signed_offset(&self, val: u64, i: i128) -> (u64, bool) {
101-
// FIXME: is it possible to over/underflow here?
102-
if i < 0 {
103-
// trickery to ensure that i64::min_value() works fine
104-
// this formula only works for true negative values, it panics for zero!
105-
let n = u64::max_value() - (i as u64) + 1;
106-
val.overflowing_sub(n)
107-
} else {
108-
self.overflowing_offset(val, i as u64)
109-
}
100+
#[inline]
101+
fn offset<'tcx>(&self, val: u64, i: u64) -> EvalResult<'tcx, u64> {
102+
let (res, over) = self.overflowing_offset(val, i);
103+
if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) }
110104
}
111105

106+
#[inline]
112107
fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) {
113108
let (res, over1) = val.overflowing_add(i);
114-
let (res, over2) = self.truncate_to_ptr(res as u128);
109+
let (res, over2) = self.truncate_to_ptr(u128::from(res));
115110
(res, over1 || over2)
116111
}
117112

113+
#[inline]
118114
fn signed_offset<'tcx>(&self, val: u64, i: i64) -> EvalResult<'tcx, u64> {
119-
let (res, over) = self.overflowing_signed_offset(val, i as i128);
115+
let (res, over) = self.overflowing_signed_offset(val, i128::from(i));
120116
if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) }
121117
}
122118

123-
fn offset<'tcx>(&self, val: u64, i: u64) -> EvalResult<'tcx, u64> {
124-
let (res, over) = self.overflowing_offset(val, i);
125-
if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) }
126-
}
127-
128-
fn wrapping_signed_offset(&self, val: u64, i: i64) -> u64 {
129-
self.overflowing_signed_offset(val, i as i128).0
119+
// Overflow checking only works properly on the range from -u64 to +u64.
120+
#[inline]
121+
fn overflowing_signed_offset(&self, val: u64, i: i128) -> (u64, bool) {
122+
// FIXME: is it possible to over/underflow here?
123+
if i < 0 {
124+
// trickery to ensure that i64::min_value() works fine
125+
// this formula only works for true negative values, it panics for zero!
126+
let n = u64::max_value() - (i as u64) + 1;
127+
val.overflowing_sub(n)
128+
} else {
129+
self.overflowing_offset(val, i as u64)
130+
}
130131
}
131132
}
132133

@@ -176,19 +177,27 @@ impl<'tcx, Tag> Pointer<Tag> {
176177
Pointer { alloc_id, offset, tag }
177178
}
178179

179-
pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
180-
Pointer::new_with_tag(
180+
#[inline]
181+
pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
182+
Ok(Pointer::new_with_tag(
181183
self.alloc_id,
182-
Size::from_bytes(cx.data_layout().wrapping_signed_offset(self.offset.bytes(), i)),
183-
self.tag,
184-
)
184+
Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?),
185+
self.tag
186+
))
185187
}
186188

187-
pub fn overflowing_signed_offset(self, i: i128, cx: &impl HasDataLayout) -> (Self, bool) {
188-
let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i);
189+
#[inline]
190+
pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) {
191+
let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes());
189192
(Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over)
190193
}
191194

195+
#[inline(always)]
196+
pub fn wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
197+
self.overflowing_offset(i, cx).0
198+
}
199+
200+
#[inline]
192201
pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
193202
Ok(Pointer::new_with_tag(
194203
self.alloc_id,
@@ -197,20 +206,18 @@ impl<'tcx, Tag> Pointer<Tag> {
197206
))
198207
}
199208

200-
pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) {
201-
let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes());
209+
#[inline]
210+
pub fn overflowing_signed_offset(self, i: i128, cx: &impl HasDataLayout) -> (Self, bool) {
211+
let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i);
202212
(Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over)
203213
}
204214

205-
pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
206-
Ok(Pointer::new_with_tag(
207-
self.alloc_id,
208-
Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?),
209-
self.tag
210-
))
215+
#[inline(always)]
216+
pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
217+
self.overflowing_signed_offset(i128::from(i), cx).0
211218
}
212219

213-
#[inline]
220+
#[inline(always)]
214221
pub fn erase_tag(self) -> Pointer {
215222
Pointer { alloc_id: self.alloc_id, offset: self.offset, tag: () }
216223
}

src/librustc/mir/interpret/value.rs

+45-8
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
#![allow(unknown_lints)]
11+
use std::fmt;
1212

1313
use ty::layout::{HasDataLayout, Size};
1414
use ty::subst::Substs;
@@ -99,6 +99,15 @@ pub enum Scalar<Tag=(), Id=AllocId> {
9999
Ptr(Pointer<Tag, Id>),
100100
}
101101

102+
impl<Tag> fmt::Display for Scalar<Tag> {
103+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104+
match self {
105+
Scalar::Ptr(_) => write!(f, "a pointer"),
106+
Scalar::Bits { bits, .. } => write!(f, "{}", bits),
107+
}
108+
}
109+
}
110+
102111
impl<'tcx> Scalar<()> {
103112
#[inline]
104113
pub fn with_default_tag<Tag>(self) -> Scalar<Tag>
@@ -134,32 +143,47 @@ impl<'tcx, Tag> Scalar<Tag> {
134143
}
135144

136145
#[inline]
137-
pub fn ptr_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
146+
pub fn ptr_offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
138147
let dl = cx.data_layout();
139148
match self {
140149
Scalar::Bits { bits, size } => {
141150
assert_eq!(size as u64, dl.pointer_size.bytes());
142151
Ok(Scalar::Bits {
143-
bits: dl.signed_offset(bits as u64, i)? as u128,
152+
bits: dl.offset(bits as u64, i.bytes())? as u128,
144153
size,
145154
})
146155
}
147-
Scalar::Ptr(ptr) => ptr.signed_offset(i, dl).map(Scalar::Ptr),
156+
Scalar::Ptr(ptr) => ptr.offset(i, dl).map(Scalar::Ptr),
148157
}
149158
}
150159

151160
#[inline]
152-
pub fn ptr_offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
161+
pub fn ptr_wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
153162
let dl = cx.data_layout();
154163
match self {
155164
Scalar::Bits { bits, size } => {
156165
assert_eq!(size as u64, dl.pointer_size.bytes());
166+
Scalar::Bits {
167+
bits: dl.overflowing_offset(bits as u64, i.bytes()).0 as u128,
168+
size,
169+
}
170+
}
171+
Scalar::Ptr(ptr) => Scalar::Ptr(ptr.wrapping_offset(i, dl)),
172+
}
173+
}
174+
175+
#[inline]
176+
pub fn ptr_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
177+
let dl = cx.data_layout();
178+
match self {
179+
Scalar::Bits { bits, size } => {
180+
assert_eq!(size as u64, dl.pointer_size().bytes());
157181
Ok(Scalar::Bits {
158-
bits: dl.offset(bits as u64, i.bytes())? as u128,
182+
bits: dl.signed_offset(bits as u64, i)? as u128,
159183
size,
160184
})
161185
}
162-
Scalar::Ptr(ptr) => ptr.offset(i, dl).map(Scalar::Ptr),
186+
Scalar::Ptr(ptr) => ptr.signed_offset(i, dl).map(Scalar::Ptr),
163187
}
164188
}
165189

@@ -170,14 +194,27 @@ impl<'tcx, Tag> Scalar<Tag> {
170194
Scalar::Bits { bits, size } => {
171195
assert_eq!(size as u64, dl.pointer_size.bytes());
172196
Scalar::Bits {
173-
bits: dl.wrapping_signed_offset(bits as u64, i) as u128,
197+
bits: dl.overflowing_signed_offset(bits as u64, i128::from(i)).0 as u128,
174198
size,
175199
}
176200
}
177201
Scalar::Ptr(ptr) => Scalar::Ptr(ptr.wrapping_signed_offset(i, dl)),
178202
}
179203
}
180204

205+
/// Returns this pointers offset from the allocation base, or from NULL (for
206+
/// integer pointers).
207+
#[inline]
208+
pub fn get_ptr_offset(self, cx: &impl HasDataLayout) -> Size {
209+
match self {
210+
Scalar::Bits { bits, size } => {
211+
assert_eq!(size as u64, cx.pointer_size().bytes());
212+
Size::from_bytes(bits as u64)
213+
}
214+
Scalar::Ptr(ptr) => ptr.offset,
215+
}
216+
}
217+
181218
#[inline]
182219
pub fn is_null_ptr(self, cx: &impl HasDataLayout) -> bool {
183220
match self {

src/librustc_mir/const_eval.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -539,10 +539,10 @@ fn validate_const<'a, 'tcx>(
539539
let val = (|| {
540540
let op = ecx.const_to_op(constant)?;
541541
let mut ref_tracking = RefTracking::new(op);
542-
while let Some((op, mut path)) = ref_tracking.todo.pop() {
542+
while let Some((op, path)) = ref_tracking.todo.pop() {
543543
ecx.validate_operand(
544544
op,
545-
&mut path,
545+
path,
546546
Some(&mut ref_tracking),
547547
/* const_mode */ true,
548548
)?;

src/librustc_mir/interpret/eval_context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
521521
// return place is always a local and then this cannot happen.
522522
self.validate_operand(
523523
self.place_to_op(return_place)?,
524-
&mut vec![],
524+
vec![],
525525
None,
526526
/*const_mode*/false,
527527
)?;

src/librustc_mir/interpret/machine.rs

+9-13
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ use std::hash::Hash;
1717

1818
use rustc::hir::{self, def_id::DefId};
1919
use rustc::mir;
20-
use rustc::ty::{self, Ty, layout::{Size, TyLayout}, query::TyCtxtAt};
20+
use rustc::ty::{self, layout::{Size, TyLayout}, query::TyCtxtAt};
2121

2222
use super::{
2323
Allocation, AllocId, EvalResult, Scalar,
24-
EvalContext, PlaceTy, OpTy, Pointer, MemPlace, MemoryKind,
24+
EvalContext, PlaceTy, MPlaceTy, OpTy, Pointer, MemoryKind,
2525
};
2626

2727
/// Whether this kind of memory is allowed to leak
@@ -217,26 +217,22 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
217217
#[inline]
218218
fn tag_reference(
219219
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
220-
place: MemPlace<Self::PointerTag>,
221-
_ty: Ty<'tcx>,
222-
_size: Size,
220+
place: MPlaceTy<'tcx, Self::PointerTag>,
223221
_mutability: Option<hir::Mutability>,
224-
) -> EvalResult<'tcx, MemPlace<Self::PointerTag>> {
225-
Ok(place)
222+
) -> EvalResult<'tcx, Scalar<Self::PointerTag>> {
223+
Ok(place.ptr)
226224
}
227225

228226
/// Executed when evaluating the `*` operator: Following a reference.
229-
/// This has the change to adjust the tag. It should not change anything else!
227+
/// This has the chance to adjust the tag. It should not change anything else!
230228
/// `mutability` can be `None` in case a raw ptr is being dereferenced.
231229
#[inline]
232230
fn tag_dereference(
233231
_ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
234-
place: MemPlace<Self::PointerTag>,
235-
_ty: Ty<'tcx>,
236-
_size: Size,
232+
place: MPlaceTy<'tcx, Self::PointerTag>,
237233
_mutability: Option<hir::Mutability>,
238-
) -> EvalResult<'tcx, MemPlace<Self::PointerTag>> {
239-
Ok(place)
234+
) -> EvalResult<'tcx, Scalar<Self::PointerTag>> {
235+
Ok(place.ptr)
240236
}
241237

242238
/// Execute a validation operation

src/librustc_mir/interpret/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ mod terminator;
2323
mod traits;
2424
mod validity;
2525
mod intrinsics;
26+
mod visitor;
2627

2728
pub use rustc::mir::interpret::*; // have all the `interpret` symbols in one place: here
2829

@@ -38,4 +39,6 @@ pub use self::machine::{Machine, AllocMap, MayLeak};
3839

3940
pub use self::operand::{ScalarMaybeUndef, Immediate, ImmTy, Operand, OpTy};
4041

42+
pub use self::visitor::ValueVisitor;
43+
4144
pub use self::validity::RefTracking;

src/librustc_mir/interpret/operand.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//! All high-level functions to read from memory work on operands as sources.
1313
1414
use std::convert::TryInto;
15+
use std::fmt;
1516

1617
use rustc::{mir, ty};
1718
use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerExt};
@@ -36,6 +37,15 @@ impl<Tag> From<Scalar<Tag>> for ScalarMaybeUndef<Tag> {
3637
}
3738
}
3839

40+
impl<Tag> fmt::Display for ScalarMaybeUndef<Tag> {
41+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42+
match self {
43+
ScalarMaybeUndef::Undef => write!(f, "uninitialized bytes"),
44+
ScalarMaybeUndef::Scalar(s) => write!(f, "{}", s),
45+
}
46+
}
47+
}
48+
3949
impl<'tcx> ScalarMaybeUndef<()> {
4050
#[inline]
4151
pub fn with_default_tag<Tag>(self) -> ScalarMaybeUndef<Tag>
@@ -732,8 +742,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
732742
Ok(match rval.layout.variants {
733743
layout::Variants::Single { .. } => bug!(),
734744
layout::Variants::Tagged { .. } => {
745+
let bits_discr = match raw_discr.to_bits(discr_val.layout.size) {
746+
Ok(raw_discr) => raw_discr,
747+
Err(_) => return err!(InvalidDiscriminant(raw_discr.erase_tag())),
748+
};
735749
let real_discr = if discr_val.layout.ty.is_signed() {
736-
let i = raw_discr.to_bits(discr_val.layout.size)? as i128;
750+
let i = bits_discr as i128;
737751
// going from layout tag type to typeck discriminant type
738752
// requires first sign extending with the layout discriminant
739753
let shift = 128 - discr_val.layout.size.bits();
@@ -748,15 +762,15 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
748762
let truncatee = sexted as u128;
749763
(truncatee << shift) >> shift
750764
} else {
751-
raw_discr.to_bits(discr_val.layout.size)?
765+
bits_discr
752766
};
753767
// Make sure we catch invalid discriminants
754768
let index = rval.layout.ty
755769
.ty_adt_def()
756770
.expect("tagged layout for non adt")
757771
.discriminants(self.tcx.tcx)
758772
.position(|var| var.val == real_discr)
759-
.ok_or_else(|| EvalErrorKind::InvalidDiscriminant(real_discr))?;
773+
.ok_or_else(|| EvalErrorKind::InvalidDiscriminant(raw_discr.erase_tag()))?;
760774
(real_discr, index)
761775
},
762776
layout::Variants::NicheFilling {

0 commit comments

Comments
 (0)