Skip to content

Commit ce5e910

Browse files
committed
Auto merge of #820 - RalfJung:intptrcast, r=RalfJung
Make most tests work with Intptrcast The one that still fails (amusingly, that's ptr_int_casts) requires some help from the librustc_mir side.
2 parents 39c9e79 + b29cb7d commit ce5e910

16 files changed

+109
-63
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ Several `-Z` flags are relevant for Miri:
279279
* `-Zalways-encode-mir` makes rustc dump MIR even for completely monomorphic
280280
functions. This is needed so that Miri can execute such functions, so Miri
281281
sets this flag per default.
282+
* `-Zmir-emit-retag` controls whether `Retag` statements are emitted. Miri
283+
enables this per default because it is needed for validation.
282284

283285
Moreover, Miri recognizes some environment variables:
284286

src/eval.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
3232
let mut ecx = InterpretCx::new(
3333
tcx.at(syntax::source_map::DUMMY_SP),
3434
ty::ParamEnv::reveal_all(),
35-
Evaluator::new(config.validate),
35+
Evaluator::new(),
3636
);
3737

3838
// FIXME: InterpretCx::new should take an initial MemoryExtra
39-
ecx.memory_mut().extra = MemoryExtra::with_rng(config.seed.map(StdRng::seed_from_u64));
39+
ecx.memory_mut().extra = MemoryExtra::new(config.seed.map(StdRng::seed_from_u64), config.validate);
4040

4141
let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
4242
let main_mir = ecx.load_mir(main_instance.def)?;

src/helpers.rs

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
5858
.map(|(size, _)| size)
5959
.unwrap_or_else(|| place.layout.size)
6060
);
61+
assert!(size.bytes() > 0);
6162
// Store how far we proceeded into the place so far. Everything to the left of
6263
// this offset has already been handled, in the sense that the frozen parts
6364
// have had `action` called on them.

src/intptrcast.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use std::cmp::max;
44

55
use rand::Rng;
66

7-
use rustc_mir::interpret::{AllocId, Pointer, InterpResult, Memory, AllocCheck};
7+
use rustc::ty::layout::HasDataLayout;
8+
use rustc_mir::interpret::{AllocId, Pointer, InterpResult, Memory, AllocCheck, PointerArithmetic};
89
use rustc_target::abi::Size;
910

