Merge pull request #708 from RalfJung/alloc-tests

test System/Global allocator API: alloc_zeroed, realloc
This commit is contained in:
Ralf Jung 2019-04-21 17:22:39 +02:00 committed by GitHub
commit 8fd40db4bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 150 additions and 69 deletions

View file

@ -50,6 +50,86 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
Ok(Some(this.load_mir(instance.def)?))
}
fn malloc(
&mut self,
size: u64,
zero_init: bool,
) -> Scalar<Tag> {
let this = self.eval_context_mut();
let tcx = &{this.tcx.tcx};
if size == 0 {
Scalar::from_int(0, this.pointer_size())
} else {
let align = this.tcx.data_layout.pointer_align.abi;
let ptr = this.memory_mut().allocate(Size::from_bytes(size), align, MiriMemoryKind::C.into());
if zero_init {
// We just allocated this, the access cannot fail
this.memory_mut()
.get_mut(ptr.alloc_id).unwrap()
.write_repeat(tcx, ptr, 0, Size::from_bytes(size)).unwrap();
}
Scalar::Ptr(ptr)
}
}
fn free(
&mut self,
ptr: Scalar<Tag>,
) -> EvalResult<'tcx> {
let this = self.eval_context_mut();
if !ptr.is_null_ptr(this) {
this.memory_mut().deallocate(
ptr.to_ptr()?,
None,
MiriMemoryKind::C.into(),
)?;
}
Ok(())
}
fn realloc(
&mut self,
old_ptr: Scalar<Tag>,
new_size: u64,
) -> EvalResult<'tcx, Scalar<Tag>> {
let this = self.eval_context_mut();
let align = this.tcx.data_layout.pointer_align.abi;
if old_ptr.is_null_ptr(this) {
if new_size == 0 {
Ok(Scalar::from_int(0, this.pointer_size()))
} else {
let new_ptr = this.memory_mut().allocate(
Size::from_bytes(new_size),
align,
MiriMemoryKind::C.into()
);
Ok(Scalar::Ptr(new_ptr))
}
} else {
let old_ptr = old_ptr.to_ptr()?;
let memory = this.memory_mut();
let old_size = Size::from_bytes(memory.get(old_ptr.alloc_id)?.bytes.len() as u64);
if new_size == 0 {
memory.deallocate(
old_ptr,
Some((old_size, align)),
MiriMemoryKind::C.into(),
)?;
Ok(Scalar::from_int(0, this.pointer_size()))
} else {
let new_ptr = memory.reallocate(
old_ptr,
old_size,
align,
Size::from_bytes(new_size),
align,
MiriMemoryKind::C.into(),
)?;
Ok(Scalar::Ptr(new_ptr))
}
}
}
/// Emulates calling a foreign item, failing if the item is not supported.
/// This function will handle `goto_block` if needed.
fn emulate_foreign_item(
@ -87,28 +167,15 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
match link_name {
"malloc" => {
let size = this.read_scalar(args[0])?.to_usize(this)?;
if size == 0 {
this.write_null(dest)?;
} else {
let align = this.tcx.data_layout.pointer_align.abi;
let ptr = this.memory_mut().allocate(Size::from_bytes(size), align, MiriMemoryKind::C.into());
this.write_scalar(Scalar::Ptr(ptr), dest)?;
}
let res = this.malloc(size, /*zero_init:*/ false);
this.write_scalar(res, dest)?;
}
"calloc" => {
let items = this.read_scalar(args[0])?.to_usize(this)?;
let len = this.read_scalar(args[1])?.to_usize(this)?;
let bytes = items.checked_mul(len).ok_or_else(|| InterpError::Overflow(mir::BinOp::Mul))?;
if bytes == 0 {
this.write_null(dest)?;
} else {
let size = Size::from_bytes(bytes);
let align = this.tcx.data_layout.pointer_align.abi;
let ptr = this.memory_mut().allocate(size, align, MiriMemoryKind::C.into());
this.memory_mut().get_mut(ptr.alloc_id)?.write_repeat(tcx, ptr, 0, size)?;
this.write_scalar(Scalar::Ptr(ptr), dest)?;
}
let size = items.checked_mul(len).ok_or_else(|| InterpError::Overflow(mir::BinOp::Mul))?;
let res = this.malloc(size, /*zero_init:*/ true);
this.write_scalar(res, dest)?;
}
"posix_memalign" => {
let ret = this.deref_operand(args[0])?;
@ -136,55 +203,15 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
}
this.write_null(dest)?;
}
"free" => {
let ptr = this.read_scalar(args[0])?.not_undef()?;
if !ptr.is_null_ptr(this) {
this.memory_mut().deallocate(
ptr.to_ptr()?,
None,
MiriMemoryKind::C.into(),
)?;
}
this.free(ptr)?;
}
"realloc" => {
let old_ptr = this.read_scalar(args[0])?.not_undef()?;
let new_size = this.read_scalar(args[1])?.to_usize(this)?;
let align = this.tcx.data_layout.pointer_align.abi;
if old_ptr.is_null_ptr(this) {
if new_size == 0 {
this.write_null(dest)?;
} else {
let new_ptr = this.memory_mut().allocate(
Size::from_bytes(new_size),
align,
MiriMemoryKind::C.into()
);
this.write_scalar(Scalar::Ptr(new_ptr), dest)?;
}
} else {
let old_ptr = old_ptr.to_ptr()?;
let memory = this.memory_mut();
let old_size = Size::from_bytes(memory.get(old_ptr.alloc_id)?.bytes.len() as u64);
if new_size == 0 {
memory.deallocate(
old_ptr,
Some((old_size, align)),
MiriMemoryKind::C.into(),
)?;
this.write_null(dest)?;
} else {
let new_ptr = memory.reallocate(
old_ptr,
old_size,
align,
Size::from_bytes(new_size),
align,
MiriMemoryKind::C.into(),
)?;
this.write_scalar(Scalar::Ptr(new_ptr), dest)?;
}
}
let res = this.realloc(old_ptr, new_size)?;
this.write_scalar(res, dest)?;
}
"__rust_alloc" => {
@ -687,6 +714,35 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
},
// Windows API stubs.
// HANDLE = isize
// DWORD = ULONG = u32
"GetProcessHeap" => {
// Just fake a HANDLE
this.write_scalar(Scalar::from_int(1, this.pointer_size()), dest)?;
}
"HeapAlloc" => {
let _handle = this.read_scalar(args[0])?.to_isize(this)?;
let flags = this.read_scalar(args[1])?.to_u32()?;
let size = this.read_scalar(args[2])?.to_usize(this)?;
let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
let res = this.malloc(size, zero_init);
this.write_scalar(res, dest)?;
}
"HeapFree" => {
let _handle = this.read_scalar(args[0])?.to_isize(this)?;
let _flags = this.read_scalar(args[1])?.to_u32()?;
let ptr = this.read_scalar(args[2])?.not_undef()?;
this.free(ptr)?;
}
"HeapReAlloc" => {
let _handle = this.read_scalar(args[0])?.to_isize(this)?;
let _flags = this.read_scalar(args[1])?.to_u32()?;
let ptr = this.read_scalar(args[2])?.not_undef()?;
let size = this.read_scalar(args[3])?.to_usize(this)?;
let res = this.realloc(ptr, size)?;
this.write_scalar(res, dest)?;
}
"SetLastError" => {
let err = this.read_scalar(args[0])?.to_u32()?;
this.machine.last_error = err;

View file

@ -1,4 +1,4 @@
//ignore-windows: env var emulation not implemented on Windows
//ignore-windows: TODO env var emulation stubbed out on Windows
use std::env;

View file

@ -27,13 +27,12 @@ fn test_map<S: BuildHasher>(mut map: HashMap<i32, i32, S>) {
}
fn main() {
if cfg!(not(target_os = "macos")) {
let map: HashMap<i32, i32> = HashMap::default();
test_map(map);
} else {
// TODO: Implement random number generation on OS X.
if cfg!(target_os = "macos") { // TODO: Implement random number generation on OS X.
// Until then, use a deterministic map.
let map : HashMap<i32, i32, BuildHasherDefault<collections::hash_map::DefaultHasher>> = HashMap::default();
test_map(map);
} else {
let map: HashMap<i32, i32> = HashMap::default();
test_map(map);
}
}

View file

@ -1,9 +1,32 @@
//ignore-windows: inspects allocation base address on Windows
#![feature(allocator_api)]
use std::ptr::NonNull;
use std::alloc::{Global, Alloc, Layout, System};
use std::slice;
fn check_alloc<T: Alloc>(mut allocator: T) { unsafe {
let layout = Layout::from_size_align(20, 4).unwrap();
let a = allocator.alloc(layout).unwrap();
allocator.dealloc(a, layout);
let p1 = allocator.alloc_zeroed(layout).unwrap();
let p2 = allocator.realloc(p1, Layout::from_size_align(20, 4).unwrap(), 40).unwrap();
let slice = slice::from_raw_parts(p2.as_ptr(), 20);
assert_eq!(&slice, &[0_u8; 20]);
// old size == new size
let p3 = allocator.realloc(p2, Layout::from_size_align(40, 4).unwrap(), 40).unwrap();
let slice = slice::from_raw_parts(p3.as_ptr(), 20);
assert_eq!(&slice, &[0_u8; 20]);
// old size > new size
let p4 = allocator.realloc(p3, Layout::from_size_align(40, 4).unwrap(), 10).unwrap();
let slice = slice::from_raw_parts(p4.as_ptr(), 10);
assert_eq!(&slice, &[0_u8; 10]);
allocator.dealloc(p4, Layout::from_size_align(10, 4).unwrap());
} }
fn check_overalign_requests<T: Alloc>(mut allocator: T) {
let size = 8;
@ -50,6 +73,9 @@ fn box_to_global() {
}
fn main() {
check_alloc(System);
check_alloc(Global);
#[cfg(not(target_os = "windows"))] // TODO: Inspects allocation base address on Windows; needs intptrcast model
check_overalign_requests(System);
check_overalign_requests(Global);
global_to_box();