Skip to content

Commit f23b782

Browse files
committed
align small malloc-allocations even less, and test that we do
1 parent 285fc0d commit f23b782

File tree

3 files changed

+58
-27
lines changed

3 files changed

+58
-27
lines changed

src/machine.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ pub enum MiriMemoryKind {
2525
Rust,
2626
/// `malloc` memory.
2727
C,
28+
/// Windows `HeapAlloc` memory.
29+
WinHeap,
2830
/// Part of env var emulation.
2931
Env,
3032
/// Statics.
@@ -407,7 +409,7 @@ impl MayLeak for MiriMemoryKind {
407409
fn may_leak(self) -> bool {
408410
use self::MiriMemoryKind::*;
409411
match self {
410-
Rust | C => false,
412+
Rust | C | WinHeap => false,
411413
Env | Static => true,
412414
}
413415
}

src/shims/foreign_items.rs

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,48 @@ use crate::*;
1010

1111
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
1212
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
13-
/// Returns the minimum alignment for the target architecture.
14-
fn min_align(&self) -> Align {
13+
/// Returns the minimum alignment for the target architecture for allocations of the given size.
14+
fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align {
1515
let this = self.eval_context_ref();
1616
// List taken from `libstd/sys_common/alloc.rs`.
1717
let min_align = match this.tcx.tcx.sess.target.target.arch.as_str() {
1818
"x86" | "arm" | "mips" | "powerpc" | "powerpc64" | "asmjs" | "wasm32" => 8,
1919
"x86_64" | "aarch64" | "mips64" | "s390x" | "sparc64" => 16,
2020
arch => bug!("Unsupported target architecture: {}", arch),
2121
};
22-
Align::from_bytes(min_align).unwrap()
22+
// Windows always aligns, even small allocations.
23+
// Source: <https://support.microsoft.com/en-us/help/286470/how-to-use-pageheap-exe-in-windows-xp-windows-2000-and-windows-server>
24+
// But jemalloc does not, so for the C heap we only align if the allocation is sufficiently big.
25+
if kind == MiriMemoryKind::WinHeap || size >= min_align {
26+
return Align::from_bytes(min_align).unwrap();
27+
}
28+
// We have `size < min_align`. Round `size` *down* to the next power of two and use that.
29+
fn prev_power_of_two(x: u64) -> u64 {
30+
let next_pow2 = x.next_power_of_two();
31+
if next_pow2 == x {
32+
// x *is* a power of two, just use that.
33+
x
34+
} else {
35+
// x is between two powers, so next = 2*prev.
36+
next_pow2 / 2
37+
}
38+
}
39+
Align::from_bytes(prev_power_of_two(size)).unwrap()
2340
}
2441

2542
fn malloc(
2643
&mut self,
2744
size: u64,
2845
zero_init: bool,
46+
kind: MiriMemoryKind,
2947
) -> Scalar<Tag> {
3048
let this = self.eval_context_mut();
3149
let tcx = &{this.tcx.tcx};
3250
if size == 0 {
3351
Scalar::from_int(0, this.pointer_size())
3452
} else {
35-
let align = this.min_align();
36-
let ptr = this.memory_mut().allocate(Size::from_bytes(size), align, MiriMemoryKind::C.into());
53+
let align = this.min_align(size, kind);
54+
let ptr = this.memory_mut().allocate(Size::from_bytes(size), align, kind.into());
3755
if zero_init {
3856
// We just allocated this, the access cannot fail
3957
this.memory_mut()
@@ -47,14 +65,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4765
fn free(
4866
&mut self,
4967
ptr: Scalar<Tag>,
68+
kind: MiriMemoryKind,
5069
) -> InterpResult<'tcx> {
5170
let this = self.eval_context_mut();
5271
if !this.is_null(ptr)? {
5372
let ptr = this.force_ptr(ptr)?;
5473
this.memory_mut().deallocate(
5574
ptr,
5675
None,
57-
MiriMemoryKind::C.into(),
76+
kind.into(),
5877
)?;
5978
}
6079
Ok(())
@@ -64,39 +83,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
6483
&mut self,
6584
old_ptr: Scalar<Tag>,
6685
new_size: u64,
86+
kind: MiriMemoryKind,
6787
) -> InterpResult<'tcx, Scalar<Tag>> {
6888
let this = self.eval_context_mut();
69-
let align = this.min_align();
89+
let new_align = this.min_align(new_size, kind);
7090
if this.is_null(old_ptr)? {
7191
if new_size == 0 {
7292
Ok(Scalar::from_int(0, this.pointer_size()))
7393
} else {
7494
let new_ptr = this.memory_mut().allocate(
7595
Size::from_bytes(new_size),
76-
align,
77-
MiriMemoryKind::C.into()
96+
new_align,
97+
kind.into()
7898
);
7999
Ok(Scalar::Ptr(new_ptr))
80100
}
81101
} else {
82102
let old_ptr = this.force_ptr(old_ptr)?;
83103
let memory = this.memory_mut();
84-
let old_size = Size::from_bytes(memory.get(old_ptr.alloc_id)?.bytes.len() as u64);
85104
if new_size == 0 {
86105
memory.deallocate(
87106
old_ptr,
88-
Some((old_size, align)),
89-
MiriMemoryKind::C.into(),
107+
None,
108+
kind.into(),
90109
)?;
91110
Ok(Scalar::from_int(0, this.pointer_size()))
92111
} else {
93112
let new_ptr = memory.reallocate(
94113
old_ptr,
95-
old_size,
96-
align,
114+
None,
97115
Size::from_bytes(new_size),
98-
align,
99-
MiriMemoryKind::C.into(),
116+
new_align,
117+
kind.into(),
100118
)?;
101119
Ok(Scalar::Ptr(new_ptr))
102120
}
@@ -145,14 +163,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
145163
match link_name {
146164
"malloc" => {
147165
let size = this.read_scalar(args[0])?.to_usize(this)?;
148-
let res = this.malloc(size, /*zero_init:*/ false);
166+
let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C);
149167
this.write_scalar(res, dest)?;
150168
}
151169
"calloc" => {
152170
let items = this.read_scalar(args[0])?.to_usize(this)?;
153171
let len = this.read_scalar(args[1])?.to_usize(this)?;
154172
let size = items.checked_mul(len).ok_or_else(|| InterpError::Overflow(mir::BinOp::Mul))?;
155-
let res = this.malloc(size, /*zero_init:*/ true);
173+
let res = this.malloc(size, /*zero_init:*/ true, MiriMemoryKind::C);
156174
this.write_scalar(res, dest)?;
157175
}
158176
"posix_memalign" => {
@@ -187,12 +205,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
187205
}
188206
"free" => {
189207
let ptr = this.read_scalar(args[0])?.not_undef()?;
190-
this.free(ptr)?;
208+
this.free(ptr, MiriMemoryKind::C)?;
191209
}
192210
"realloc" => {
193211
let old_ptr = this.read_scalar(args[0])?.not_undef()?;
194212
let new_size = this.read_scalar(args[1])?.to_usize(this)?;
195-
let res = this.realloc(old_ptr, new_size)?;
213+
let res = this.realloc(old_ptr, new_size, MiriMemoryKind::C)?;
196214
this.write_scalar(res, dest)?;
197215
}
198216

@@ -262,12 +280,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
262280
if !align.is_power_of_two() {
263281
return err!(HeapAllocNonPowerOfTwoAlignment(align));
264282
}
283+
let align = Align::from_bytes(align).unwrap();
265284
let new_ptr = this.memory_mut().reallocate(
266285
ptr,
267-
Size::from_bytes(old_size),
268-
Align::from_bytes(align).unwrap(),
286+
Some((Size::from_bytes(old_size), align)),
269287
Size::from_bytes(new_size),
270-
Align::from_bytes(align).unwrap(),
288+
align,
271289
MiriMemoryKind::Rust.into(),
272290
)?;
273291
this.write_scalar(Scalar::Ptr(new_ptr), dest)?;
@@ -765,22 +783,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
765783
let flags = this.read_scalar(args[1])?.to_u32()?;
766784
let size = this.read_scalar(args[2])?.to_usize(this)?;
767785
let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
768-
let res = this.malloc(size, zero_init);
786+
let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap);
769787
this.write_scalar(res, dest)?;
770788
}
771789
"HeapFree" => {
772790
let _handle = this.read_scalar(args[0])?.to_isize(this)?;
773791
let _flags = this.read_scalar(args[1])?.to_u32()?;
774792
let ptr = this.read_scalar(args[2])?.not_undef()?;
775-
this.free(ptr)?;
793+
this.free(ptr, MiriMemoryKind::WinHeap)?;
776794
this.write_scalar(Scalar::from_int(1, Size::from_bytes(4)), dest)?;
777795
}
778796
"HeapReAlloc" => {
779797
let _handle = this.read_scalar(args[0])?.to_isize(this)?;
780798
let _flags = this.read_scalar(args[1])?.to_u32()?;
781799
let ptr = this.read_scalar(args[2])?.not_undef()?;
782800
let size = this.read_scalar(args[3])?.to_usize(this)?;
783-
let res = this.realloc(ptr, size)?;
801+
let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
784802
this.write_scalar(res, dest)?;
785803
}
786804

tests/run-pass/realloc.rs renamed to tests/run-pass/malloc.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//ignore-windows: Uses POSIX APIs
2+
//compile-flags: -Zmiri-seed=
23

34
#![feature(rustc_private)]
45

@@ -7,6 +8,16 @@ use core::{slice, ptr};
78
extern crate libc;
89

910
fn main() {
11+
// Test that small allocations sometimes *are* not very aligned.
12+
let saw_unaligned = (0..64).any(|_| unsafe {
13+
let p = libc::malloc(3);
14+
let addr = p as usize;
15+
let unaligned = addr % 4 != 0; // test that this is not 4-aligned
16+
libc::free(p); // FIXME have to free *after* test; should allow ptr-to-int of dangling ptr.
17+
unaligned
18+
});
19+
assert!(saw_unaligned);
20+
1021
unsafe {
1122
// Use calloc for initialized memory
1223
let p1 = libc::calloc(20, 1);

0 commit comments

Comments
 (0)