Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adjust for refactored memory pointer checks #787

Merged
merged 5 commits into from
Jun 24, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/fn_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx

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

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

this.memory().check_align(key_ptr.into(), key_layout.align.abi)?;
let key_ptr = this.memory().check_ptr_access(key_ptr, key_layout.size, key_layout.align.abi)?
.expect("cannot be a ZST");
this.memory_mut().get_mut(key_ptr.alloc_id)?.write_scalar(
tcx,
key_ptr,
Expand Down
15 changes: 9 additions & 6 deletions src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,13 +517,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let val_byte = this.read_scalar(args[1])?.to_u8()?;
let ptr = this.read_scalar(args[0])?.not_undef()?;
let count = this.read_scalar(args[2])?.to_usize(this)?;
this.memory().check_align(ptr, ty_layout.align.abi)?;
let byte_count = ty_layout.size * count;
if byte_count.bytes() != 0 {
let ptr = ptr.to_ptr()?;
this.memory_mut()
.get_mut(ptr.alloc_id)?
.write_repeat(tcx, ptr, val_byte, byte_count)?;
match this.memory().check_ptr_access(ptr, byte_count, ty_layout.align.abi)? {
Some(ptr) => {
this.memory_mut()
.get_mut(ptr.alloc_id)?
.write_repeat(tcx, ptr, val_byte, byte_count)?;
}
None => {
// Size is 0, nothing to do.
}
}
}

Expand Down
41 changes: 28 additions & 13 deletions src/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ use rustc::mir;
use crate::*;

pub trait EvalContextExt<'tcx> {
fn pointer_inbounds(
&self,
ptr: Pointer<Tag>
) -> InterpResult<'tcx>;

fn ptr_op(
&self,
bin_op: mir::BinOp,
Expand Down Expand Up @@ -34,6 +39,13 @@ pub trait EvalContextExt<'tcx> {
}

impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
/// Test if the pointer is in-bounds of a live allocation.
#[inline]
fn pointer_inbounds(&self, ptr: Pointer<Tag>) -> InterpResult<'tcx> {
let (size, _align) = self.memory().get_size_and_align(ptr.alloc_id, AllocCheck::Live)?;
ptr.check_in_alloc(size, CheckInAllocMsg::InboundsTest)
}

