Skip to content

Commit 3726afa

Browse files
committed
Auto merge of rust-lang#3585 - devnexen:aligned_alloc, r=RalfJung
support aligned_alloc for unixes. Fixes rust-lang/miri#3577
2 parents 8d7c8ac + 430298c commit 3726afa

File tree

6 files changed

+171
-44
lines changed

6 files changed

+171
-44
lines changed

src/tools/miri/src/shims/alloc.rs

+41
Original file line numberDiff line numberDiff line change
@@ -172,4 +172,45 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
172172
}
173173
}
174174
}
175+
176+
fn aligned_alloc(
177+
&mut self,
178+
align: &OpTy<'tcx, Provenance>,
179+
size: &OpTy<'tcx, Provenance>,
180+
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
181+
let this = self.eval_context_mut();
182+
let align = this.read_target_usize(align)?;
183+
let size = this.read_target_usize(size)?;
184+
185+
// Alignment must be a power of 2, and "supported by the implementation".
186+
// We decide that "supported by the implementation" means that the
187+
// size must be a multiple of the alignment. (This restriction seems common
188+
// enough that it is stated on <https://en.cppreference.com/w/c/memory/aligned_alloc>
189+
// as a general rule, but the actual standard has no such rule.)
190+
// If any of these are violated, we have to return NULL.
191+
// All fundamental alignments must be supported.
192+
//
193+
// macOS and Illumos are buggy in that they require the alignment
194+
// to be at least the size of a pointer, so they do not support all fundamental
195+
// alignments. We do not emulate those platform bugs.
196+
//
197+
// Linux also sets errno to EINVAL, but that's non-standard behavior that we do not
198+
// emulate.
199+
// FreeBSD says some of these cases are UB but that's violating the C standard.
200+
// http://en.cppreference.com/w/cpp/memory/c/aligned_alloc
201+
// Linux: https://linux.die.net/man/3/aligned_alloc
202+
// FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=aligned_alloc&apropos=0&sektion=3&manpath=FreeBSD+9-current&format=html
203+
match size.checked_rem(align) {
204+
Some(0) if align.is_power_of_two() => {
205+
let align = align.max(this.malloc_align(size).bytes());
206+
let ptr = this.allocate_ptr(
207+
Size::from_bytes(size),
208+
Align::from_bytes(align).unwrap(),
209+
MiriMemoryKind::C.into(),
210+
)?;
211+
Ok(ptr.into())
212+
}
213+
_ => Ok(Pointer::null()),
214+
}
215+
}
175216
}

src/tools/miri/src/shims/unix/foreign_items.rs

+8
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
295295
}
296296
}
297297
}
298+
"aligned_alloc" => {
299+
// This is a C11 function, we assume all Unixes have it.
300+
// (MSVC explicitly does not support this.)
301+
let [align, size] =
302+
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
303+
let res = this.aligned_alloc(align, size)?;
304+
this.write_pointer(res, dest)?;
305+
}
298306

299307
// Dynamic symbol loading
300308
"dlsym" => {

src/tools/miri/src/shims/wasi/foreign_items.rs

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
2626
let result = this.posix_memalign(memptr, align, size)?;
2727
this.write_scalar(result, dest)?;
2828
}
29+
"aligned_alloc" => {
30+
let [align, size] =
31+
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
32+
let res = this.aligned_alloc(align, size)?;
33+
this.write_pointer(res, dest)?;
34+
}
2935

