Skip to content

Commit

Permalink
Merge pull request #708 from RalfJung/alloc-tests
Browse files Browse the repository at this point in the history
test System/Global allocator API: alloc_zeroed, realloc
  • Loading branch information
RalfJung authored Apr 21, 2019
2 parents 788616d + e4970fe commit 8fd40db
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 69 deletions.
178 changes: 117 additions & 61 deletions src/fn_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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])?;
Expand Down Expand Up @@ -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" => {
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion tests/run-pass/env.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
9 changes: 4 additions & 5 deletions tests/run-pass/hashmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
30 changes: 28 additions & 2 deletions tests/run-pass/heap_allocator.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 8fd40db

Please sign in to comment.