Skip to content

Commit 2bd22c0

Browse files
committed
Auto merge of #787 - RalfJung:pointer-checks, r=RalfJung
adjust for refactored memory pointer checks The Miri side of rust-lang/rust#62081.
2 parents c65fbc4 + b66cf70 commit 2bd22c0

20 files changed

+107
-53
lines changed

rust-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
305930cffeac1da0fd73a08d9f5680e4a49bfb9f
1+
7e08576e4276a97b523c25bfd196d419c39c7b87

src/fn_call.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
654654

655655
// Hook pthread calls that go to the thread-local storage memory subsystem.
656656
"pthread_key_create" => {
657-
let key_ptr = this.read_scalar(args[0])?.to_ptr()?;
657+
let key_ptr = this.read_scalar(args[0])?.not_undef()?;
658658

659659
// Extract the function type out of the signature (that seems easier than constructing it ourselves).
660660
let dtor = match this.read_scalar(args[1])?.not_undef()? {
@@ -681,7 +681,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
681681
return err!(OutOfTls);
682682
}
683683

684-
this.memory().check_align(key_ptr.into(), key_layout.align.abi)?;
684+
let key_ptr = this.memory().check_ptr_access(key_ptr, key_layout.size, key_layout.align.abi)?
685+
.expect("cannot be a ZST");
685686
this.memory_mut().get_mut(key_ptr.alloc_id)?.write_scalar(
686687
tcx,
687688
key_ptr,

src/intrinsic.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -517,13 +517,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
517517
let val_byte = this.read_scalar(args[1])?.to_u8()?;
518518
let ptr = this.read_scalar(args[0])?.not_undef()?;
519519
let count = this.read_scalar(args[2])?.to_usize(this)?;
520-
this.memory().check_align(ptr, ty_layout.align.abi)?;
521520
let byte_count = ty_layout.size * count;
522-
if byte_count.bytes() != 0 {
523-
let ptr = ptr.to_ptr()?;
524-
this.memory_mut()
525-
.get_mut(ptr.alloc_id)?
526-
.write_repeat(tcx, ptr, val_byte, byte_count)?;
521+
match this.memory().check_ptr_access(ptr, byte_count, ty_layout.align.abi)? {
522+
Some(ptr) => {
523+
this.memory_mut()
524+
.get_mut(ptr.alloc_id)?
525+
.write_repeat(tcx, ptr, val_byte, byte_count)?;
526+
}
527+
None => {
528+
// Size is 0, nothing to do.
529+
}
527530
}
528531
}
529532

src/operator.rs

+28-13
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ use rustc::mir;
44
use crate::*;
55

66
pub trait EvalContextExt<'tcx> {
7+
fn pointer_inbounds(
8+
&self,
9+
ptr: Pointer<Tag>
10+
) -> InterpResult<'tcx>;
11+
712
fn ptr_op(
813
&self,
914
bin_op: mir::BinOp,
@@ -34,6 +39,13 @@ pub trait EvalContextExt<'tcx> {
3439
}
3540

3641
impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
42+
/// Test if the pointer is in-bounds of a live allocation.
43+
#[inline]
44+
fn pointer_inbounds(&self, ptr: Pointer<Tag>) -> InterpResult<'tcx> {
45+
let (size, _align) = self.memory().get_size_and_align(ptr.alloc_id, AllocCheck::Live)?;
46+
ptr.check_in_alloc(size, CheckInAllocMsg::InboundsTest)
47+
}
48+
3749
fn ptr_op(
3850
&self,
3951
bin_op: mir::BinOp,
@@ -114,8 +126,8 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
114126
let left = left.to_ptr().expect("we checked is_ptr");
115127
let right = right.to_bits(self.memory().pointer_size()).expect("we checked is_bits");
116128
let (_alloc_size, alloc_align) = self.memory()
117-
.get_size_and_align(left.alloc_id, InboundsCheck::MaybeDead)
118-
.expect("determining size+align of dead ptr cannot fail");
129+
.get_size_and_align(left.alloc_id, AllocCheck::MaybeDead)
130+
.expect("alloc info with MaybeDead cannot fail");
119131
let min_ptr_val = u128::from(alloc_align.bytes()) + u128::from(left.offset.bytes());
120132
let result = match bin_op {
121133
Gt => min_ptr_val > right,
@@ -170,6 +182,7 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
170182
if left.alloc_id == right.alloc_id {
171183
left.offset == right.offset
172184
} else {
185+
// Make sure both pointers are in-bounds.
173186
// This accepts one-past-the end. Thus, there is still technically
174187
// some non-determinism that we do not fully rule out when two
175188
// allocations sit right next to each other. The C/C++ standards are
@@ -179,10 +192,12 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
179192
// Dead allocations in miri cannot overlap with live allocations, but
180193
// on read hardware this can easily happen. Thus for comparisons we require
181194
// both pointers to be live.
182-
self.memory().check_bounds_ptr(left, InboundsCheck::Live, CheckInAllocMsg::InboundsTest)?;
183-
self.memory().check_bounds_ptr(right, InboundsCheck::Live, CheckInAllocMsg::InboundsTest)?;
184-
// Two in-bounds pointers, we can compare across allocations.
185-
left == right
195+
if self.pointer_inbounds(left).is_ok() && self.pointer_inbounds(right).is_ok() {
196+
// Two in-bounds pointers in different allocations are different.
197+
false
198+
} else {
199+
return err!(InvalidPointerMath);
200+
}
186201
}
187202
}
188203
// Comparing ptr and integer.
@@ -202,16 +217,16 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
202217
// alignment 32 or higher, hence the limit of 32.
203218
// FIXME: Once we support intptrcast, we could try to fix these holes.
204219
if bits < 32 {
205-
// Test if the ptr is in-bounds. Then it cannot be NULL.
206-
// Even dangling pointers cannot be NULL.
207-
if self.memory().check_bounds_ptr(ptr, InboundsCheck::MaybeDead, CheckInAllocMsg::NullPointerTest).is_ok() {
220+
// Test if the pointer can be different from NULL or not.
221+
// We assume that pointers that are not NULL are also not "small".
222+
if !self.memory().ptr_may_be_null(ptr) {
208223
return Ok(false);
209224
}
210225
}
211226

212227
let (alloc_size, alloc_align) = self.memory()
213-
.get_size_and_align(ptr.alloc_id, InboundsCheck::MaybeDead)
214-
.expect("determining size+align of dead ptr cannot fail");
228+
.get_size_and_align(ptr.alloc_id, AllocCheck::MaybeDead)
229+
.expect("alloc info with MaybeDead cannot fail");
215230

216231
// Case II: Alignment gives it away
217232
if ptr.offset.bytes() % alloc_align.bytes() == 0 {
@@ -359,9 +374,9 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
359374
if let Scalar::Ptr(ptr) = ptr {
360375
// Both old and new pointer must be in-bounds of a *live* allocation.
361376
// (Of the same allocation, but that part is trivial with our representation.)
362-
self.memory().check_bounds_ptr(ptr, InboundsCheck::Live, CheckInAllocMsg::InboundsTest)?;
377+
self.pointer_inbounds(ptr)?;
363378
let ptr = ptr.signed_offset(offset, self)?;
364-
self.memory().check_bounds_ptr(ptr, InboundsCheck::Live, CheckInAllocMsg::InboundsTest)?;
379+
self.pointer_inbounds(ptr)?;
365380
Ok(Scalar::Ptr(ptr))
366381
} else {
367382
// An integer pointer. They can only be offset by 0, and we pretend there

src/stacked_borrows.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc::mir::RetagKind;
1010

1111
use crate::{
1212
InterpResult, InterpError, MiriEvalContext, HelpersEvalContextExt, Evaluator, MutValueVisitor,
13-
MemoryKind, MiriMemoryKind, RangeMap, Allocation, AllocationExtra, AllocId, CheckInAllocMsg,
13+
MemoryKind, MiriMemoryKind, RangeMap, Allocation, AllocationExtra, AllocId,
1414
Pointer, Immediate, ImmTy, PlaceTy, MPlaceTy,
1515
};
1616

@@ -531,13 +531,14 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
531531
) -> InterpResult<'tcx> {
532532
let this = self.eval_context_mut();
533533
let protector = if protect { Some(this.frame().extra) } else { None };
534-
let ptr = place.ptr.to_ptr()?;
534+
let ptr = this.memory().check_ptr_access(place.ptr, size, place.align)
535+
.expect("validity checks should have excluded dangling/unaligned pointer")
536+
.expect("we shouldn't get here for ZST");
535537
trace!("reborrow: {} reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
536538
kind, new_tag, ptr.tag, place.layout.ty, ptr.erase_tag(), size.bytes());
537539

538540
// Get the allocation. It might not be mutable, so we cannot use `get_mut`.
539541
let alloc = this.memory().get(ptr.alloc_id)?;
540-
alloc.check_bounds(this, ptr, size, CheckInAllocMsg::InboundsTest)?;
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!

tests/compile-fail/ptr_eq_dangling.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ fn main() {
66
let y = &*b as *const i32; // different allocation
77
// We cannot compare these even though both are inbounds -- they *could* be
88
// equal if memory was reused.
9-
assert!(x != y); //~ ERROR dangling pointer
9+
assert!(x != y); //~ ERROR invalid arithmetic on pointers
1010
}

tests/compile-fail/ptr_eq_out_of_bounds.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ fn main() {
55
let y = &*b as *const i32; // different allocation
66
// We cannot compare these even though both allocations are live -- they *could* be
77
// equal (with the right base addresses).
8-
assert!(x != y); //~ ERROR outside bounds
8+
assert!(x != y); //~ ERROR invalid arithmetic on pointers
99
}

tests/compile-fail/rc_as_raw.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// This should fail even without validation
2+
// compile-flags: -Zmiri-disable-validation
13
#![feature(weak_into_raw)]
24

35
use std::rc::{Rc, Weak};

tests/compile-fail/unaligned_ptr1.rs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// This should fail even without validation
2+
// compile-flags: -Zmiri-disable-validation
3+
4+
fn main() {
5+
let x = [2u16, 3, 4]; // Make it big enough so we don't get an out-of-bounds error.
6+
let x = &x[0] as *const _ as *const u32;
7+
// This must fail because alignment is violated: the allocation's base is not sufficiently aligned.
8+
let _x = unsafe { *x }; //~ ERROR tried to access memory with alignment 2, but alignment 4 is required
9+
}

tests/compile-fail/unaligned_ptr2.rs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// This should fail even without validation.
2+
// compile-flags: -Zmiri-disable-validation
3+
4+
fn main() {
5+
let x = [2u32, 3]; // Make it big enough so we don't get an out-of-bounds error.
6+
let x = (x.as_ptr() as *const u8).wrapping_offset(3) as *const u32;
7+
// This must fail because alignment is violated: the offset is not sufficiently aligned.
8+
// Also make the offset not a power of 2, that used to ICE.
9+
let _x = unsafe { *x }; //~ ERROR tried to access memory with alignment 1, but alignment 4 is required
10+
}

tests/compile-fail/unaligned_ptr_cast2.rs renamed to tests/compile-fail/unaligned_ptr3.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
// compile-flags: -Zmiri-disable-validation
33

44
fn main() {
5-
let x = &2u16;
6-
let x = x as *const _ as *const *const u8;
5+
let x = [2u16, 3, 4, 5]; // Make it big enough so we don't get an out-of-bounds error.
6+
let x = &x[0] as *const _ as *const *const u8; // cast to ptr-to-ptr, so that we load a ptr
77
// This must fail because alignment is violated. Test specifically for loading pointers,
88
// which have special code in miri's memory.
99
let _x = unsafe { *x };

tests/compile-fail/unaligned_ptr_cast1.rs

-9
This file was deleted.

tests/compile-fail/unaligned_ptr_cast_zst.rs renamed to tests/compile-fail/unaligned_ptr_zst.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// This should fail even without validation
2+
// compile-flags: -Zmiri-disable-validation
3+
14
fn main() {
25
let x = &2u16;
36
let x = x as *const _ as *const [u32; 0];
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::mem;
22

33
fn main() {
4-
let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR tried to interpret some bytes as a pointer
4+
let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR integer pointer in non-ZST reference
55
}

tests/compile-fail/validity/dangling_ref2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ use std::mem;
33
fn main() {
44
let val = 14;
55
let ptr = (&val as *const i32).wrapping_offset(1);
6-
let _x: &i32 = unsafe { mem::transmute(ptr) }; //~ ERROR outside bounds of allocation
6+
let _x: &i32 = unsafe { mem::transmute(ptr) }; //~ ERROR encountered dangling (not entirely in bounds) reference
77
}

tests/compile-fail/zst.rs

-4
This file was deleted.

tests/compile-fail/zst1.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fn main() {
2+
// make sure ZST locals cannot be accessed
3+
let x = &() as *const () as *const i8;
4+
let _val = unsafe { *x }; //~ ERROR pointer must be in-bounds
5+
}

tests/compile-fail/zst2.rs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
fn main() {
2+
// Not using the () type here, as writes of that type do not even have MIR generated.
3+
// Also not assigning directly as that's array initialization, not assignment.
4+
let zst_val = [1u8; 0];
5+
6+
// make sure ZST accesses are checked against being "truly" dangling pointers
7+
// (into deallocated allocations).
8+
let mut x_box = Box::new(1u8);
9+
let x = &mut *x_box as *mut _ as *mut [u8; 0];
10+
drop(x_box);
11+
unsafe { *x = zst_val; } //~ ERROR dangling pointer was dereferenced
12+
}

tests/compile-fail/zst3.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
fn main() {
2+
// Not using the () type here, as writes of that type do not even have MIR generated.
3+
// Also not assigning directly as that's array initialization, not assignment.
4+
let zst_val = [1u8; 0];
5+
6+
// make sure ZST accesses are checked against being "truly" dangling pointers
7+
// (that are out-of-bounds).
8+
let mut x_box = Box::new(1u8);
9+
let x = (&mut *x_box as *mut u8).wrapping_offset(1);
10+
// This one is just "at the edge", but still okay
11+
unsafe { *(x as *mut [u8; 0]) = zst_val; }
12+
// One byte further is OOB.
13+
let x = x.wrapping_offset(1);
14+
unsafe { *(x as *mut [u8; 0]) = zst_val; } //~ ERROR pointer must be in-bounds
15+
}

tests/run-pass/zst.rs

-9
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,4 @@ fn main() {
2121
// Reading and writing is ok.
2222
unsafe { *x = zst_val; }
2323
unsafe { let _y = *x; }
24-
25-
// We should even be able to use "true" pointers for ZST when the allocation has been
26-
// removed already. The box is for a non-ZST to make sure there actually is an allocation.
27-
let mut x_box = Box::new(((), 1u8));
28-
let x = &mut x_box.0 as *mut _ as *mut [u8; 0];
29-
drop(x_box);
30-
// Reading and writing is ok.
31-
unsafe { *x = zst_val; }
32-
unsafe { let _y = *x; }
3324
}

0 commit comments

Comments
 (0)