Skip to content

Commit

Permalink
Merge pull request #15 from oli-obk/step_by_step
Browse files Browse the repository at this point in the history
4 byte pointers + tests
  • Loading branch information
solson committed Jun 1, 2016
2 parents f3923ee + f910019 commit c815170
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 87 deletions.
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
.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

0 comments on commit c815170

Please sign in to comment.