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

4 byte pointers + tests #15

Merged
merged 5 commits into from
Jun 1, 2016
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ version = "0.1.0"
[[bin]]
doc = false
name = "miri"
test = false

[lib]
test = false

[dependencies]
byteorder = "0.4.2"
Expand Down
23 changes: 20 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
use std::error::Error;
use std::fmt;
use rustc::mir::repr as mir;
use memory::Pointer;

#[derive(Clone, Debug)]
pub enum EvalError {
DanglingPointerDeref,
InvalidBool,
InvalidDiscriminant,
PointerOutOfBounds,
PointerOutOfBounds {
ptr: Pointer,
size: usize,
allocation_size: usize,
},
ReadPointerAsBytes,
ReadBytesAsPointer,
InvalidPointerMath,
ReadUndefBytes,
InvalidBoolOp(mir::BinOp),
Unimplemented(String),
}

pub type EvalResult<T> = Result<T, EvalError>;
Expand All @@ -24,7 +32,7 @@ impl Error for EvalError {
"invalid boolean value read",
EvalError::InvalidDiscriminant =>
"invalid enum discriminant value read",
EvalError::PointerOutOfBounds =>
EvalError::PointerOutOfBounds { .. } =>
"pointer offset outside bounds of allocation",
EvalError::ReadPointerAsBytes =>
"a raw memory access tried to access part of a pointer value as raw bytes",
Expand All @@ -34,6 +42,9 @@ impl Error for EvalError {
"attempted to do math or a comparison on pointers into different allocations",
EvalError::ReadUndefBytes =>
"attempted to read undefined bytes",
EvalError::InvalidBoolOp(_) =>
"invalid boolean operation",
EvalError::Unimplemented(ref msg) => msg,
}
}

Expand All @@ -42,6 +53,12 @@ impl Error for EvalError {

impl fmt::Display for EvalError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
match *self {
EvalError::PointerOutOfBounds { ptr, size, allocation_size } => {
write!(f, "memory access of {}..{} outside bounds of allocation {} which has size {}",
ptr.offset, ptr.offset + size, ptr.alloc_id, allocation_size)
},
_ => write!(f, "{}", self.description()),
}
}
}
93 changes: 49 additions & 44 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,11 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> {
tcx: tcx,
mir_map: mir_map,
mir_cache: RefCell::new(DefIdMap()),
memory: Memory::new(),
memory: Memory::new(tcx.sess
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, it would be best if Memory had access to tcx.data_layout (the TargetDataLayout). This would also allow it to use primitive type alignments to diagnose unaligned memory accesses.

But you don't need to make that change now. I just wanted to make a note of it.

.target
.uint_type
.bit_width()
.expect("Session::target::uint_type was usize")/8),
substs_stack: Vec::new(),
name_stack: Vec::new(),
}
Expand Down Expand Up @@ -392,11 +396,11 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
TerminatorTarget::Call
}

abi => panic!("can't handle function with {:?} ABI", abi),
abi => return Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))),
}
}

_ => panic!("can't handle callee of type {:?}", func_ty),
_ => return Err(EvalError::Unimplemented(format!("can't handle callee of type {:?}", func_ty))),
}
}

Expand Down Expand Up @@ -470,7 +474,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
}

StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
let offset = self.nonnull_offset(adt_ty, nndiscr, discrfield);
let offset = self.nonnull_offset(adt_ty, nndiscr, discrfield)?;
let nonnull = adt_ptr.offset(offset.bytes() as isize);
self.read_nonnull_discriminant_value(nonnull, nndiscr)?
}
Expand Down Expand Up @@ -620,7 +624,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
self.memory.write_uint(dest, n * elem_size, dest_size)?;
}

_ => panic!("unimplemented: size_of_val::<{:?}>", ty),
_ => return Err(EvalError::Unimplemented(format!("unimplemented: size_of_val::<{:?}>", ty))),
}
}
}
Expand All @@ -631,7 +635,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
}
"uninit" => self.memory.mark_definedness(dest, dest_size, false)?,

name => panic!("can't handle intrinsic: {}", name),
name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))),
}

// Since we pushed no stack frame, the main loop will act
Expand Down Expand Up @@ -693,7 +697,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
self.memory.write_int(dest, result, dest_size)?;
}

