Skip to content

Commit 725199c

Browse files
committed
Improve miri's error reporting in check_in_alloc
1 parent 9ebf478 commit 725199c

File tree

8 files changed

+82
-42
lines changed

8 files changed

+82
-42
lines changed

src/librustc/mir/interpret/allocation.rs

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use super::{
77

88
use crate::ty::layout::{Size, Align};
99
use syntax::ast::Mutability;
10-
use std::iter;
10+
use std::{iter, fmt::{self, Display}};
1111
use crate::mir;
1212
use std::ops::{Deref, DerefMut};
1313
use rustc_data_structures::sorted_map::SortedMap;
@@ -22,6 +22,44 @@ pub enum InboundsCheck {
2222
MaybeDead,
2323
}
2424

25+
/// Used by `check_in_alloc` to indicate whether the pointer needs to be just inbounds
26+
/// or also inbounds of a *live* allocation.
27+
#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)]
28+
pub enum CheckInAllocMsg {
29+
ReadCStr,
30+
CheckBytes,
31+
WriteBytes,
32+
WriteRepeat,
33+
ReadScalar,
34+
WriteScalar,
35+
SlicePatCoveredByConst,
36+
ReadDiscriminant,
37+
CheckAlign,
38+
ReadBytes,
39+
CopyRepeatedly,
40+
CheckBounds,
41+
}
42+
43+
impl Display for CheckInAllocMsg {
44+
45+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46+
write!(f, "{}", match *self {
47+
CheckInAllocMsg::ReadCStr => "read C str",
48+
CheckInAllocMsg::CheckBytes => "check bytes",
49+
CheckInAllocMsg::WriteBytes => "write bytes",
50+
CheckInAllocMsg::WriteRepeat => "write repeat",
51+
CheckInAllocMsg::ReadScalar => "read scalar",
52+
CheckInAllocMsg::WriteScalar => "write scalar",
53+
CheckInAllocMsg::SlicePatCoveredByConst => "slice pat covered by const",
54+
CheckInAllocMsg::ReadDiscriminant => "read discriminant",
55+
CheckInAllocMsg::CheckAlign => "check align",
56+
CheckInAllocMsg::ReadBytes => "read bytes",
57+
CheckInAllocMsg::CopyRepeatedly => "copy repeatedly",
58+
CheckInAllocMsg::CheckBounds => "check bounds",
59+
})
60+
}
61+
}
62+
2563
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
2664
pub struct Allocation<Tag=(),Extra=()> {
2765
/// The actual bytes of the allocation.
@@ -140,9 +178,10 @@ impl<'tcx, Tag, Extra> Allocation<Tag, Extra> {
140178
fn check_bounds_ptr(
141179
&self,
142180
ptr: Pointer<Tag>,
181+
msg: CheckInAllocMsg,
143182
) -> EvalResult<'tcx> {
144183
let allocation_size = self.bytes.len() as u64;
145-
ptr.check_in_alloc(Size::from_bytes(allocation_size), InboundsCheck::Live)
184+
ptr.check_in_alloc(Size::from_bytes(allocation_size), msg)
146185
}
147186

148187
/// Checks if the memory range beginning at `ptr` and of size `Size` is "in-bounds".
@@ -152,9 +191,10 @@ impl<'tcx, Tag, Extra> Allocation<Tag, Extra> {
152191
cx: &impl HasDataLayout,
153192
ptr: Pointer<Tag>,
154193
size: Size,
194+
msg: CheckInAllocMsg,
155195
) -> EvalResult<'tcx> {
156196
// if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
157-
self.check_bounds_ptr(ptr.offset(size, cx)?)
197+
self.check_bounds_ptr(ptr.offset(size, cx)?, msg)
158198
}
159199
}
160200

