diff --git a/src/error.rs b/src/error.rs index ec75ab445269..c82b7fa1ca61 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,7 +7,11 @@ pub enum EvalError { DanglingPointerDeref, InvalidBool, InvalidDiscriminant, - PointerOutOfBounds, + PointerOutOfBounds { + offset: usize, + size: usize, + len: usize, + }, ReadPointerAsBytes, ReadBytesAsPointer, InvalidPointerMath, @@ -27,7 +31,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", @@ -48,6 +52,9 @@ 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 { offset, size, len } => write!(f, "pointer offset ({} + {}) outside bounds ({}) of allocation", offset, size, len), + _ => write!(f, "{}", self.description()), + } } } diff --git a/src/interpreter.rs b/src/interpreter.rs index 8734427f07f5..bd8a5012b2e7 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -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(), } @@ -1196,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 { 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), + 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), - // 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, .. }) => { + (_, &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), diff --git a/src/memory.rs b/src/memory.rs index d69e07ae2a2b..7cc6a9a8e9d1 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -49,14 +49,11 @@ pub struct Memory { } impl Memory { - pub fn new() -> Self { + 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, } } @@ -183,7 +180,11 @@ 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 { + offset: ptr.offset, + size: size, + len: alloc.bytes.len(), + }); } Ok(&alloc.bytes[ptr.offset..ptr.offset + size]) } @@ -191,7 +192,11 @@ impl Memory { 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 { + offset: ptr.offset, + size: size, + len: alloc.bytes.len(), + }); } Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) } diff --git a/tests/compile-fail/errors.rs b/tests/compile-fail/errors.rs index 6ef9800a1ad0..1f7e3ce88c6d 100644 --- a/tests/compile-fail/errors.rs +++ b/tests/compile-fail/errors.rs @@ -6,7 +6,9 @@ fn overwriting_part_of_relocation_makes_the_rest_undefined() -> i32 { let mut p = &42; unsafe { let ptr: *mut _ = &mut p; - *(ptr as *mut u32) = 123; + *(ptr as *mut u8) = 123; // if we ever support 8 bit pointers, this is gonna cause + // "attempted to interpret some raw bytes as a pointer address" instead of + // "attempted to read undefined bytes" } *p //~ ERROR: attempted to read undefined bytes } @@ -34,7 +36,7 @@ fn undefined_byte_read() -> u8 { #[miri_run] fn out_of_bounds_read() -> u8 { let v: Vec = vec![1, 2]; - unsafe { *v.get_unchecked(5) } //~ ERROR: pointer offset outside bounds of allocation + unsafe { *v.get_unchecked(5) } //~ ERROR: pointer offset (5 + 1) outside bounds (2) of allocation } #[miri_run] diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 51634fd15bff..184e28241324 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -3,17 +3,24 @@ extern crate compiletest_rs as compiletest; use std::path::PathBuf; fn run_mode(mode: &'static str) { - let mut config = compiletest::default_config(); - config.rustc_path = "target/debug/miri".into(); - let path = std::env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); - config.target_rustcflags = Some(format!("--sysroot {}", path)); - config.host_rustcflags = Some(format!("--sysroot {}", path)); - let cfg_mode = mode.parse().ok().expect("Invalid mode"); + // FIXME: read directories in sysroot/lib/rustlib and generate the test targets from that + let targets = &["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"]; - config.mode = cfg_mode; - config.src_base = PathBuf::from(format!("tests/{}", mode)); + for &target in targets { + let mut config = compiletest::default_config(); + config.rustc_path = "target/debug/miri".into(); + let path = std::env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); + config.run_lib_path = format!("{}/lib/rustlib/{}/lib", path, target); + let path = format!("--sysroot {}", path); + config.target_rustcflags = Some(path.clone()); + config.host_rustcflags = Some(path); + let cfg_mode = mode.parse().ok().expect("Invalid mode"); - compiletest::run_tests(&config); + config.mode = cfg_mode; + config.src_base = PathBuf::from(format!("tests/{}", mode)); + config.target = target.to_owned(); + compiletest::run_tests(&config); + } } #[test] diff --git a/tests/run-pass/strings.rs b/tests/run-pass/strings.rs index 3d71fa4e09f8..809233511698 100644 --- a/tests/run-pass/strings.rs +++ b/tests/run-pass/strings.rs @@ -21,4 +21,9 @@ fn hello_bytes_fat() -> &'static [u8] { b"Hello, world!" } +#[miri_run] +fn fat_pointer_on_32_bit() { + Some(5).expect("foo"); +} + fn main() {}