_ => panic!("can't call C ABI function: {}", link_name),
_ => return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))),
}

// Since we pushed no stack frame, the main loop will act
Expand Down Expand Up @@ -748,7 +752,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
let ptr = self.eval_operand(operand)?;
let ty = self.operand_ty(operand);
let val = self.read_primval(ptr, ty)?;
self.memory.write_primval(dest, primval::unary_op(un_op, val))?;
self.memory.write_primval(dest, primval::unary_op(un_op, val)?)?;
}

Aggregate(ref kind, ref operands) => {
Expand Down Expand Up @@ -809,7 +813,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
try!(self.assign_fields(dest, offsets, operands));
} else {
assert_eq!(operands.len(), 0);
let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield);
let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield)?;
let dest = dest.offset(offset.bytes() as isize);
try!(self.memory.write_isize(dest, 0));
}
Expand All @@ -834,8 +838,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
}
}

_ => panic!("can't handle destination layout {:?} when assigning {:?}",
dest_layout, kind),
_ => return Err(EvalError::Unimplemented(format!("can't handle destination layout {:?} when assigning {:?}", dest_layout, kind))),
}
}

Expand Down Expand Up @@ -904,7 +907,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
self.memory.write_usize(len_ptr, length as u64)?;
}

_ => panic!("can't handle cast: {:?}", rvalue),
_ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))),
}
}

Expand All @@ -914,7 +917,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
self.memory.copy(src, dest, size)?;
}

_ => panic!("can't handle cast: {:?}", rvalue),
_ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))),
}
}

Expand All @@ -925,7 +928,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
Ok(())
}

fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> Size {
fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<Size> {
// Skip the constant 0 at the start meant for LLVM GEP.
let mut path = discrfield.iter().skip(1).map(|&i| i as usize);

Expand All @@ -946,49 +949,49 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
self.field_path_offset(inner_ty, path)
}

fn field_path_offset<I: Iterator<Item = usize>>(&self, mut ty: Ty<'tcx>, path: I) -> Size {
fn field_path_offset<I: Iterator<Item = usize>>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult<Size> {
let mut offset = Size::from_bytes(0);

// Skip the initial 0 intended for LLVM GEP.
for field_index in path {
let field_offset = self.get_field_offset(ty, field_index);
ty = self.get_field_ty(ty, field_index);
let field_offset = self.get_field_offset(ty, field_index)?;
ty = self.get_field_ty(ty, field_index)?;
offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap();
}

offset
Ok(offset)
}

fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> Ty<'tcx> {
fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<Ty<'tcx>> {
match ty.sty {
ty::TyStruct(adt_def, substs) => {
adt_def.struct_variant().fields[field_index].ty(self.tcx, substs)
Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs))
}

ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) |
ty::TyBox(ty) => {
assert_eq!(field_index, 0);
ty
Ok(ty)
}
_ => panic!("can't handle type: {:?}", ty),
_ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}", ty))),
}
}

fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> Size {
fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<Size> {
let layout = self.type_layout(ty);

use rustc::ty::layout::Layout::*;
match *layout {
Univariant { .. } => {
assert_eq!(field_index, 0);
Size::from_bytes(0)
Ok(Size::from_bytes(0))
}
FatPointer { .. } => {
let bytes = layout::FAT_PTR_ADDR * self.memory.pointer_size;
Size::from_bytes(bytes as u64)
Ok(Size::from_bytes(bytes as u64))
}
_ => panic!("can't handle type: {:?}, with layout: {:?}", ty, layout),
_ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, with layout: {:?}", ty, layout))),
}
}

Expand Down Expand Up @@ -1197,23 +1200,25 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {

pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<PrimVal> {
use syntax::ast::{IntTy, UintTy};
let val = match ty.sty {
ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?),
ty::TyInt(IntTy::I8) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8),
ty::TyInt(IntTy::I16) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16),
ty::TyInt(IntTy::I32) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32),
ty::TyInt(IntTy::I64) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64),
ty::TyUint(UintTy::U8) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8),
ty::TyUint(UintTy::U16) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16),
ty::TyUint(UintTy::U32) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32),
ty::TyUint(UintTy::U64) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64),