@@ -173,11 +213,12 @@ impl<'tcx, Tag: Copy, Extra> Allocation<Tag, Extra> {
173213
ptr: Pointer<Tag>,
174214
size: Size,
175215
check_defined_and_ptr: bool,
216+
msg: CheckInAllocMsg,
176217
) -> EvalResult<'tcx, &[u8]>
177218
// FIXME: Working around https://github.com/rust-lang/rust/issues/56209
178219
where Extra: AllocationExtra<Tag, MemoryExtra>
179220
{
180-
self.check_bounds(cx, ptr, size)?;
221+
self.check_bounds(cx, ptr, size, msg)?;
181222

182223
if check_defined_and_ptr {
183224
self.check_defined(ptr, size)?;
@@ -201,11 +242,12 @@ impl<'tcx, Tag: Copy, Extra> Allocation<Tag, Extra> {
201242
cx: &impl HasDataLayout,
202243
ptr: Pointer<Tag>,
203244
size: Size,
245+
msg: CheckInAllocMsg,
204246
) -> EvalResult<'tcx, &[u8]>
205247
// FIXME: Working around https://github.com/rust-lang/rust/issues/56209
206248
where Extra: AllocationExtra<Tag, MemoryExtra>
207249
{
208-
self.get_bytes_internal(cx, ptr, size, true)
250+
self.get_bytes_internal(cx, ptr, size, true, msg)
209251
}
210252

211253
/// It is the caller's responsibility to handle undefined and pointer bytes.
@@ -216,11 +258,12 @@ impl<'tcx, Tag: Copy, Extra> Allocation<Tag, Extra> {
216258
cx: &impl HasDataLayout,
217259
ptr: Pointer<Tag>,
218260
size: Size,
261+
msg: CheckInAllocMsg,
219262
) -> EvalResult<'tcx, &[u8]>
220263
// FIXME: Working around https://github.com/rust-lang/rust/issues/56209
221264
where Extra: AllocationExtra<Tag, MemoryExtra>
222265
{
223-
self.get_bytes_internal(cx, ptr, size, false)
266+
self.get_bytes_internal(cx, ptr, size, false, msg)
224267
}
225268

