diff --git a/src/fn_call.rs b/src/fn_call.rs index ae6aff10ac20..9789a76c6393 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -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 { + 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, + ) -> 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, + new_size: u64, + ) -> EvalResult<'tcx, Scalar> { + 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; diff --git a/tests/run-pass/env.rs b/tests/run-pass/env.rs index 0ca63e148fdb..91e15f249d45 100644 --- a/tests/run-pass/env.rs +++ b/tests/run-pass/env.rs @@ -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; diff --git a/tests/run-pass/hashmap.rs b/tests/run-pass/hashmap.rs index 796e63c81a41..25a816bcf245 100644 --- a/tests/run-pass/hashmap.rs +++ b/tests/run-pass/hashmap.rs @@ -27,13 +27,12 @@ fn test_map(mut map: HashMap) { } fn main() { - if cfg!(not(target_os = "macos")) { - let map: HashMap = 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> = HashMap::default(); test_map(map); + } else { + let map: HashMap = HashMap::default(); + test_map(map); } } diff --git a/tests/run-pass/heap_allocator.rs b/tests/run-pass/heap_allocator.rs index bfed725a497c..b201f24e2563 100644 --- a/tests/run-pass/heap_allocator.rs +++ b/tests/run-pass/heap_allocator.rs @@ -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(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(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();