3036
_ => return Ok(EmulateItemResult::NotSupported),
3137
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ignore-target-windows: Windows does not support the standard C11 aligned_alloc.
2+
3+
fn main() {
4+
// libc doesn't have this function (https://github.com/rust-lang/libc/issues/3689),
5+
// so we declare it ourselves.
6+
extern "C" {
7+
fn aligned_alloc(alignment: libc::size_t, size: libc::size_t) -> *mut libc::c_void;
8+
}
9+
10+
// Make sure even zero-sized allocations need to be freed.
11+
12+
unsafe {
13+
aligned_alloc(2, 0); //~ERROR: memory leaked
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: memory leaked: ALLOC (C heap, size: 0, align: 2), allocated here:
2+
--> $DIR/aligned_alloc_size_zero_leak.rs:LL:CC
3+
|
4+
LL | aligned_alloc(2, 0);
5+
| ^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: BACKTRACE:
8+
= note: inside `main` at $DIR/aligned_alloc_size_zero_leak.rs:LL:CC
9+
10+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
11+
12+
note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check
13+
14+
error: aborting due to 1 previous error
15+

src/tools/miri/tests/pass-dep/libc/libc-mem.rs

+86-44
Original file line numberDiff line numberDiff line change
@@ -148,53 +148,55 @@ fn test_calloc() {
148148

149149
#[cfg(not(target_os = "windows"))]
150150
fn test_memalign() {
151-
// A normal allocation.
152-
unsafe {
153-
let mut ptr: *mut libc::c_void = ptr::null_mut();
154-
let align = 8;
155-
let size = 64;
156-
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
157-
assert!(!ptr.is_null());
158-
assert!(ptr.is_aligned_to(align));
159-
ptr.cast::<u8>().write_bytes(1, size);
160-
libc::free(ptr);
161-
}
151+
for _ in 0..16 {
152+
// A normal allocation.
153+
unsafe {
154+
let mut ptr: *mut libc::c_void = ptr::null_mut();
155+
let align = 8;
156+
let size = 64;
157+
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
158+
assert!(!ptr.is_null());
159+
assert!(ptr.is_aligned_to(align));
160+
ptr.cast::<u8>().write_bytes(1, size);
161+
libc::free(ptr);
162+
}
162163

163-
// Align > size.
164-
unsafe {
165-
let mut ptr: *mut libc::c_void = ptr::null_mut();
166-
let align = 64;
167-
let size = 8;
168-
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
169-
assert!(!ptr.is_null());
170-
assert!(ptr.is_aligned_to(align));
171-
ptr.cast::<u8>().write_bytes(1, size);
172-
libc::free(ptr);
173-
}
164+
// Align > size.
165+
unsafe {
166+
let mut ptr: *mut libc::c_void = ptr::null_mut();
167+
let align = 64;
168+
let size = 8;
169+
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
170+
assert!(!ptr.is_null());
171+
assert!(ptr.is_aligned_to(align));
172+
ptr.cast::<u8>().write_bytes(1, size);
173+
libc::free(ptr);
174+
}
174175

175-
// Size not multiple of align
176-
unsafe {
177-
let mut ptr: *mut libc::c_void = ptr::null_mut();
178-
let align = 16;
179-
let size = 31;
180-
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
181-
assert!(!ptr.is_null());
182-
assert!(ptr.is_aligned_to(align));
183-
ptr.cast::<u8>().write_bytes(1, size);
184-
libc::free(ptr);
185-
}
176+
// Size not multiple of align
177+
unsafe {
178+
let mut ptr: *mut libc::c_void = ptr::null_mut();
179+
let align = 16;
180+
let size = 31;
181+
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
182+
assert!(!ptr.is_null());
183+
assert!(ptr.is_aligned_to(align));
184+
ptr.cast::<u8>().write_bytes(1, size);
185+
libc::free(ptr);
186+
}
186187

187-
// Size == 0
188-
unsafe {
189-
let mut ptr: *mut libc::c_void = ptr::null_mut();
190-
let align = 64;
191-
let size = 0;
192-
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
193-
// Non-null pointer is returned if size == 0.
194-
// (This is not a guarantee, it just reflects our current behavior.)
195-
assert!(!ptr.is_null());
196-
assert!(ptr.is_aligned_to(align));
197-
libc::free(ptr);
188+
// Size == 0
189+
unsafe {
190+
let mut ptr: *mut libc::c_void = ptr::null_mut();
191+
let align = 64;
192+
let size = 0;
193+
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
194+
// Non-null pointer is returned if size == 0.
195+
// (This is not a guarantee, it just reflects our current behavior.)
196+
assert!(!ptr.is_null());
197+
assert!(ptr.is_aligned_to(align));
198+
libc::free(ptr);
199+
}
198200
}
199201

200202
// Non-power of 2 align
@@ -241,6 +243,44 @@ fn test_reallocarray() {
241243
}
242244
}
243245

246+
#[cfg(not(target_os = "windows"))]
247+
fn test_aligned_alloc() {
248+
// libc doesn't have this function (https://github.com/rust-lang/libc/issues/3689),
249+
// so we declare it ourselves.
250+
extern "C" {
251+
fn aligned_alloc(alignment: libc::size_t, size: libc::size_t) -> *mut libc::c_void;
252+
}
253+
// size not a multiple of the alignment
254+
unsafe {
255+
let p = aligned_alloc(16, 3);
256+
assert_eq!(p, ptr::null_mut());
257+
}
258+
259+
// alignment not power of 2
260+
unsafe {
261+
let p = aligned_alloc(63, 8);
262+
assert_eq!(p, ptr::null_mut());
263+
}
264+
265+
// repeated tests on correct alignment/size
266+
for _ in 0..16 {
267+
// alignment 1, size 4 should succeed and actually must align to 4 (because C says so...)
268+
unsafe {
269+
let p = aligned_alloc(1, 4);
270+
assert!(!p.is_null());
271+
assert!(p.is_aligned_to(4));
272+
libc::free(p);
273+
}
274+
275+
unsafe {
276+
let p = aligned_alloc(64, 64);
277+
assert!(!p.is_null());
278+
assert!(p.is_aligned_to(64));
279+
libc::free(p);
280+
}
281+
}
282+
}
283+
244284
fn main() {
245285
test_malloc();
246286
test_calloc();
@@ -254,6 +294,8 @@ fn main() {
254294
target_os = "wasi",
255295
)))]
256296
test_reallocarray();
297+
#[cfg(not(target_os = "windows"))]
298+
test_aligned_alloc();
257299

258300
test_memcpy();
259301
test_strcpy();

0 commit comments

Comments
 (0)