// TODO(solson): Pick the PrimVal dynamically.
ty::TyInt(IntTy::Is) => PrimVal::I64(self.memory.read_isize(ptr)?),
ty::TyUint(UintTy::Us) => PrimVal::U64(self.memory.read_usize(ptr)?),

ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => {
let val = match (self.memory.pointer_size, &ty.sty) {
(_, &ty::TyBool) => PrimVal::Bool(self.memory.read_bool(ptr)?),
(_, &ty::TyInt(IntTy::I8)) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8),
(2, &ty::TyInt(IntTy::Is)) |
(_, &ty::TyInt(IntTy::I16)) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16),
(4, &ty::TyInt(IntTy::Is)) |
(_, &ty::TyInt(IntTy::I32)) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32),
(8, &ty::TyInt(IntTy::Is)) |
(_, &ty::TyInt(IntTy::I64)) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64),
(_, &ty::TyUint(UintTy::U8)) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8),
(2, &ty::TyUint(UintTy::Us)) |
(_, &ty::TyUint(UintTy::U16)) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16),
(4, &ty::TyUint(UintTy::Us)) |
(_, &ty::TyUint(UintTy::U32)) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32),
(8, &ty::TyUint(UintTy::Us)) |
(_, &ty::TyUint(UintTy::U64)) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64),

(_, &ty::TyRef(_, ty::TypeAndMut { ty, .. })) |
(_, &ty::TyRawPtr(ty::TypeAndMut { ty, .. })) => {
if self.type_is_sized(ty) {
match self.memory.read_ptr(ptr) {
Ok(p) => PrimVal::AbstractPtr(p),
Expand All @@ -1223,7 +1228,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
Err(e) => return Err(e),
}
} else {
panic!("unimplemented: primitive read of fat pointer type: {:?}", ty);
return Err(EvalError::Unimplemented(format!("unimplemented: primitive read of fat pointer type: {:?}", ty)));
}
}

Expand Down
26 changes: 16 additions & 10 deletions src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,12 @@ pub struct Memory {
}

impl Memory {
pub fn new() -> Self {
// FIXME: pass tcx.data_layout (This would also allow it to use primitive type alignments to diagnose unaligned memory accesses.)
pub fn new(pointer_size: usize) -> Self {
Memory {
alloc_map: HashMap::new(),
next_id: AllocId(0),

// FIXME(solson): This should work for both 4 and 8, but it currently breaks some things
// when set to 4.
pointer_size: 8,
pointer_size: pointer_size,
}
}

Expand All @@ -80,7 +78,7 @@ impl Memory {
pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<()> {
if ptr.offset != 0 {
// TODO(solson): Report error about non-__rust_allocate'd pointer.
panic!()
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
}

let alloc = self.get_mut(ptr.alloc_id)?;
Expand All @@ -90,7 +88,7 @@ impl Memory {
alloc.bytes.extend(iter::repeat(0).take(amount));
alloc.undef_mask.grow(amount, false);
} else if size > new_size {
unimplemented!()
return Err(EvalError::Unimplemented(format!("unimplemented allocation relocation")));
// alloc.bytes.truncate(new_size);
// alloc.undef_mask.len = new_size;
// TODO: potentially remove relocations
Expand All @@ -103,7 +101,7 @@ impl Memory {
pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<()> {
if ptr.offset != 0 {
// TODO(solson): Report error about non-__rust_allocate'd pointer.
panic!()
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
}

if self.alloc_map.remove(&ptr.alloc_id).is_none() {
Expand Down Expand Up @@ -183,15 +181,23 @@ impl Memory {
fn get_bytes_unchecked(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> {
let alloc = self.get(ptr.alloc_id)?;
if ptr.offset + size > alloc.bytes.len() {
return Err(EvalError::PointerOutOfBounds);
return Err(EvalError::PointerOutOfBounds {
ptr: ptr,
size: size,
allocation_size: alloc.bytes.len(),
});
}
Ok(&alloc.bytes[ptr.offset..ptr.offset + size])
}

fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> {
let alloc = self.get_mut(ptr.alloc_id)?;
if ptr.offset + size > alloc.bytes.len() {
return Err(EvalError::PointerOutOfBounds);
return Err(EvalError::PointerOutOfBounds {
ptr: ptr,
size: size,
allocation_size: alloc.bytes.len(),
});
}
Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size])
}
Expand Down
Loading