1011
use crate::{Evaluator, Tag, STACK_ADDR};
@@ -75,7 +76,9 @@ impl<'mir, 'tcx> GlobalState {
7576
let mut global_state = memory.extra.intptrcast.borrow_mut();
7677
let global_state = &mut *global_state;
7778

78-
let (size, align) = memory.get_size_and_align(ptr.alloc_id, AllocCheck::Live)?;
79+
// There is nothing wrong with a raw pointer being cast to an integer only after
80+
// it became dangling. Hence `MaybeDead`.
81+
let (size, align) = memory.get_size_and_align(ptr.alloc_id, AllocCheck::MaybeDead)?;
7982

8083
let base_addr = match global_state.base_addr.entry(ptr.alloc_id) {
8184
Entry::Occupied(entry) => *entry.get(),
@@ -107,7 +110,9 @@ impl<'mir, 'tcx> GlobalState {
107110
};
108111

109112
debug_assert_eq!(base_addr % align.bytes(), 0); // sanity check
110-
Ok(base_addr + ptr.offset.bytes())
113+
// Add offset with the right kind of pointer-overflowing arithmetic.
114+
let dl = memory.data_layout();
115+
Ok(dl.overflowing_offset(base_addr, ptr.offset.bytes()).0)
111116
}
112117

113118
/// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple

src/machine.rs

+52-26
Original file line numberDiff line numberDiff line change
@@ -41,25 +41,31 @@ impl Into<MemoryKind<MiriMemoryKind>> for MiriMemoryKind {
4141
/// Extra per-allocation data
4242
#[derive(Debug, Clone)]
4343
pub struct AllocExtra {
44-
pub stacked_borrows: stacked_borrows::AllocExtra,
44+
/// Stacked Borrows state is only added if validation is enabled.
45+
pub stacked_borrows: Option<stacked_borrows::AllocExtra>,
4546
}
4647

4748
/// Extra global memory data
4849
#[derive(Default, Clone, Debug)]
4950
pub struct MemoryExtra {
5051
pub stacked_borrows: stacked_borrows::MemoryExtra,
5152
pub intptrcast: intptrcast::MemoryExtra,
53+
5254
/// The random number generator to use if Miri is running in non-deterministic mode and to
5355
/// enable intptrcast
54-
pub(crate) rng: Option<RefCell<StdRng>>
56+
pub(crate) rng: Option<RefCell<StdRng>>,
57+
58+
/// Whether to enforce the validity invariant.
59+
pub(crate) validate: bool,
5560
}
5661

5762
impl MemoryExtra {
58-
pub fn with_rng(rng: Option<StdRng>) -> Self {
63+
pub fn new(rng: Option<StdRng>, validate: bool) -> Self {
5964
MemoryExtra {
6065
stacked_borrows: Default::default(),
6166
intptrcast: Default::default(),
6267
rng: rng.map(RefCell::new),
68+
validate,
6369
}
6470
}
6571
}
@@ -82,21 +88,17 @@ pub struct Evaluator<'tcx> {
8288

8389
/// TLS state.
8490
pub(crate) tls: TlsData<'tcx>,
85-
86-
/// Whether to enforce the validity invariant.
87-
pub(crate) validate: bool,
8891
}
8992

9093
impl<'tcx> Evaluator<'tcx> {
91-
pub(crate) fn new(validate: bool) -> Self {
94+
pub(crate) fn new() -> Self {
9295
Evaluator {
9396
env_vars: HashMap::default(),
9497
argc: None,
9598
argv: None,
9699
cmd_line: None,
97100
last_error: 0,
98101
tls: TlsData::default(),
99-
validate,
100102
}
101103
}
102104
}
@@ -135,7 +137,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
135137

136138
#[inline(always)]
137139
fn enforce_validity(ecx: &InterpretCx<'mir, 'tcx, Self>) -> bool {
138-
ecx.machine.validate
140+
ecx.memory().extra.validate
139141
}
140142

141143
/// Returns `Ok()` when the function was handled; fail otherwise.
@@ -251,12 +253,17 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
251253
) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag) {
252254
let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
253255
let alloc = alloc.into_owned();
254-
let (stacks, base_tag) = Stacks::new_allocation(
255-
id,
256-
Size::from_bytes(alloc.bytes.len() as u64),
257-
Rc::clone(&memory.extra.stacked_borrows),
258-
kind,
259-
);
256+
let (stacks, base_tag) = if !memory.extra.validate {
257+
(None, Tag::Untagged)
258+
} else {
259+
let (stacks, base_tag) = Stacks::new_allocation(
260+
id,
261+
Size::from_bytes(alloc.bytes.len() as u64),
262+
Rc::clone(&memory.extra.stacked_borrows),
263+
kind,
264+
);
265+
(Some(stacks), base_tag)
266+
};
260267
if kind != MiriMemoryKind::Static.into() {
261268
assert!(alloc.relocations.is_empty(), "Only statics can come initialized with inner pointers");
262269
// Now we can rely on the inner pointers being static, too.
@@ -268,7 +275,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
268275
alloc.relocations.iter()
269276
// The allocations in the relocations (pointers stored *inside* this allocation)
270277
// all get the base pointer tag.
271-
.map(|&(offset, ((), alloc))| (offset, (memory_extra.static_base_ptr(alloc), alloc)))
278+
.map(|&(offset, ((), alloc))| {
279+
let tag = if !memory.extra.validate {
280+
Tag::Untagged
281+
} else {
282+
memory_extra.static_base_ptr(alloc)
283+
};
284+
(offset, (tag, alloc))
285+
})
272286
.collect()
273287
),
274288
undef_mask: alloc.undef_mask,
@@ -286,7 +300,11 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
286300
id: AllocId,
287301
memory: &Memory<'mir, 'tcx, Self>,
288302
) -> Self::PointerTag {
289-
memory.extra.stacked_borrows.borrow_mut().static_base_ptr(id)
303+
if !memory.extra.validate {
304+
Tag::Untagged
305+
} else {
306+
memory.extra.stacked_borrows.borrow_mut().static_base_ptr(id)
307+
}
290308
}
291309

292310
#[inline(always)]
@@ -295,12 +313,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
295313
kind: mir::RetagKind,
296314
place: PlaceTy<'tcx, Tag>,
297315
) -> InterpResult<'tcx> {
298-
if !ecx.tcx.sess.opts.debugging_opts.mir_emit_retag || !Self::enforce_validity(ecx) {
299-
// No tracking, or no retagging. The latter is possible because a dependency of ours
300-
// might be called with different flags than we are, so there are `Retag`
301-
// statements but we do not want to execute them.
302-
// Also, honor the whitelist in `enforce_validity` because otherwise we might retag
303-
// uninitialized data.
316+
if !Self::enforce_validity(ecx) {
317+
// No tracking.
304318
Ok(())
305319
} else {
306320
ecx.retag(kind, place)
@@ -354,7 +368,11 @@ impl AllocationExtra<Tag> for AllocExtra {
354368
ptr: Pointer<Tag>,
355369
size: Size,
356370
) -> InterpResult<'tcx> {
357-
alloc.extra.stacked_borrows.memory_read(ptr, size)
371+
if let Some(ref stacked_borrows) = alloc.extra.stacked_borrows {
372+
stacked_borrows.memory_read(ptr, size)
373+
} else {
374+
Ok(())
375+
}
358376
}
359377

360378
#[inline(always)]
@@ -363,7 +381,11 @@ impl AllocationExtra<Tag> for AllocExtra {
363381
ptr: Pointer<Tag>,
364382
size: Size,
365383
) -> InterpResult<'tcx> {
366-
alloc.extra.stacked_borrows.memory_written(ptr, size)
384+
if let Some(ref mut stacked_borrows) = alloc.extra.stacked_borrows {
385+
stacked_borrows.memory_written(ptr, size)
386+
} else {
387+
Ok(())
388+
}
367389
}
368390

