From 3c0a6d6922db63981d18de7e1dbd9842648d7227 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Jul 2017 13:34:54 -0700 Subject: [PATCH 1/5] simplify reallocate --- src/memory.rs | 47 +++---------------- .../compile-fail/reallocate-bad-alignment.rs | 2 +- 2 files changed, 8 insertions(+), 41 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index c638fabbe63..64549f45b39 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -224,6 +224,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // 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, old_size: u64, new_size: u64, align: u64) -> EvalResult<'tcx, Pointer> { + use std::cmp::min; + assert!(align.is_power_of_two()); // TODO(solson): Report error about non-__rust_allocate'd pointer. if ptr.offset != 0 || self.get(ptr.alloc_id).is_err() { @@ -233,39 +235,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Err(EvalError::ReallocatedStaticMemory); } - let size = self.get(ptr.alloc_id)?.bytes.len() as u64; - let real_align = self.get(ptr.alloc_id)?.align; - if size != old_size || real_align != align { - return Err(EvalError::IncorrectAllocationInformation); - } + // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc" + let new_ptr = self.allocate(new_size, align)?; + self.copy(PrimVal::Ptr(ptr), PrimVal::Ptr(new_ptr), min(old_size, new_size), align, /*nonoverlapping*/true)?; + self.deallocate(ptr, Some((old_size, align)))?; - if new_size > size { - let amount = new_size - size; - self.memory_usage += amount; - let alloc = self.get_mut(ptr.alloc_id)?; - assert_eq!(amount as usize as u64, amount); - alloc.bytes.extend(iter::repeat(0).take(amount as usize)); - alloc.undef_mask.grow(amount, false); - } else if size > new_size { - self.memory_usage -= size - new_size; - self.clear_relocations(ptr.offset(new_size, self.layout)?, size - new_size)?; - let alloc = self.get_mut(ptr.alloc_id)?; - // `as usize` is fine here, since it is smaller than `size`, which came from a usize - alloc.bytes.truncate(new_size as usize); - alloc.bytes.shrink_to_fit(); - alloc.undef_mask.truncate(new_size); - } - - // Change allocation ID. We do this after the above to be able to re-use methods like `clear_relocations`. - let id = { - let alloc = self.alloc_map.remove(&ptr.alloc_id).expect("We already used this pointer above"); - let id = self.next_id; - self.next_id.0 += 1; - self.alloc_map.insert(id, alloc); - id - }; - - Ok(Pointer::new(id, 0)) + Ok(new_ptr) } // TODO(solson): See comment on `reallocate`. @@ -1138,14 +1113,6 @@ impl UndefMask { self.len += amount; self.set_range_inbounds(start, start + amount, new_state); } - - fn truncate(&mut self, length: u64) { - self.len = length; - let truncate = self.len / BLOCK_SIZE + 1; - assert_eq!(truncate as usize as u64, truncate); - self.blocks.truncate(truncate as usize); - self.blocks.shrink_to_fit(); - } } fn bit_index(bits: u64) -> (usize, usize) { diff --git a/tests/compile-fail/reallocate-bad-alignment.rs b/tests/compile-fail/reallocate-bad-alignment.rs index 2edc13ee1a1..23fe93c5af6 100644 --- a/tests/compile-fail/reallocate-bad-alignment.rs +++ b/tests/compile-fail/reallocate-bad-alignment.rs @@ -2,7 +2,7 @@ extern crate alloc; -// error-pattern: tried to deallocate or reallocate using incorrect alignment or size +// error-pattern: tried to access memory with alignment 1, but alignment 2 is required use alloc::heap::*; fn main() { From e60f11f52cea83322721a24985ffb1f95c041555 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Jul 2017 13:57:18 -0700 Subject: [PATCH 2/5] update for latest nightly --- tests/run-pass/associated-const.rs | 2 -- tests/run-pass/issue-31267-additional.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/tests/run-pass/associated-const.rs b/tests/run-pass/associated-const.rs index d9065445009..fe5da49f807 100644 --- a/tests/run-pass/associated-const.rs +++ b/tests/run-pass/associated-const.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(associated_consts)] - trait Foo { const ID: i32; } diff --git a/tests/run-pass/issue-31267-additional.rs b/tests/run-pass/issue-31267-additional.rs index 90160ebcdf9..14e38f43c52 100644 --- a/tests/run-pass/issue-31267-additional.rs +++ b/tests/run-pass/issue-31267-additional.rs @@ -10,8 +10,6 @@ #![allow(unused_variables)] -#![feature(associated_consts)] - #[derive(Clone, Copy, Debug)] struct Bar; From d2cf3d76b91f936f7dfecf89c52d06dd54335e9a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Jul 2017 15:46:16 -0700 Subject: [PATCH 3/5] update for allocator API --- src/memory.rs | 9 ++- src/terminator/mod.rs | 163 +++++++++++++++++++++++------------------- 2 files changed, 95 insertions(+), 77 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 64549f45b39..ba9b13f07c9 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -223,10 +223,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // 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, old_size: u64, new_size: u64, align: u64) -> EvalResult<'tcx, Pointer> { + pub fn reallocate(&mut self, ptr: Pointer, old_size: u64, old_align: u64, new_size: u64, new_align: u64) -> EvalResult<'tcx, Pointer> { use std::cmp::min; - assert!(align.is_power_of_two()); // TODO(solson): Report error about non-__rust_allocate'd pointer. if ptr.offset != 0 || self.get(ptr.alloc_id).is_err() { return Err(EvalError::ReallocateNonBasePtr); @@ -236,9 +235,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc" - let new_ptr = self.allocate(new_size, align)?; - self.copy(PrimVal::Ptr(ptr), PrimVal::Ptr(new_ptr), min(old_size, new_size), align, /*nonoverlapping*/true)?; - self.deallocate(ptr, Some((old_size, align)))?; + let new_ptr = self.allocate(new_size, new_align)?; + self.copy(PrimVal::Ptr(ptr), PrimVal::Ptr(new_ptr), min(old_size, new_size), min(old_align, new_align), /*nonoverlapping*/true)?; + self.deallocate(ptr, Some((old_size, old_align)))?; Ok(new_ptr) } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 9b0596d58da..38fd4654741 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -520,37 +520,111 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { sig: ty::FnSig<'tcx>, path: String, ) -> EvalResult<'tcx> { + // In some cases in non-MIR libstd-mode, not having a destination is legit. Handle these early. + match &path[..] { + "std::panicking::rust_panic_with_hook" | + "std::rt::begin_panic_fmt" => return Err(EvalError::Panic), + _ => {}, + } + + let dest_ty = sig.output(); + let (dest, dest_block) = destination.ok_or_else(|| EvalError::NoMirFor(path.clone()))?; + if sig.abi == Abi::C { // An external C function - let ty = sig.output(); - let (ret, target) = destination.unwrap(); - self.call_c_abi(instance.def_id(), arg_operands, ret, ty, target)?; + // TODO: That functions actually has a similar preamble to what follows here. May make sense to + // unify these two mechanisms for "hooking into missing functions". + self.call_c_abi(instance.def_id(), arg_operands, dest, dest_ty, dest_block)?; return Ok(()); } + + let args_res: EvalResult> = arg_operands.iter() + .map(|arg| self.eval_operand(arg)) + .collect(); + let args = args_res?; + + let usize = self.tcx.types.usize; - // A Rust function is missing, which means we are running with MIR missing for libstd (or other dependencies). - // Still, we can make many things mostly work by "emulating" or ignoring some functions. match &path[..] { + // Allocators are magic. They have no MIR, even when the rest of libstd does. + "alloc::heap::::__rust_alloc" => { + let size = self.value_to_primval(args[0], usize)?.to_u64()?; + let align = self.value_to_primval(args[1], usize)?.to_u64()?; + if size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); + } + let ptr = self.memory.allocate(size, align)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + } + "alloc::heap::::__rust_alloc_zeroed" => { + let size = self.value_to_primval(args[0], usize)?.to_u64()?; + let align = self.value_to_primval(args[1], usize)?.to_u64()?; + if size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); + } + let ptr = self.memory.allocate(size, align)?; + self.memory.write_repeat(PrimVal::Ptr(ptr), 0, size)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + } + "alloc::heap::::__rust_dealloc" => { + let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; + let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; + let align = self.value_to_primval(args[2], usize)?.to_u64()?; + if old_size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); + } + self.memory.deallocate(ptr, Some((old_size, align)))?; + } + "alloc::heap::::__rust_realloc" => { + let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; + let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; + let old_align = self.value_to_primval(args[2], usize)?.to_u64()?; + let new_size = self.value_to_primval(args[3], usize)?.to_u64()?; + let new_align = self.value_to_primval(args[4], usize)?.to_u64()?; + if old_size == 0 || new_size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !old_align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(old_align)); + } + if !new_align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(new_align)); + } + let new_ptr = self.memory.reallocate(ptr, old_size, old_align, new_size, new_align)?; + self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; + } + + // A Rust function is missing, which means we are running with MIR missing for libstd (or other dependencies). + // Still, we can make many things mostly work by "emulating" or ignoring some functions. "std::io::_print" => { trace!("Ignoring output. To run programs that print, make sure you have a libstd with full MIR."); - self.goto_block(destination.unwrap().1); - Ok(()) - }, - "std::thread::Builder::new" => Err(EvalError::Unimplemented("miri does not support threading".to_owned())), - "std::env::args" => Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), - "std::panicking::rust_panic_with_hook" | - "std::rt::begin_panic_fmt" => Err(EvalError::Panic), + } + "std::thread::Builder::new" => return Err(EvalError::Unimplemented("miri does not support threading".to_owned())), + "std::env::args" => return Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), "std::panicking::panicking" | "std::rt::panicking" => { - let (lval, block) = destination.expect("std::rt::panicking does not diverge"); // we abort on panic -> `std::rt::panicking` always returns false let bool = self.tcx.types.bool; - self.write_primval(lval, PrimVal::from_bool(false), bool)?; - self.goto_block(block); - Ok(()) + self.write_primval(dest, PrimVal::from_bool(false), bool)?; } - _ => Err(EvalError::NoMirFor(path)), + _ => return Err(EvalError::NoMirFor(path)), } + + // Since we pushed no stack frame, the main loop will act + // as if the call just completed and it's returning to the + // current frame. + self.dump_local(dest); + self.goto_block(dest_block); + return Ok(()); } fn call_c_abi( @@ -609,61 +683,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Err(EvalError::Unimplemented(format!("miri does not support dynamically loading libraries (requested symbol: {})", symbol_name))); } - "__rust_allocate" => { - let size = self.value_to_primval(args[0], usize)?.to_u64()?; - let align = self.value_to_primval(args[1], usize)?.to_u64()?; - if size == 0 { - return Err(EvalError::HeapAllocZeroBytes); - } - if !align.is_power_of_two() { - return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); - } - let ptr = self.memory.allocate(size, align)?; - self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; - } - - "__rust_allocate_zeroed" => { - let size = self.value_to_primval(args[0], usize)?.to_u64()?; - let align = self.value_to_primval(args[1], usize)?.to_u64()?; - if size == 0 { - return Err(EvalError::HeapAllocZeroBytes); - } - if !align.is_power_of_two() { - return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); - } - let ptr = self.memory.allocate(size, align)?; - self.memory.write_repeat(PrimVal::Ptr(ptr), 0, size)?; - self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; - } - - "__rust_deallocate" => { - let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; - let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; - let align = self.value_to_primval(args[2], usize)?.to_u64()?; - if old_size == 0 { - return Err(EvalError::HeapAllocZeroBytes); - } - if !align.is_power_of_two() { - return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); - } - self.memory.deallocate(ptr, Some((old_size, align)))?; - }, - - "__rust_reallocate" => { - let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; - let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; - let size = self.value_to_primval(args[2], usize)?.to_u64()?; - let align = self.value_to_primval(args[3], usize)?.to_u64()?; - if old_size == 0 || size == 0 { - return Err(EvalError::HeapAllocZeroBytes); - } - if !align.is_power_of_two() { - return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); - } - let new_ptr = self.memory.reallocate(ptr, old_size, size, align)?; - self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; - } - "__rust_maybe_catch_panic" => { // fn __rust_maybe_catch_panic(f: fn(*mut u8), data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32 // We abort on panic, so not much is going on here, but we still have to call the closure From ea730ab20f72196a683f313cbd2797456eb28b3e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Jul 2017 15:58:47 -0700 Subject: [PATCH 4/5] update tests for new allocator API --- tests/compile-fail/deallocate-bad-alignment.rs | 9 ++++++--- tests/compile-fail/deallocate-bad-size.rs | 9 ++++++--- tests/compile-fail/deallocate-twice.rs | 11 +++++++---- tests/compile-fail/reallocate-bad-alignment.rs | 11 +++++++---- tests/compile-fail/reallocate-bad-size.rs | 9 ++++++--- tests/compile-fail/reallocate-change-alloc.rs | 10 ++++++---- 6 files changed, 38 insertions(+), 21 deletions(-) diff --git a/tests/compile-fail/deallocate-bad-alignment.rs b/tests/compile-fail/deallocate-bad-alignment.rs index fb3c865fa25..a0bcffa47d9 100644 --- a/tests/compile-fail/deallocate-bad-alignment.rs +++ b/tests/compile-fail/deallocate-bad-alignment.rs @@ -1,13 +1,16 @@ -#![feature(alloc, heap_api)] +#![feature(alloc, allocator_api)] extern crate alloc; +use alloc::heap::Heap; +use alloc::allocator::*; + // error-pattern: tried to deallocate or reallocate using incorrect alignment or size use alloc::heap::*; fn main() { unsafe { - let x = allocate(1, 1); - deallocate(x, 1, 2); + let x = Heap.alloc(Layout::from_size_align_unchecked(1, 1)).unwrap(); + Heap.dealloc(x, Layout::from_size_align_unchecked(1, 2)); } } diff --git a/tests/compile-fail/deallocate-bad-size.rs b/tests/compile-fail/deallocate-bad-size.rs index fb3c865fa25..d8c4493043d 100644 --- a/tests/compile-fail/deallocate-bad-size.rs +++ b/tests/compile-fail/deallocate-bad-size.rs @@ -1,13 +1,16 @@ -#![feature(alloc, heap_api)] +#![feature(alloc, allocator_api)] extern crate alloc; +use alloc::heap::Heap; +use alloc::allocator::*; + // error-pattern: tried to deallocate or reallocate using incorrect alignment or size use alloc::heap::*; fn main() { unsafe { - let x = allocate(1, 1); - deallocate(x, 1, 2); + let x = Heap.alloc(Layout::from_size_align_unchecked(1, 1)).unwrap(); + Heap.dealloc(x, Layout::from_size_align_unchecked(2, 1)); } } diff --git a/tests/compile-fail/deallocate-twice.rs b/tests/compile-fail/deallocate-twice.rs index 9f0f9369a80..3c4399eaa3e 100644 --- a/tests/compile-fail/deallocate-twice.rs +++ b/tests/compile-fail/deallocate-twice.rs @@ -1,14 +1,17 @@ -#![feature(alloc, heap_api)] +#![feature(alloc, allocator_api)] extern crate alloc; +use alloc::heap::Heap; +use alloc::allocator::*; + // error-pattern: tried to deallocate with a pointer not to the beginning of an existing object use alloc::heap::*; fn main() { unsafe { - let x = allocate(1, 1); - deallocate(x, 1, 1); - deallocate(x, 1, 1); + let x = Heap.alloc(Layout::from_size_align_unchecked(1, 1)).unwrap(); + Heap.dealloc(x, Layout::from_size_align_unchecked(1, 1)); + Heap.dealloc(x, Layout::from_size_align_unchecked(1, 1)); } } diff --git a/tests/compile-fail/reallocate-bad-alignment.rs b/tests/compile-fail/reallocate-bad-alignment.rs index 23fe93c5af6..246d5592957 100644 --- a/tests/compile-fail/reallocate-bad-alignment.rs +++ b/tests/compile-fail/reallocate-bad-alignment.rs @@ -1,13 +1,16 @@ -#![feature(alloc, heap_api)] +#![feature(alloc, allocator_api)] extern crate alloc; -// error-pattern: tried to access memory with alignment 1, but alignment 2 is required +use alloc::heap::Heap; +use alloc::allocator::*; + +// error-pattern: tried to deallocate or reallocate using incorrect alignment or size use alloc::heap::*; fn main() { unsafe { - let x = allocate(1, 1); - let _y = reallocate(x, 1, 1, 2); + let x = Heap.alloc(Layout::from_size_align_unchecked(1, 2)).unwrap(); + let _y = Heap.realloc(x, Layout::from_size_align_unchecked(1, 1), Layout::from_size_align_unchecked(1, 2)).unwrap(); } } diff --git a/tests/compile-fail/reallocate-bad-size.rs b/tests/compile-fail/reallocate-bad-size.rs index f7f1b48a7f2..2e5a6418380 100644 --- a/tests/compile-fail/reallocate-bad-size.rs +++ b/tests/compile-fail/reallocate-bad-size.rs @@ -1,13 +1,16 @@ -#![feature(alloc, heap_api)] +#![feature(alloc, allocator_api)] extern crate alloc; +use alloc::heap::Heap; +use alloc::allocator::*; + // error-pattern: tried to deallocate or reallocate using incorrect alignment or size use alloc::heap::*; fn main() { unsafe { - let x = allocate(1, 1); - let _y = reallocate(x, 2, 1, 1); + let x = Heap.alloc(Layout::from_size_align_unchecked(1, 1)).unwrap(); + let _y = Heap.realloc(x, Layout::from_size_align_unchecked(2, 1), Layout::from_size_align_unchecked(1, 1)).unwrap(); } } diff --git a/tests/compile-fail/reallocate-change-alloc.rs b/tests/compile-fail/reallocate-change-alloc.rs index a63629388e7..290c966a2bc 100644 --- a/tests/compile-fail/reallocate-change-alloc.rs +++ b/tests/compile-fail/reallocate-change-alloc.rs @@ -1,12 +1,14 @@ -#![feature(alloc, heap_api)] +#![feature(alloc, allocator_api)] extern crate alloc; -use alloc::heap::*; +use alloc::heap::Heap; +use alloc::allocator::*; + fn main() { unsafe { - let x = allocate(1, 1); - let _y = reallocate(x, 1, 1, 1); + let x = Heap.alloc(Layout::from_size_align_unchecked(1, 1)).unwrap(); + let _y = Heap.realloc(x, Layout::from_size_align_unchecked(1, 1), Layout::from_size_align_unchecked(1, 1)).unwrap(); let _z = *x; //~ ERROR: dangling pointer was dereferenced } } From 1cbf5e896219be3e1eda8b1f92d1ba76ae30bd55 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Jul 2017 18:09:46 -0700 Subject: [PATCH 5/5] leave notes regarding possible alignment checks --- src/memory.rs | 1 + src/terminator/intrinsic.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/memory.rs b/src/memory.rs index ba9b13f07c9..0f32ac5183d 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -663,6 +663,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn copy(&mut self, src: PrimVal, dest: PrimVal, size: u64, align: u64, nonoverlapping: bool) -> EvalResult<'tcx> { if size == 0 { + // TODO: Should we check for alignment here? (Also see write_bytes intrinsic) return Ok(()); } let src = src.to_ptr()?; diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index dbfd8f1d952..f2b0446618a 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -463,6 +463,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = arg_vals[0].read_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; if count > 0 { + // TODO: Should we, at least, validate the alignment? (Also see memory::copy) self.memory.check_align(ptr, ty_align, size * count)?; self.memory.write_repeat(ptr, val_byte, size * count)?; }