Skip to content

Commit ad83707

Browse files
committed
Auto merge of #817 - RalfJung:small-alloc, r=RalfJung
align small malloc-allocations even less, and test that we do Needs #62295 to land. Fixes rust-lang/miri#812.
2 parents 285fc0d + 029a294 commit ad83707

File tree

5 files changed

+73
-44
lines changed

5 files changed

+73
-44
lines changed

rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
088b987307b91612ab164026e1dcdd0129fdb62b
1+
24a9bcbb7cb0d8bdc11b8252a9c13f7562c7e4ca

src/eval.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc::hir::def_id::DefId;
88
use rustc::mir;
99

1010
use crate::{
11-
InterpResult, InterpError, InterpretCx, StackPopCleanup, struct_error,
11+
InterpResult, InterpError, InterpCx, StackPopCleanup, struct_error,
1212
Scalar, Tag, Pointer,
1313
MemoryExtra, MiriMemoryKind, Evaluator, TlsEvalContextExt,
1414
};
@@ -28,8 +28,8 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
2828
tcx: TyCtxt<'tcx>,
2929
main_id: DefId,
3030
config: MiriConfig,
31-
) -> InterpResult<'tcx, InterpretCx<'mir, 'tcx, Evaluator<'tcx>>> {
32-
let mut ecx = InterpretCx::new(
31+
) -> InterpResult<'tcx, InterpCx<'mir, 'tcx, Evaluator<'tcx>>> {
32+
let mut ecx = InterpCx::new(
3333
tcx.at(syntax::source_map::DUMMY_SP),
3434
ty::ParamEnv::reveal_all(),
3535
Evaluator::new(),
@@ -43,7 +43,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
4343
config.validate
4444
};
4545

46-
// FIXME: InterpretCx::new should take an initial MemoryExtra
46+
// FIXME: InterpCx::new should take an initial MemoryExtra
4747
ecx.memory_mut().extra = MemoryExtra::new(config.seed.map(StdRng::seed_from_u64), validate);
4848

4949
let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);

src/machine.rs

Lines changed: 14 additions & 12 deletions
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.
@@ -103,8 +105,8 @@ impl<'tcx> Evaluator<'tcx> {
103105
}
104106
}
105107

106-
/// A rustc InterpretCx for Miri.
107-
pub type MiriEvalContext<'mir, 'tcx> = InterpretCx<'mir, 'tcx, Evaluator<'tcx>>;
108+
/// A rustc InterpCx for Miri.
109+
pub type MiriEvalContext<'mir, 'tcx> = InterpCx<'mir, 'tcx, Evaluator<'tcx>>;
108110

109111
/// A little trait that's useful to be inherited by extension traits.
110112
pub trait MiriEvalContextExt<'mir, 'tcx> {
@@ -136,14 +138,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
136138
const STATIC_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Static);
137139

138140
#[inline(always)]
139-
fn enforce_validity(ecx: &InterpretCx<'mir, 'tcx, Self>) -> bool {
141+
fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
140142
ecx.memory().extra.validate
141143
}
142144

143145
/// Returns `Ok()` when the function was handled; fail otherwise.
144146
#[inline(always)]
145147
fn find_fn(
146-
ecx: &mut InterpretCx<'mir, 'tcx, Self>,
148+
ecx: &mut InterpCx<'mir, 'tcx, Self>,
147149
instance: ty::Instance<'tcx>,
148150
args: &[OpTy<'tcx, Tag>],
149151
dest: Option<PlaceTy<'tcx, Tag>>,
@@ -154,7 +156,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
154156

155157
#[inline(always)]
156158
fn call_intrinsic(
157-
ecx: &mut rustc_mir::interpret::InterpretCx<'mir, 'tcx, Self>,
159+
ecx: &mut rustc_mir::interpret::InterpCx<'mir, 'tcx, Self>,
158160
instance: ty::Instance<'tcx>,
159161
args: &[OpTy<'tcx, Tag>],
160162
dest: PlaceTy<'tcx, Tag>,
@@ -164,7 +166,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
164166

165167
#[inline(always)]
166168
fn ptr_op(
167-
ecx: &rustc_mir::interpret::InterpretCx<'mir, 'tcx, Self>,
169+
ecx: &rustc_mir::interpret::InterpCx<'mir, 'tcx, Self>,
168170
bin_op: mir::BinOp,
169171
left: ImmTy<'tcx, Tag>,
170172
right: ImmTy<'tcx, Tag>,
@@ -173,7 +175,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
173175
}
174176

175177
fn box_alloc(
176-
ecx: &mut InterpretCx<'mir, 'tcx, Self>,
178+
ecx: &mut InterpCx<'mir, 'tcx, Self>,
177179
dest: PlaceTy<'tcx, Tag>,
178180
) -> InterpResult<'tcx> {
179181
trace!("box_alloc for {:?}", dest.layout.ty);
@@ -239,7 +241,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
239241
}
240242

241243
#[inline(always)]
242-
fn before_terminator(_ecx: &mut InterpretCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>
244+
fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>
243245
{
244246
// We are not interested in detecting loops.
245247
Ok(())
@@ -309,7 +311,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
309311

310312
#[inline(always)]
311313
fn retag(
312-
ecx: &mut InterpretCx<'mir, 'tcx, Self>,
314+
ecx: &mut InterpCx<'mir, 'tcx, Self>,
313315
kind: mir::RetagKind,
314316
place: PlaceTy<'tcx, Tag>,
315317
) -> InterpResult<'tcx> {
@@ -323,14 +325,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
323325

324326
#[inline(always)]
325327
fn stack_push(
326-
ecx: &mut InterpretCx<'mir, 'tcx, Self>,
328+
ecx: &mut InterpCx<'mir, 'tcx, Self>,
327329
) -> InterpResult<'tcx, stacked_borrows::CallId> {
328330
Ok(ecx.memory().extra.stacked_borrows.borrow_mut().new_call())
329331
}
330332

331333
#[inline(always)]
332334
fn stack_pop(
333-
ecx: &mut InterpretCx<'mir, 'tcx, Self>,
335+
ecx: &mut InterpCx<'mir, 'tcx, Self>,
334336
extra: stacked_borrows::CallId,
335337
) -> InterpResult<'tcx> {
336338
Ok(ecx.memory().extra.stacked_borrows.borrow_mut().end_call(extra))
@@ -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: 45 additions & 27 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)?;
@@ -327,7 +345,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
327345
trace!("__rust_maybe_catch_panic: {:?}", f_instance);
328346

329347
// Now we make a function call.
330-
// TODO: consider making this reusable? `InterpretCx::step` does something similar
348+
// TODO: consider making this reusable? `InterpCx::step` does something similar
331349
// for the TLS destructors, and of course `eval_main`.
332350
let mir = this.load_mir(f_instance.def)?;
333351
let ret_place = MPlaceTy::dangling(this.layout_of(this.tcx.mk_unit())?, this).into();
@@ -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-noseed/malloc.rs

Lines changed: 9 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,14 @@ 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+
libc::free(p);
15+
(p as usize) % 4 != 0 // find any that this is *not* 4-aligned
16+
});
17+
assert!(saw_unaligned);
18+
1019
unsafe {
1120
// Use calloc for initialized memory
1221
let p1 = libc::calloc(20, 1);

0 commit comments

Comments
 (0)