Skip to content

Commit 8fd40db

Browse files
authored
Merge pull request #708 from RalfJung/alloc-tests
test System/Global allocator API: alloc_zeroed, realloc
2 parents 788616d + e4970fe commit 8fd40db

File tree

4 files changed

+150
-69
lines changed

4 files changed

+150
-69
lines changed

src/fn_call.rs

+117-61
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,86 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
5050
Ok(Some(this.load_mir(instance.def)?))
5151
}
5252

53+
fn malloc(
54+
&mut self,
55+
size: u64,
56+
zero_init: bool,
57+
) -> Scalar<Tag> {
58+
let this = self.eval_context_mut();
59+
let tcx = &{this.tcx.tcx};
60+
if size == 0 {
61+
Scalar::from_int(0, this.pointer_size())
62+
} else {
63+
let align = this.tcx.data_layout.pointer_align.abi;
64+
let ptr = this.memory_mut().allocate(Size::from_bytes(size), align, MiriMemoryKind::C.into());
65+
if zero_init {
66+
// We just allocated this, the access cannot fail
67+
this.memory_mut()
68+
.get_mut(ptr.alloc_id).unwrap()
69+
.write_repeat(tcx, ptr, 0, Size::from_bytes(size)).unwrap();
70+
}
71+
Scalar::Ptr(ptr)
72+
}
73+
}
74+
75+
fn free(
76+
&mut self,
77+
ptr: Scalar<Tag>,
78+
) -> EvalResult<'tcx> {
79+
let this = self.eval_context_mut();
80+
if !ptr.is_null_ptr(this) {
81+
this.memory_mut().deallocate(
82+
ptr.to_ptr()?,
83+
None,
84+
MiriMemoryKind::C.into(),
85+
)?;
86+
}
87+
Ok(())
88+
}
89+
90+
fn realloc(
91+
&mut self,
92+
old_ptr: Scalar<Tag>,
93+
new_size: u64,
94+
) -> EvalResult<'tcx, Scalar<Tag>> {
95+
let this = self.eval_context_mut();
96+
let align = this.tcx.data_layout.pointer_align.abi;
97+
if old_ptr.is_null_ptr(this) {
98+
if new_size == 0 {
99+
Ok(Scalar::from_int(0, this.pointer_size()))
100+
} else {
101+
let new_ptr = this.memory_mut().allocate(
102+
Size::from_bytes(new_size),
103+
align,
104+
MiriMemoryKind::C.into()
105+
);
106+
Ok(Scalar::Ptr(new_ptr))
107+
}
108+
} else {
109+
let old_ptr = old_ptr.to_ptr()?;
110+
let memory = this.memory_mut();
111+
let old_size = Size::from_bytes(memory.get(old_ptr.alloc_id)?.bytes.len() as u64);
112+
if new_size == 0 {
113+
memory.deallocate(
114+
old_ptr,
115+
Some((old_size, align)),
116+
MiriMemoryKind::C.into(),
117+
)?;
118+
Ok(Scalar::from_int(0, this.pointer_size()))
119+
} else {
120+
let new_ptr = memory.reallocate(
121+
old_ptr,
122+
old_size,
123+
align,
124+
Size::from_bytes(new_size),
125+
align,
126+
MiriMemoryKind::C.into(),
127+
)?;
128+
Ok(Scalar::Ptr(new_ptr))
129+
}
130+
}
131+
}
132+
53133
/// Emulates calling a foreign item, failing if the item is not supported.
54134
/// This function will handle `goto_block` if needed.
55135
fn emulate_foreign_item(
@@ -87,28 +167,15 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
87167
match link_name {
88168
"malloc" => {
89169
let size = this.read_scalar(args[0])?.to_usize(this)?;
90-
if size == 0 {
91-
this.write_null(dest)?;
92-
} else {
93-
let align = this.tcx.data_layout.pointer_align.abi;
94-
let ptr = this.memory_mut().allocate(Size::from_bytes(size), align, MiriMemoryKind::C.into());
95-
this.write_scalar(Scalar::Ptr(ptr), dest)?;
96-
}
170+
let res = this.malloc(size, /*zero_init:*/ false);
171+
this.write_scalar(res, dest)?;
97172
}
98173
"calloc" => {
99174
let items = this.read_scalar(args[0])?.to_usize(this)?;
100175
let len = this.read_scalar(args[1])?.to_usize(this)?;
101-
let bytes = items.checked_mul(len).ok_or_else(|| InterpError::Overflow(mir::BinOp::Mul))?;
102-
103-
if bytes == 0 {
104-
this.write_null(dest)?;
105-
} else {
106-
let size = Size::from_bytes(bytes);
107-
let align = this.tcx.data_layout.pointer_align.abi;
108-
let ptr = this.memory_mut().allocate(size, align, MiriMemoryKind::C.into());
109-
this.memory_mut().get_mut(ptr.alloc_id)?.write_repeat(tcx, ptr, 0, size)?;
110-
this.write_scalar(Scalar::Ptr(ptr), dest)?;
111-
}
176+
let size = items.checked_mul(len).ok_or_else(|| InterpError::Overflow(mir::BinOp::Mul))?;
177+
let res = this.malloc(size, /*zero_init:*/ true);
178+
this.write_scalar(res, dest)?;
112179
}
113180
"posix_memalign" => {
114181
let ret = this.deref_operand(args[0])?;
@@ -136,55 +203,15 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
136203
}
137204
this.write_null(dest)?;
138205
}
139-
140206
"free" => {
141207
let ptr = this.read_scalar(args[0])?.not_undef()?;
142-
if !ptr.is_null_ptr(this) {
143-
this.memory_mut().deallocate(
144-
ptr.to_ptr()?,
145-
None,
146-
MiriMemoryKind::C.into(),
147-
)?;
148-
}
208+
this.free(ptr)?;
149209
}
150210
"realloc" => {
151211
let old_ptr = this.read_scalar(args[0])?.not_undef()?;
152212
let new_size = this.read_scalar(args[1])?.to_usize(this)?;
153-
let align = this.tcx.data_layout.pointer_align.abi;
154-
if old_ptr.is_null_ptr(this) {
155-
if new_size == 0 {
156-
this.write_null(dest)?;
157-
} else {
158-
let new_ptr = this.memory_mut().allocate(
159-
Size::from_bytes(new_size),
160-
align,
161-
MiriMemoryKind::C.into()
162-
);
163-
this.write_scalar(Scalar::Ptr(new_ptr), dest)?;
164-
}
165-
} else {
166-
let old_ptr = old_ptr.to_ptr()?;
167-
let memory = this.memory_mut();
168-
let old_size = Size::from_bytes(memory.get(old_ptr.alloc_id)?.bytes.len() as u64);
169-
if new_size == 0 {
170-
memory.deallocate(
171-
old_ptr,
172-
Some((old_size, align)),
173-
MiriMemoryKind::C.into(),
174-
)?;
175-
this.write_null(dest)?;
176-
} else {
177-
let new_ptr = memory.reallocate(
178-
old_ptr,
179-
old_size,
180-
align,
181-
Size::from_bytes(new_size),
182-
align,
183-
MiriMemoryKind::C.into(),
184-
)?;
185-
this.write_scalar(Scalar::Ptr(new_ptr), dest)?;
186-
}
187-
}
213+
let res = this.realloc(old_ptr, new_size)?;
214+
this.write_scalar(res, dest)?;
188215
}
189216

190217
"__rust_alloc" => {
@@ -687,6 +714,35 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
687714
},
688715

689716
// Windows API stubs.
717+
// HANDLE = isize
718+
// DWORD = ULONG = u32
719+
"GetProcessHeap" => {
720+
// Just fake a HANDLE
721+
this.write_scalar(Scalar::from_int(1, this.pointer_size()), dest)?;
722+
}
723+
"HeapAlloc" => {
724+
let _handle = this.read_scalar(args[0])?.to_isize(this)?;
725+
let flags = this.read_scalar(args[1])?.to_u32()?;
726+
let size = this.read_scalar(args[2])?.to_usize(this)?;
727+
let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
728+
let res = this.malloc(size, zero_init);
729+
this.write_scalar(res, dest)?;
730+
}
731+
"HeapFree" => {
732+
let _handle = this.read_scalar(args[0])?.to_isize(this)?;
733+
let _flags = this.read_scalar(args[1])?.to_u32()?;
734+
let ptr = this.read_scalar(args[2])?.not_undef()?;
735+
this.free(ptr)?;
736+
}
737+
"HeapReAlloc" => {
738+
let _handle = this.read_scalar(args[0])?.to_isize(this)?;
739+
let _flags = this.read_scalar(args[1])?.to_u32()?;
740+
let ptr = this.read_scalar(args[2])?.not_undef()?;
741+
let size = this.read_scalar(args[3])?.to_usize(this)?;
742+
let res = this.realloc(ptr, size)?;
743+
this.write_scalar(res, dest)?;
744+
}
745+
690746
"SetLastError" => {
691747
let err = this.read_scalar(args[0])?.to_u32()?;
692748
this.machine.last_error = err;

tests/run-pass/env.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//ignore-windows: env var emulation not implemented on Windows
1+
//ignore-windows: TODO env var emulation stubbed out on Windows
22

33
use std::env;
44

tests/run-pass/hashmap.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,12 @@ fn test_map<S: BuildHasher>(mut map: HashMap<i32, i32, S>) {
2727
}
2828

2929
fn main() {
30-
if cfg!(not(target_os = "macos")) {
31-
let map: HashMap<i32, i32> = HashMap::default();
32-
test_map(map);
33-
} else {
34-
// TODO: Implement random number generation on OS X.
30+
if cfg!(target_os = "macos") { // TODO: Implement random number generation on OS X.
3531
// Until then, use a deterministic map.
3632
let map : HashMap<i32, i32, BuildHasherDefault<collections::hash_map::DefaultHasher>> = HashMap::default();
3733
test_map(map);
34+
} else {
35+
let map: HashMap<i32, i32> = HashMap::default();
36+
test_map(map);
3837
}
3938
}

tests/run-pass/heap_allocator.rs

+28-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,32 @@
1-
//ignore-windows: inspects allocation base address on Windows
2-
31
#![feature(allocator_api)]
42

53
use std::ptr::NonNull;
64
use std::alloc::{Global, Alloc, Layout, System};
5+
use std::slice;
6+
7+
fn check_alloc<T: Alloc>(mut allocator: T) { unsafe {
8+
let layout = Layout::from_size_align(20, 4).unwrap();
9+
let a = allocator.alloc(layout).unwrap();
10+
allocator.dealloc(a, layout);
11+
12+
let p1 = allocator.alloc_zeroed(layout).unwrap();
13+
14+
let p2 = allocator.realloc(p1, Layout::from_size_align(20, 4).unwrap(), 40).unwrap();
15+
let slice = slice::from_raw_parts(p2.as_ptr(), 20);
16+
assert_eq!(&slice, &[0_u8; 20]);
17+
18+
// old size == new size
19+
let p3 = allocator.realloc(p2, Layout::from_size_align(40, 4).unwrap(), 40).unwrap();
20+
let slice = slice::from_raw_parts(p3.as_ptr(), 20);
21+
assert_eq!(&slice, &[0_u8; 20]);
22+
23+
// old size > new size
24+
let p4 = allocator.realloc(p3, Layout::from_size_align(40, 4).unwrap(), 10).unwrap();
25+
let slice = slice::from_raw_parts(p4.as_ptr(), 10);
26+
assert_eq!(&slice, &[0_u8; 10]);
27+
28+
allocator.dealloc(p4, Layout::from_size_align(10, 4).unwrap());
29+
} }
730

831
fn check_overalign_requests<T: Alloc>(mut allocator: T) {
932
let size = 8;
@@ -50,6 +73,9 @@ fn box_to_global() {
5073
}
5174

5275
fn main() {
76+
check_alloc(System);
77+
check_alloc(Global);
78+
#[cfg(not(target_os = "windows"))] // TODO: Inspects allocation base address on Windows; needs intptrcast model
5379
check_overalign_requests(System);
5480
check_overalign_requests(Global);
5581
global_to_box();

0 commit comments

Comments
 (0)