226269
/// Just calling this already marks everything as defined and removes relocations,
@@ -230,12 +273,13 @@ impl<'tcx, Tag: Copy, Extra> Allocation<Tag, Extra> {
230273
cx: &impl HasDataLayout,
231274
ptr: Pointer<Tag>,
232275
size: Size,
276+
msg: CheckInAllocMsg,
233277
) -> EvalResult<'tcx, &mut [u8]>
234278
// FIXME: Working around https://github.com/rust-lang/rust/issues/56209
235279
where Extra: AllocationExtra<Tag, MemoryExtra>
236280
{
237281
assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`");
238-
self.check_bounds(cx, ptr, size)?;
282+
self.check_bounds(cx, ptr, size, msg)?;
239283

240284
self.mark_definedness(ptr, size, true)?;
241285
self.clear_relocations(cx, ptr, size)?;
@@ -269,7 +313,7 @@ impl<'tcx, Tag: Copy, Extra> Allocation<Tag, Extra> {
269313
// Go through `get_bytes` for checks and AllocationExtra hooks.
270314
// We read the null, so we include it in the request, but we want it removed
271315
// from the result!
272-
Ok(&self.get_bytes(cx, ptr, size_with_null)?[..size])
316+
Ok(&self.get_bytes(cx, ptr, size_with_null, CheckInAllocMsg::ReadCStr)?[..size])
273317
}
274318
None => err!(UnterminatedCString(ptr.erase_tag())),
275319
}
@@ -289,7 +333,7 @@ impl<'tcx, Tag: Copy, Extra> Allocation<Tag, Extra> {
289333
where Extra: AllocationExtra<Tag, MemoryExtra>
290334
{
291335
// Check bounds and relocations on the edges
292-
self.get_bytes_with_undef_and_ptr(cx, ptr, size)?;
336+
self.get_bytes_with_undef_and_ptr(cx, ptr, size, CheckInAllocMsg::CheckBytes)?;
293337
// Check undef and ptr
294338
if !allow_ptr_and_undef {
295339
self.check_defined(ptr, size)?;
@@ -310,7 +354,7 @@ impl<'tcx, Tag: Copy, Extra> Allocation<Tag, Extra> {
310354
// FIXME: Working around https://github.com/rust-lang/rust/issues/56209
311355
where Extra: AllocationExtra<Tag, MemoryExtra>
312356
{
313-
let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(src.len() as u64))?;
357+
let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(src.len() as u64), CheckInAllocMsg::WriteBytes)?;
314358
bytes.clone_from_slice(src);
315359
Ok(())
316360
}
@@ -326,7 +370,7 @@ impl<'tcx, Tag: Copy, Extra> Allocation<Tag, Extra> {
326370
// FIXME: Working around https://github.com/rust-lang/rust/issues/56209
327371
where Extra: AllocationExtra<Tag, MemoryExtra>
328372
{
329-
let bytes = self.get_bytes_mut(cx, ptr, count)?;
373+
let bytes = self.get_bytes_mut(cx, ptr, count, CheckInAllocMsg::WriteRepeat)?;
330374
for b in bytes {
331375
*b = val;
332376
}
@@ -351,7 +395,7 @@ impl<'tcx, Tag: Copy, Extra> Allocation<Tag, Extra> {
351395
where Extra: AllocationExtra<Tag, MemoryExtra>
352396
{
353397
// get_bytes_unchecked tests relocation edges
354-
let bytes = self.get_bytes_with_undef_and_ptr(cx, ptr, size)?;
398+
let bytes = self.get_bytes_with_undef_and_ptr(cx, ptr, size, CheckInAllocMsg::ReadScalar)?;
355399
// Undef check happens *after* we established that the alignment is correct.
356400
// We must not return Ok() for unaligned pointers!
357401
if self.check_defined(ptr, size).is_err() {
@@ -428,7 +472,7 @@ impl<'tcx, Tag: Copy, Extra> Allocation<Tag, Extra> {
428472
};
429473

430474
let endian = cx.data_layout().endian;
431-
let dst = self.get_bytes_mut(cx, ptr, type_size)?;
475+
let dst = self.get_bytes_mut(cx, ptr, type_size, CheckInAllocMsg::WriteScalar)?;
432476
write_target_uint(endian, dst, bytes).unwrap();
433477

434478
// See if we have to also write a relocation

src/librustc/mir/interpret/error.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::ty::layout::{Size, Align, LayoutError};
88
use rustc_target::spec::abi::Abi;
99
use rustc_macros::HashStable;
1010

11-
use super::{RawConst, Pointer, InboundsCheck, ScalarMaybeUndef};
11+
use super::{RawConst, Pointer, CheckInAllocMsg, ScalarMaybeUndef};
1212

1313
use backtrace::Backtrace;
1414

@@ -243,7 +243,7 @@ pub enum EvalErrorKind<'tcx, O> {
243243
InvalidDiscriminant(ScalarMaybeUndef),
244244
PointerOutOfBounds {
245245
ptr: Pointer,
246-
check: InboundsCheck,
246+
msg: CheckInAllocMsg,
247247
allocation_size: Size,
248248
},
249249
InvalidNullPointerUsage,
@@ -460,13 +460,9 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for EvalErrorKind<'tcx, O> {
460460
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461461
use self::EvalErrorKind::*;
462462
match *self {
463-
PointerOutOfBounds { ptr, check, allocation_size } => {
463+
PointerOutOfBounds { ptr, msg, allocation_size } => {
464464
write!(f, "Pointer must be in-bounds{} at offset {}, but is outside bounds of \
465-
allocation {} which has size {}",
466-
match check {
467-
InboundsCheck::Live => " and live",
468-
InboundsCheck::MaybeDead => "",
469-
},
465+
allocation {} which has size {}", msg,
470466
ptr.offset.bytes(), ptr.alloc_id, allocation_size.bytes())
471467
},
472468
ValidationFailure(ref err) => {

src/librustc/mir/interpret/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub use self::value::{Scalar, ScalarMaybeUndef, RawConst, ConstValue};
1919

2020
pub use self::allocation::{
2121
InboundsCheck, Allocation, AllocationExtra,
22-
Relocations, UndefMask,
22+
Relocations, UndefMask, CheckInAllocMsg,
2323
};
2424

2525
pub use self::pointer::{Pointer, PointerArithmetic};

src/librustc/mir/interpret/pointer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::ty::layout::{self, HasDataLayout, Size};
33
use rustc_macros::HashStable;
44

55
use super::{
6-
AllocId, EvalResult, InboundsCheck,
6+
AllocId, EvalResult, CheckInAllocMsg
77
};
88

99
////////////////////////////////////////////////////////////////////////////////
@@ -157,12 +157,12 @@ impl<'tcx, Tag> Pointer<Tag> {
157157
pub fn check_in_alloc(
158158
self,
159159
allocation_size: Size,
160-
check: InboundsCheck,
160+
msg: CheckInAllocMsg,
161161
) -> EvalResult<'tcx, ()> {
162162
if self.offset > allocation_size {
163163
err!(PointerOutOfBounds {
164164
ptr: self.erase_tag(),
165-
check,
165+
msg,
166166
allocation_size,
167167
})
168168
} else {

src/librustc_mir/hair/pattern/_match.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Const};
172172
use rustc::ty::layout::{Integer, IntegerExt, VariantIdx, Size};
173173

174174
use rustc::mir::Field;
175-
use rustc::mir::interpret::{ConstValue, Scalar, truncate};
175+
use rustc::mir::interpret::{ConstValue, Scalar, truncate, CheckInAllocMsg};
176176
use rustc::util::common::ErrorReported;
177177

178178
use syntax::attr::{SignedInt, UnsignedInt};
@@ -1418,7 +1418,7 @@ fn slice_pat_covered_by_const<'tcx>(
14181418
return Ok(false);
14191419
}
14201420
let n = n.assert_usize(tcx).unwrap();
1421-
alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap()
1421+
alloc.get_bytes(&tcx, ptr, Size::from_bytes(n), CheckInAllocMsg::SlicePatCoveredByConst).unwrap()
14221422
},
14231423
// a slice fat pointer to a zero length slice
14241424
(ConstValue::Slice(Scalar::Bits { .. }, 0), ty::Slice(t)) => {
@@ -1443,7 +1443,7 @@ fn slice_pat_covered_by_const<'tcx>(
14431443
tcx.alloc_map
14441444
.lock()
14451445
.unwrap_memory(ptr.alloc_id)
1446-
.get_bytes(&tcx, ptr, Size::from_bytes(n))
1446+
.get_bytes(&tcx, ptr, Size::from_bytes(n), CheckInAllocMsg::SlicePatCoveredByConst)
14471447
.unwrap()
14481448
},
14491449
_ => bug!(

src/librustc_mir/interpret/memory.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use syntax::ast::Mutability;
2020
use super::{
2121
Pointer, AllocId, Allocation, GlobalId, AllocationExtra,
2222
EvalResult, Scalar, EvalErrorKind, AllocKind, PointerArithmetic,
23-
Machine, AllocMap, MayLeak, ErrorHandled, InboundsCheck,
23+
Machine, AllocMap, MayLeak, ErrorHandled, InboundsCheck, CheckInAllocMsg,
2424
};
2525

2626
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
@@ -252,7 +252,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
252252
Scalar::Ptr(ptr) => {
253253
// check this is not NULL -- which we can ensure only if this is in-bounds
254254
// of some (potentially dead) allocation.
255-
let align = self.check_bounds_ptr(ptr, InboundsCheck::MaybeDead)?;
255+
let align = self.check_bounds_ptr(ptr, CheckInAllocMsg::CheckAlign)?;
256256
(ptr.offset.bytes(), align)
257257
}
258258
Scalar::Bits { bits, size } => {
@@ -292,10 +292,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
292292
pub fn check_bounds_ptr(
293293
&self,
294294
ptr: Pointer<M::PointerTag>,
295-
liveness: InboundsCheck,
295+
msg: CheckInAllocMsg,
296296
) -> EvalResult<'tcx, Align> {
297-
let (allocation_size, align) = self.get_size_and_align(ptr.alloc_id, liveness)?;
298-
ptr.check_in_alloc(allocation_size, liveness)?;
297+
let (allocation_size, align) = self.get_size_and_align(ptr.alloc_id, msg)?;
298+
ptr.check_in_alloc(allocation_size, msg)?;
299299
Ok(align)
300300
}
301301
}
@@ -423,7 +423,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
423423
pub fn get_size_and_align(
424424
&self,
425425
id: AllocId,
426-
liveness: InboundsCheck,
426+
msg: CheckInAllocMsg,
427427
) -> EvalResult<'static, (Size, Align)> {
428428
if let Ok(alloc) = self.get(id) {
429429
return Ok((Size::from_bytes(alloc.bytes.len() as u64), alloc.align));
@@ -439,7 +439,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
439439
let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
440440
Ok((layout.size, layout.align.abi))
441441
}
442-
_ => match liveness {
442+
_ => match msg {
443443
InboundsCheck::MaybeDead => {
444444
// Must be a deallocated pointer
445445
Ok(*self.dead_alloc_map.get(&id).expect(
@@ -604,7 +604,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
604604
Ok(&[])
605605
} else {
606606
let ptr = ptr.to_ptr()?;
607-
self.get(ptr.alloc_id)?.get_bytes(self, ptr, size)
607+
self.get(ptr.alloc_id)?.get_bytes(self, ptr, size, CheckInAllocMsg::ReadBytes)
608608
}
609609
}
610610
}
@@ -729,10 +729,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
729729

730730
// This checks relocation edges on the src.
731731
let src_bytes = self.get(src.alloc_id)?
732-
.get_bytes_with_undef_and_ptr(&tcx, src, size)?
732+
.get_bytes_with_undef_and_ptr(&tcx, src, size, CheckInAllocMsg::CopyRepeatedly)?
733733
.as_ptr();
734734
let dest_bytes = self.get_mut(dest.alloc_id)?
735-
.get_bytes_mut(&tcx, dest, size * length)?
735+
.get_bytes_mut(&tcx, dest, size * length, CheckInAllocMsg::CopyRepeatedly)?
736736
.as_mut_ptr();
737737

738738
// SAFE: The above indexing would have panicked if there weren't at least `size` bytes

src/librustc_mir/interpret/operand.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc::{mir, ty};
77
use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerExt, VariantIdx};
88

99
use rustc::mir::interpret::{
10-
GlobalId, AllocId, InboundsCheck,
10+
GlobalId, AllocId, CheckInAllocMsg,
1111
ConstValue, Pointer, Scalar,
1212
EvalResult, EvalErrorKind,
1313
sign_extend, truncate,
@@ -667,7 +667,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
667667
ScalarMaybeUndef::Scalar(Scalar::Ptr(ptr)) => {
668668
// The niche must be just 0 (which an inbounds pointer value never is)
669669
let ptr_valid = niche_start == 0 && variants_start == variants_end &&
670-
self.memory.check_bounds_ptr(ptr, InboundsCheck::MaybeDead).is_ok();
670+
self.memory.check_bounds_ptr(ptr, CheckInAllocMsg::ReadDiscriminant).is_ok();
671671
if !ptr_valid {
672672
return err!(InvalidDiscriminant(raw_discr.erase_tag()));
673673
}

src/librustc_mir/interpret/validity.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc::ty::layout::{self, Size, Align, TyLayout, LayoutOf, VariantIdx};
77
use rustc::ty;
88
use rustc_data_structures::fx::FxHashSet;
99
use rustc::mir::interpret::{
10-
Scalar, AllocKind, EvalResult, EvalErrorKind,
10+
Scalar, AllocKind, EvalResult, EvalErrorKind, CheckInAllocMsg,
1111
};
1212

1313
use super::{
@@ -394,7 +394,7 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
394394
try_validation!(
395395
self.ecx.memory
396396
.get(ptr.alloc_id)?
397-
.check_bounds(self.ecx, ptr, size),
397+
.check_bounds(self.ecx, ptr, size, CheckInAllocMsg::CheckBounds),
398398
"dangling (not entirely in bounds) reference", self.path);
399399
}
400400
// Check if we have encountered this pointer+layout combination

0 commit comments

Comments
 (0)