diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 0506335dc221fa2786903a7e929238325cf5cc2a..c1e1fff999fd3a74395888077e69eb2ee06743d0 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -535,11 +535,9 @@ fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<' }; let src = self.eval_operand(operand)?; - src.check_align(elem_align)?; - dest.check_align(elem_align)?; for i in 0..length { let elem_dest = dest.offset((i * elem_size) as isize); - self.memory.copy(src, elem_dest, elem_size)?; + self.memory.copy(src, elem_dest, elem_size, elem_align)?; } } @@ -603,17 +601,17 @@ fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<' let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); // FIXME(solson): Wrong for almost everything. - // FIXME: check alignment warn!("misc cast from {:?} to {:?}", src_ty, dest_ty); let dest_size = self.type_size(dest_ty); let src_size = self.type_size(src_ty); + let dest_align = self.type_align(dest_ty); // Hack to support fat pointer -> thin pointer casts to keep tests for // other things passing for now. let is_fat_ptr_cast = pointee_type(src_ty).map_or(false, |ty| !self.type_is_sized(ty)); if dest_size == src_size || is_fat_ptr_cast { - self.memory.copy(src, dest, dest_size)?; + self.memory.copy(src, dest, dest_size, dest_align)?; } else { return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))); } @@ -858,9 +856,7 @@ fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> Ty<'tcx> { fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { let size = self.type_size(ty); let align = self.type_align(ty); - src.check_align(align)?; - dest.check_align(align)?; - self.memory.copy(src, dest, size)?; + self.memory.copy(src, dest, size, align)?; Ok(()) } diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index d5b7dbaf623868ecdba8dcbd3a05041e3599ac32..568d8358f0874d6c3a93cf80610c909669896e3c 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -291,11 +291,9 @@ fn call_intrinsic( let elem_size = self.type_size(elem_ty); let elem_align = self.type_align(elem_ty); let src = self.memory.read_ptr(args_ptrs[0])?; - src.check_align(elem_align)?; let dest = self.memory.read_ptr(args_ptrs[1])?; - dest.check_align(elem_align)?; let count = self.memory.read_isize(args_ptrs[2])?; - self.memory.copy(src, dest, count as usize * elem_size)?; + self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; } "discriminant_value" => { diff --git a/src/memory.rs b/src/memory.rs index d4f8eb58f2f72b00260d22d6d05407d0c4915707..91ae90da8b6f6b767709b3764b9a2810b355c1f4 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -51,25 +51,6 @@ fn zst_ptr() -> Self { offset: 0, } } - pub fn is_aligned_to(&self, align: usize) -> bool { - self.offset % align == 0 - } - pub fn check_align(&self, align: usize) -> EvalResult<'static, ()> { - if self.is_aligned_to(align) { - Ok(()) - } else { - let mut best = self.offset; - let mut i = 1; - while best > 0 && (best & 1 == 0) { - best >>= 1; - i <<= 1; - } - Err(EvalError::AlignmentCheckFailed { - required: align, - has: i, - }) - } - } } #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] @@ -118,11 +99,11 @@ pub fn new(layout: &'a TargetDataLayout, max_memory: usize) -> Self { bytes: Vec::new(), relocations: BTreeMap::new(), undef_mask: UndefMask::new(0), - align: 0, + align: 1, }; mem.alloc_map.insert(ZST_ALLOC_ID, alloc); // check that additional zst allocs work - debug_assert!(mem.allocate(0, 0).unwrap().points_to_zst()); + debug_assert!(mem.allocate(0, 1).unwrap().points_to_zst()); debug_assert!(mem.get(ZST_ALLOC_ID).is_ok()); mem } @@ -155,24 +136,22 @@ pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty } pub fn allocate(&mut self, size: usize, align: usize) -> EvalResult<'tcx, Pointer> { + assert!(align != 0); if size == 0 { return Ok(Pointer::zst_ptr()); } - // make sure we can offset the result pointer by the worst possible alignment - // this allows cheaply checking for alignment directly in the pointer - let least_aligned_size = size + align; if self.memory_size - self.memory_usage < size { return Err(EvalError::OutOfMemory { - allocation_size: least_aligned_size, + allocation_size: size, memory_size: self.memory_size, memory_usage: self.memory_usage, }); } self.memory_usage += size; let alloc = Allocation { - bytes: vec![0; least_aligned_size], + bytes: vec![0; size], relocations: BTreeMap::new(), - undef_mask: UndefMask::new(least_aligned_size), + undef_mask: UndefMask::new(size), align: align, }; let id = self.next_id; @@ -180,15 +159,15 @@ pub fn allocate(&mut self, size: usize, align: usize) -> EvalResult<'tcx, Pointe self.alloc_map.insert(id, alloc); Ok(Pointer { alloc_id: id, - // offset by the alignment, so larger accesses will fail - offset: align, + offset: 0, }) } // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. pub fn reallocate(&mut self, ptr: Pointer, new_size: usize, align: usize) -> EvalResult<'tcx, Pointer> { - if ptr.offset != self.get(ptr.alloc_id)?.align { + // TODO(solson): Report error about non-__rust_allocate'd pointer. + if ptr.offset != 0 { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } @@ -197,27 +176,26 @@ pub fn reallocate(&mut self, ptr: Pointer, new_size: usize, align: usize) -> Eva } let size = self.get(ptr.alloc_id)?.bytes.len(); - let least_aligned_size = new_size + align; - if least_aligned_size > size { - let amount = least_aligned_size - size; + if new_size > size { + let amount = new_size - size; self.memory_usage += amount; let alloc = self.get_mut(ptr.alloc_id)?; alloc.bytes.extend(iter::repeat(0).take(amount)); alloc.undef_mask.grow(amount, false); - } else if size > least_aligned_size { + } else if size > new_size { // it's possible to cause miri to use arbitrary amounts of memory that aren't detectable // through the memory_usage value, by allocating a lot and reallocating to zero - self.memory_usage -= size - least_aligned_size; - self.clear_relocations(ptr.offset(least_aligned_size as isize), size - least_aligned_size)?; + self.memory_usage -= size - new_size; + self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?; let alloc = self.get_mut(ptr.alloc_id)?; - alloc.bytes.truncate(least_aligned_size); - alloc.undef_mask.truncate(least_aligned_size); + alloc.bytes.truncate(new_size); + alloc.undef_mask.truncate(new_size); } Ok(Pointer { alloc_id: ptr.alloc_id, - offset: align, + offset: 0, }) } @@ -226,7 +204,7 @@ pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<'tcx, ()> { if ptr.points_to_zst() { return Ok(()); } - if ptr.offset != self.get(ptr.alloc_id)?.align { + if ptr.offset != 0 { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } @@ -251,6 +229,24 @@ pub fn pointer_size(&self) -> usize { pub fn endianess(&self) -> layout::Endian { self.layout.endian } + + pub fn check_align(&self, ptr: Pointer, align: usize) -> EvalResult<'tcx, ()> { + let alloc = self.get(ptr.alloc_id)?; + if alloc.align < align { + return Err(EvalError::AlignmentCheckFailed { + has: alloc.align, + required: align, + }); + } + if ptr.offset % align == 0 { + Ok(()) + } else { + Err(EvalError::AlignmentCheckFailed { + has: ptr.offset % align, + required: align, + }) + } + } } /// Allocation accessors @@ -368,7 +364,8 @@ fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<' Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) } - fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { + fn get_bytes(&self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &[u8]> { + self.check_align(ptr, align)?; if self.relocations(ptr, size)?.count() != 0 { return Err(EvalError::ReadPointerAsBytes); } @@ -376,7 +373,8 @@ fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { self.get_bytes_unchecked(ptr, size) } - fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &mut [u8]> { + fn get_bytes_mut(&mut self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &mut [u8]> { + self.check_align(ptr, align)?; self.clear_relocations(ptr, size)?; self.mark_definedness(ptr, size, true)?; self.get_bytes_unchecked_mut(ptr, size) @@ -385,11 +383,11 @@ fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &mut /// Reading and writing impl<'a, 'tcx> Memory<'a, 'tcx> { - pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<'tcx, ()> { + pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize, align: usize) -> EvalResult<'tcx, ()> { self.check_relocation_edges(src, size)?; let src_bytes = self.get_bytes_unchecked_mut(src, size)?.as_mut_ptr(); - let dest_bytes = self.get_bytes_mut(dest, size)?.as_mut_ptr(); + let dest_bytes = self.get_bytes_mut(dest, size, align)?.as_mut_ptr(); // SAFE: The above indexing would have panicked if there weren't at least `size` bytes // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and @@ -409,17 +407,17 @@ pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<' } pub fn read_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { - self.get_bytes(ptr, size) + self.get_bytes(ptr, size, 1) } pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<'tcx, ()> { - let bytes = self.get_bytes_mut(ptr, src.len())?; + let bytes = self.get_bytes_mut(ptr, src.len(), 1)?; bytes.clone_from_slice(src); Ok(()) } pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: usize) -> EvalResult<'tcx, ()> { - let bytes = self.get_bytes_mut(ptr, count)?; + let bytes = self.get_bytes_mut(ptr, count, 1)?; for b in bytes { *b = val; } Ok(()) } @@ -465,8 +463,7 @@ pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, } pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> { - ptr.check_align(self.layout.i1_align.abi() as usize)?; - let bytes = self.get_bytes(ptr, 1)?; + let bytes = self.get_bytes(ptr, 1, self.layout.i1_align.abi() as usize)?; match bytes[0] { 0 => Ok(false), 1 => Ok(true), @@ -475,42 +472,43 @@ pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> { } pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<'tcx, ()> { - ptr.check_align(self.layout.i1_align.abi() as usize)?; - self.get_bytes_mut(ptr, 1).map(|bytes| bytes[0] = b as u8) + let align = self.layout.i1_align.abi() as usize; + self.get_bytes_mut(ptr, 1, align) + .map(|bytes| bytes[0] = b as u8) } - fn check_int_align(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, ()> { + fn int_align(&self, size: usize) -> EvalResult<'tcx, usize> { match size { - 1 => ptr.check_align(self.layout.i8_align.abi() as usize), - 2 => ptr.check_align(self.layout.i16_align.abi() as usize), - 4 => ptr.check_align(self.layout.i32_align.abi() as usize), - 8 => ptr.check_align(self.layout.i64_align.abi() as usize), + 1 => Ok(self.layout.i8_align.abi() as usize), + 2 => Ok(self.layout.i16_align.abi() as usize), + 4 => Ok(self.layout.i32_align.abi() as usize), + 8 => Ok(self.layout.i64_align.abi() as usize), _ => panic!("bad integer size"), } } pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, i64> { - self.check_int_align(ptr, size)?; - self.get_bytes(ptr, size).map(|b| read_target_int(self.endianess(), b).unwrap()) + let align = self.int_align(size)?; + self.get_bytes(ptr, size, align).map(|b| read_target_int(self.endianess(), b).unwrap()) } pub fn write_int(&mut self, ptr: Pointer, n: i64, size: usize) -> EvalResult<'tcx, ()> { - self.check_int_align(ptr, size)?; + let align = self.int_align(size)?; let endianess = self.endianess(); - let b = self.get_bytes_mut(ptr, size)?; + let b = self.get_bytes_mut(ptr, size, align)?; write_target_int(endianess, b, n).unwrap(); Ok(()) } pub fn read_uint(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, u64> { - self.check_int_align(ptr, size)?; - self.get_bytes(ptr, size).map(|b| read_target_uint(self.endianess(), b).unwrap()) + let align = self.int_align(size)?; + self.get_bytes(ptr, size, align).map(|b| read_target_uint(self.endianess(), b).unwrap()) } pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: usize) -> EvalResult<'tcx, ()> { - self.check_int_align(ptr, size)?; + let align = self.int_align(size)?; let endianess = self.endianess(); - let b = self.get_bytes_mut(ptr, size)?; + let b = self.get_bytes_mut(ptr, size, align)?; write_target_uint(endianess, b, n).unwrap(); Ok(()) } @@ -534,29 +532,29 @@ pub fn write_usize(&mut self, ptr: Pointer, n: u64) -> EvalResult<'tcx, ()> { } pub fn write_f32(&mut self, ptr: Pointer, f: f32) -> EvalResult<'tcx, ()> { - ptr.check_align(self.layout.f32_align.abi() as usize)?; let endianess = self.endianess(); - let b = self.get_bytes_mut(ptr, 4)?; + let align = self.layout.f32_align.abi() as usize; + let b = self.get_bytes_mut(ptr, 4, align)?; write_target_f32(endianess, b, f).unwrap(); Ok(()) } pub fn write_f64(&mut self, ptr: Pointer, f: f64) -> EvalResult<'tcx, ()> { - ptr.check_align(self.layout.f64_align.abi() as usize)?; let endianess = self.endianess(); - let b = self.get_bytes_mut(ptr, 8)?; + let align = self.layout.f64_align.abi() as usize; + let b = self.get_bytes_mut(ptr, 8, align)?; write_target_f64(endianess, b, f).unwrap(); Ok(()) } pub fn read_f32(&self, ptr: Pointer) -> EvalResult<'tcx, f32> { - ptr.check_align(self.layout.f32_align.abi() as usize)?; - self.get_bytes(ptr, 4).map(|b| read_target_f32(self.endianess(), b).unwrap()) + self.get_bytes(ptr, 4, self.layout.f32_align.abi() as usize) + .map(|b| read_target_f32(self.endianess(), b).unwrap()) } pub fn read_f64(&self, ptr: Pointer) -> EvalResult<'tcx, f64> { - ptr.check_align(self.layout.f64_align.abi() as usize)?; - self.get_bytes(ptr, 8).map(|b| read_target_f64(self.endianess(), b).unwrap()) + self.get_bytes(ptr, 8, self.layout.f64_align.abi() as usize) + .map(|b| read_target_f64(self.endianess(), b).unwrap()) } } diff --git a/tests/compile-fail/oom.rs b/tests/compile-fail/oom.rs index d5f021bda704fe6a2c160e1aeb7afdb2ac044d1d..83109a77e62016961643d2434aac5178dc05f023 100644 --- a/tests/compile-fail/oom.rs +++ b/tests/compile-fail/oom.rs @@ -6,6 +6,6 @@ fn bar() { assert_eq!(x, 6); } -fn main() { //~ ERROR tried to allocate 8 more bytes, but only 0 bytes are free of the 0 byte memory +fn main() { //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 0 byte memory bar(); } diff --git a/tests/compile-fail/oom2.rs b/tests/compile-fail/oom2.rs index 65ef0dd0b7daa52d3255a3f7a500fb48fbbc7d37..63c51dbaa7d2689cef85d3ee94a14272d7a241aa 100644 --- a/tests/compile-fail/oom2.rs +++ b/tests/compile-fail/oom2.rs @@ -3,7 +3,7 @@ fn bar(i: i32) { if i < 1000 { - bar(i + 1) //~ ERROR tried to allocate 8 more bytes, but only 1 bytes are free of the 1000 byte memory + bar(i + 1) //~ ERROR tried to allocate 4 more bytes, but only 1 bytes are free of the 1000 byte memory //~^NOTE inside call to bar //~|NOTE inside call to bar //~|NOTE inside call to bar diff --git a/tests/compile-fail/out_of_bounds_read.rs b/tests/compile-fail/out_of_bounds_read.rs index 29355992e44c06f114a63518af726c45ffb034cc..f6a305840c241be8213fbad1c5c11532e3f27a35 100644 --- a/tests/compile-fail/out_of_bounds_read.rs +++ b/tests/compile-fail/out_of_bounds_read.rs @@ -1,5 +1,5 @@ fn main() { let v: Vec = vec![1, 2]; - let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: which has size 3 + let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: which has size 2 panic!("this should never print: {}", x); } diff --git a/tests/compile-fail/out_of_bounds_read2.rs b/tests/compile-fail/out_of_bounds_read2.rs index 388ed8b130639162c5a36d719851988907b309ca..5509a8346e552b323f16fb755ab46cd8aff3ce9c 100644 --- a/tests/compile-fail/out_of_bounds_read2.rs +++ b/tests/compile-fail/out_of_bounds_read2.rs @@ -1,5 +1,5 @@ fn main() { let v: Vec = vec![1, 2]; - let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 6..7 outside bounds of allocation + let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 5..6 outside bounds of allocation panic!("this should never print: {}", x); }