369391
#[inline(always)]
@@ -372,7 +394,11 @@ impl AllocationExtra<Tag> for AllocExtra {
372394
ptr: Pointer<Tag>,
373395
size: Size,
374396
) -> InterpResult<'tcx> {
375-
alloc.extra.stacked_borrows.memory_deallocated(ptr, size)
397+
if let Some(ref mut stacked_borrows) = alloc.extra.stacked_borrows {
398+
stacked_borrows.memory_deallocated(ptr, size)
399+
} else {
400+
Ok(())
401+
}
376402
}
377403
}
378404

src/operator.rs

+31-19
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,12 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
5656

5757
trace!("ptr_op: {:?} {:?} {:?}", *left, bin_op, *right);
5858

59-
// If intptrcast is enabled and the operation is not an offset
60-
// we can force the cast from pointers to integer addresses and
61-
// then dispatch to rustc binary operation method
62-
if self.memory().extra.rng.is_some() && bin_op != Offset {
59+
// If intptrcast is enabled, treat everything of integer *type* at integer *value*.
60+
if self.memory().extra.rng.is_some() && left.layout.ty.is_integral() {
61+
// This is actually an integer operation, so dispatch back to the core engine.
62+
// TODO: Once intptrcast is the default, librustc_mir should never even call us
63+
// for integer types.
64+
assert!(right.layout.ty.is_integral());
6365
let l_bits = self.force_bits(left.imm.to_scalar()?, left.layout.size)?;
6466
let r_bits = self.force_bits(right.imm.to_scalar()?, right.layout.size)?;
6567

@@ -186,6 +188,13 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
186188
right: Scalar<Tag>,
187189
) -> InterpResult<'tcx, bool> {
188190
let size = self.pointer_size();
191+
if self.memory().extra.rng.is_some() {
192+
// Just compare the integers.
193+
// TODO: Do we really want to *always* do that, even when comparing two live in-bounds pointers?
194+
let left = self.force_bits(left, size)?;
195+
let right = self.force_bits(right, size)?;
196+
return Ok(left == right);
197+
}
189198
Ok(match (left, right) {
190199
(Scalar::Raw { .. }, Scalar::Raw { .. }) =>
191200
left.to_bits(size)? == right.to_bits(size)?,
@@ -388,21 +397,24 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
388397
.checked_mul(pointee_size)
389398
.ok_or_else(|| InterpError::Overflow(mir::BinOp::Mul))?;
390399
// Now let's see what kind of pointer this is.
391-
if let Scalar::Ptr(ptr) = ptr {
392-
// Both old and new pointer must be in-bounds of a *live* allocation.
393-
// (Of the same allocation, but that part is trivial with our representation.)
394-
self.pointer_inbounds(ptr)?;
395-
let ptr = ptr.signed_offset(offset, self)?;
396-
self.pointer_inbounds(ptr)?;
397-
Ok(Scalar::Ptr(ptr))
398-
} else {
399-
// An integer pointer. They can only be offset by 0, and we pretend there
400-
// is a little zero-sized allocation here.
401-
if offset == 0 {
402-
Ok(ptr)
403-
} else {
404-
err!(InvalidPointerMath)
400+
let ptr = if offset == 0 {
401+
match ptr {
402+
Scalar::Ptr(ptr) => ptr,
403+
Scalar::Raw { .. } => {
404+
// Offset 0 on an integer. We accept that, pretending there is
405+
// a little zero-sized allocation here.
406+
return Ok(ptr);
407+
}
405408
}
406-
}
409+
} else {
410+
// Offset > 0. We *require* a pointer.
411+
self.force_ptr(ptr)?
412+
};
413+
// Both old and new pointer must be in-bounds of a *live* allocation.
414+
// (Of the same allocation, but that part is trivial with our representation.)
415+
self.pointer_inbounds(ptr)?;
416+
let ptr = ptr.signed_offset(offset, self)?;
417+
self.pointer_inbounds(ptr)?;
418+
Ok(Scalar::Ptr(ptr))
407419
}
408420
}

src/stacked_borrows.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
538538

539539
// Get the allocation. It might not be mutable, so we cannot use `get_mut`.
540540
let alloc = this.memory().get(ptr.alloc_id)?;
541+
let stacked_borrows = alloc.extra.stacked_borrows.as_ref().expect("we should have Stacked Borrows data");
541542
// Update the stacks.
542543
// Make sure that raw pointers and mutable shared references are reborrowed "weak":
543544
// There could be existing unique pointers reborrowed from them that should remain valid!
@@ -553,14 +554,14 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
553554
// We are only ever `SharedReadOnly` inside the frozen bits.
554555
let perm = if frozen { Permission::SharedReadOnly } else { Permission::SharedReadWrite };
555556
let item = Item { perm, tag: new_tag, protector };
556-
alloc.extra.stacked_borrows.for_each(cur_ptr, size, |stack, global| {
557+
stacked_borrows.for_each(cur_ptr, size, |stack, global| {
557558
stack.grant(cur_ptr.tag, item, global)
558559
})
559560
});
560561
}
561562
};
562563
let item = Item { perm, tag: new_tag, protector };
563-
alloc.extra.stacked_borrows.for_each(ptr, size, |stack, global| {
564+
stacked_borrows.for_each(ptr, size, |stack, global| {
564565
stack.grant(ptr.tag, item, global)
565566
})
566567
}

tests/compile-fail/ptr_offset_int_plus_int.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// error-pattern: invalid arithmetic on pointers
1+
// error-pattern: tried to interpret some bytes as a pointer
22

33
fn main() {
44
// Can't offset an integer pointer by non-zero offset.

tests/compiletest.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ fn compile_fail(path: &str, target: &str, opt: bool) {
6969
run_tests("compile-fail", path, target, flags);
7070
}
7171

72-
fn miri_pass(path: &str, target: &str, opt: bool) {
72+
fn miri_pass(path: &str, target: &str, opt: bool, noseed: bool) {
7373
let opt_str = if opt { " with optimizations" } else { "" };
7474
eprintln!("{}", format!(
7575
"## Running run-pass tests in {} against miri for target {}{}",
@@ -81,6 +81,9 @@ fn miri_pass(path: &str, target: &str, opt: bool) {
8181
let mut flags = Vec::new();
8282
if opt {
8383
flags.push("-Zmir-opt-level=3".to_owned());
84+
} else if !noseed {
85+
// Run with intptrcast. Avoid test matrix explosion by doing either this or opt-level=3.
86+
flags.push("-Zmiri-seed=".to_owned());
8487
}
8588

8689
run_tests("ui", path, target, flags);
@@ -104,7 +107,8 @@ fn get_target() -> String {
104107
}
105108

106109
fn run_pass_miri(opt: bool) {
107-
miri_pass("tests/run-pass", &get_target(), opt);
110+
miri_pass("tests/run-pass", &get_target(), opt, false);
111+
miri_pass("tests/run-pass-noseed", &get_target(), opt, true);
108112
}
109113

110114
fn compile_fail_miri(opt: bool) {
File renamed without changes.
File renamed without changes.

tests/run-pass/intptrcast_format.rs

-6
This file was deleted.

tests/run-pass/intptrcast_format.stdout

-2
This file was deleted.

tests/run-pass/transmute_fat.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Validation disallows this becuase the reference is never cast to a raw pointer.
2+
// compile-flags: -Zmiri-disable-validation
3+
14
fn main() {
25
// If we are careful, we can exploit data layout...
36
let raw = unsafe {

0 commit comments

Comments
 (0)