fn ptr_op(
&self,
bin_op: mir::BinOp,
Expand Down Expand Up @@ -114,8 +126,8 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
let left = left.to_ptr().expect("we checked is_ptr");
let right = right.to_bits(self.memory().pointer_size()).expect("we checked is_bits");
let (_alloc_size, alloc_align) = self.memory()
.get_size_and_align(left.alloc_id, InboundsCheck::MaybeDead)
.expect("determining size+align of dead ptr cannot fail");
.get_size_and_align(left.alloc_id, AllocCheck::MaybeDead)
.expect("alloc info with MaybeDead cannot fail");
let min_ptr_val = u128::from(alloc_align.bytes()) + u128::from(left.offset.bytes());
let result = match bin_op {
Gt => min_ptr_val > right,
Expand Down Expand Up @@ -170,6 +182,7 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
if left.alloc_id == right.alloc_id {
left.offset == right.offset
} else {
// Make sure both pointers are in-bounds.
// This accepts one-past-the end. Thus, there is still technically
// some non-determinism that we do not fully rule out when two
// allocations sit right next to each other. The C/C++ standards are
Expand All @@ -179,10 +192,12 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
// Dead allocations in miri cannot overlap with live allocations, but
// on read hardware this can easily happen. Thus for comparisons we require
// both pointers to be live.
self.memory().check_bounds_ptr(left, InboundsCheck::Live, CheckInAllocMsg::InboundsTest)?;
self.memory().check_bounds_ptr(right, InboundsCheck::Live, CheckInAllocMsg::InboundsTest)?;
// Two in-bounds pointers, we can compare across allocations.
left == right
if self.pointer_inbounds(left).is_ok() && self.pointer_inbounds(right).is_ok() {
// Two in-bounds pointers in different allocatons are different.
false
} else {
return err!(InvalidPointerMath);
}
}
}
// Comparing ptr and integer.
Expand All @@ -202,16 +217,16 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
// alignment 32 or higher, hence the limit of 32.
// FIXME: Once we support intptrcast, we could try to fix these holes.
if bits < 32 {
// Test if the ptr is in-bounds. Then it cannot be NULL.
// Even dangling pointers cannot be NULL.
if self.memory().check_bounds_ptr(ptr, InboundsCheck::MaybeDead, CheckInAllocMsg::NullPointerTest).is_ok() {
// Test if the pointer can be different from NULL or not.
// We assume that pointers that are not NULL are also not "small".
if !self.memory().ptr_may_be_null(ptr) {
return Ok(false);
}
}

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

// Case II: Alignment gives it away
if ptr.offset.bytes() % alloc_align.bytes() == 0 {
Expand Down Expand Up @@ -359,9 +374,9 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
if let Scalar::Ptr(ptr) = ptr {
// Both old and new pointer must be in-bounds of a *live* allocation.
// (Of the same allocation, but that part is trivial with our representation.)
self.memory().check_bounds_ptr(ptr, InboundsCheck::Live, CheckInAllocMsg::InboundsTest)?;
self.pointer_inbounds(ptr)?;
let ptr = ptr.signed_offset(offset, self)?;
self.memory().check_bounds_ptr(ptr, InboundsCheck::Live, CheckInAllocMsg::InboundsTest)?;
self.pointer_inbounds(ptr)?;
Ok(Scalar::Ptr(ptr))
} else {
// An integer pointer. They can only be offset by 0, and we pretend there
Expand Down
7 changes: 4 additions & 3 deletions src/stacked_borrows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc::mir::RetagKind;

use crate::{
InterpResult, InterpError, MiriEvalContext, HelpersEvalContextExt, Evaluator, MutValueVisitor,
MemoryKind, MiriMemoryKind, RangeMap, Allocation, AllocationExtra, AllocId, CheckInAllocMsg,
MemoryKind, MiriMemoryKind, RangeMap, Allocation, AllocationExtra, AllocId,
Pointer, Immediate, ImmTy, PlaceTy, MPlaceTy,
};

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

// Get the allocation. It might not be mutable, so we cannot use `get_mut`.
let alloc = this.memory().get(ptr.alloc_id)?;
alloc.check_bounds(this, ptr, size, CheckInAllocMsg::InboundsTest)?;
// Update the stacks.
// Make sure that raw pointers and mutable shared references are reborrowed "weak":
// There could be existing unique pointers reborrowed from them that should remain valid!
Expand Down
2 changes: 1 addition & 1 deletion tests/compile-fail/ptr_eq_dangling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ fn main() {
let y = &*b as *const i32; // different allocation
// We cannot compare these even though both are inbounds -- they *could* be
// equal if memory was reused.
assert!(x != y); //~ ERROR dangling pointer
assert!(x != y); //~ ERROR invalid arithmetic on pointers
}
2 changes: 1 addition & 1 deletion tests/compile-fail/ptr_eq_out_of_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ fn main() {
let y = &*b as *const i32; // different allocation
// We cannot compare these even though both allocations are live -- they *could* be
// equal (with the right base addresses).
assert!(x != y); //~ ERROR outside bounds
assert!(x != y); //~ ERROR invalid arithmetic on pointers
}
2 changes: 2 additions & 0 deletions tests/compile-fail/rc_as_raw.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// This should fail even without validation
// compile-flags: -Zmiri-disable-validation
#![feature(weak_into_raw)]

use std::rc::{Rc, Weak};
Expand Down
9 changes: 9 additions & 0 deletions tests/compile-fail/unaligned_ptr1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// This should fail even without validation
// compile-flags: -Zmiri-disable-validation

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

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

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

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// This should fail even without validation
// compile-flags: -Zmiri-disable-validation

fn main() {
let x = &2u16;
let x = x as *const _ as *const [u32; 0];
Expand Down
2 changes: 1 addition & 1 deletion tests/compile-fail/validity/dangling_ref1.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::mem;

fn main() {
let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR tried to interpret some bytes as a pointer
let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR integer pointer in non-ZST reference
}
2 changes: 1 addition & 1 deletion tests/compile-fail/validity/dangling_ref2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ use std::mem;
fn main() {
let val = 14;
let ptr = (&val as *const i32).wrapping_offset(1);
let _x: &i32 = unsafe { mem::transmute(ptr) }; //~ ERROR outside bounds of allocation
let _x: &i32 = unsafe { mem::transmute(ptr) }; //~ ERROR encountered dangling (not entirely in bounds) reference
}
4 changes: 0 additions & 4 deletions tests/compile-fail/zst.rs

This file was deleted.

5 changes: 5 additions & 0 deletions tests/compile-fail/zst1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fn main() {
// make sure ZST locals cannot be accessed
let x = &() as *const () as *const i8;
let _val = unsafe { *x }; //~ ERROR pointer must be in-bounds
}
12 changes: 12 additions & 0 deletions tests/compile-fail/zst2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
fn main() {
// Not using the () type here, as writes of that type do not even have MIR generated.
// Also not assigning directly as that's array initialization, not assignment.
let zst_val = [1u8; 0];

// make sure ZST accesses are checked against being "truly" dangling pointers
// (into deallocated allocations).
let mut x_box = Box::new(1u8);
let x = &mut *x_box as *mut _ as *mut [u8; 0];
drop(x_box);
unsafe { *x = zst_val; } //~ ERROR dangling pointer was dereferenced
}
15 changes: 15 additions & 0 deletions tests/compile-fail/zst3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
fn main() {
// Not using the () type here, as writes of that type do not even have MIR generated.
// Also not assigning directly as that's array initialization, not assignment.
let zst_val = [1u8; 0];

// make sure ZST accesses are checked against being "truly" dangling pointers
// (that are out-of-bounds).
let mut x_box = Box::new(1u8);
let x = (&mut *x_box as *mut u8).wrapping_offset(1);
// This one is just "at the egde", but still okay
unsafe { *(x as *mut [u8; 0]) = zst_val; }
// One byte further is OOB.
let x = x.wrapping_offset(1);
unsafe { *(x as *mut [u8; 0]) = zst_val; } //~ ERROR pointer must be in-bounds
}
9 changes: 0 additions & 9 deletions tests/run-pass/zst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,4 @@ fn main() {
// Reading and writing is ok.
unsafe { *x = zst_val; }
unsafe { let _y = *x; }

// We should even be able to use "true" pointers for ZST when the allocation has been
// removed already. The box is for a non-ZST to make sure there actually is an allocation.
let mut x_box = Box::new(((), 1u8));
let x = &mut x_box.0 as *mut _ as *mut [u8; 0];
drop(x_box);
// Reading and writing is ok.
unsafe { *x = zst_val; }
unsafe { let _y = *x; }
}