Skip to content

Commit 31b95dd

Browse files
committed
Auto merge of #2485 - 5225225:memalign, r=RalfJung
Breaking posix_memalign precondition is not UB The `size==0` test here might be overtesting, but I figured might as well test it and leave a comment saying it is fine to remove it if the implementation changes. Fixes #2099
2 parents ac655ce + e75b2c8 commit 31b95dd

File tree

2 files changed

+98
-19
lines changed

2 files changed

+98
-19
lines changed

src/shims/unix/foreign_items.rs

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -186,27 +186,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
186186
let align = this.read_scalar(align)?.to_machine_usize(this)?;
187187
let size = this.read_scalar(size)?.to_machine_usize(this)?;
188188
// Align must be power of 2, and also at least ptr-sized (POSIX rules).
189-
if !align.is_power_of_two() {
190-
throw_ub_format!("posix_memalign: alignment must be a power of two, but is {}", align);
191-
}
192-
if align < this.pointer_size().bytes() {
193-
throw_ub_format!(
194-
"posix_memalign: alignment must be at least the size of a pointer, but is {}",
195-
align,
196-
);
197-
}
198-
199-
if size == 0 {
200-
this.write_null(&ret.into())?;
189+
// But failure to adhere to this is not UB, it's an error condition.
190+
if !align.is_power_of_two() || align < this.pointer_size().bytes() {
191+
let einval = this.eval_libc_i32("EINVAL")?;
192+
this.write_int(einval, dest)?;
201193
} else {
202-
let ptr = this.allocate_ptr(
203-
Size::from_bytes(size),
204-
Align::from_bytes(align).unwrap(),
205-
MiriMemoryKind::C.into(),
206-
)?;
207-
this.write_pointer(ptr, &ret.into())?;
194+
if size == 0 {
195+
this.write_null(&ret.into())?;
196+
} else {
197+
let ptr = this.allocate_ptr(
198+
Size::from_bytes(size),
199+
Align::from_bytes(align).unwrap(),
200+
MiriMemoryKind::C.into(),
201+
)?;
202+
this.write_pointer(ptr, &ret.into())?;
203+
}
204+
this.write_null(dest)?;
208205
}
209-
this.write_null(dest)?;
210206
}
211207

212208
// Dynamic symbol loading

tests/pass/posix_memalign.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//@ignore-target-windows: No libc on Windows
2+
3+
#![feature(rustc_private)]
4+
#![feature(pointer_is_aligned)]
5+
#![feature(strict_provenance)]
6+
7+
use core::ptr;
8+
9+
fn main() {
10+
// A normal allocation.
11+
unsafe {
12+
let mut ptr: *mut libc::c_void = ptr::null_mut();
13+
let align = 8;
14+
let size = 64;
15+
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
16+
assert!(!ptr.is_null());
17+
assert!(ptr.is_aligned_to(align));
18+
ptr.cast::<u8>().write_bytes(1, size);
19+
libc::free(ptr);
20+
}
21+
22+
// Align > size.
23+
unsafe {
24+
let mut ptr: *mut libc::c_void = ptr::null_mut();
25+
let align = 64;
26+
let size = 8;
27+
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
28+
assert!(!ptr.is_null());
29+
assert!(ptr.is_aligned_to(align));
30+
ptr.cast::<u8>().write_bytes(1, size);
31+
libc::free(ptr);
32+
}
33+
34+
// Size not multiple of align
35+
unsafe {
36+
let mut ptr: *mut libc::c_void = ptr::null_mut();
37+
let align = 16;
38+
let size = 31;
39+
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
40+
assert!(!ptr.is_null());
41+
assert!(ptr.is_aligned_to(align));
42+
ptr.cast::<u8>().write_bytes(1, size);
43+
libc::free(ptr);
44+
}
45+
46+
// Size == 0
47+
unsafe {
48+
let mut ptr: *mut libc::c_void = ptr::null_mut();
49+
let align = 64;
50+
let size = 0;
51+
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
52+
// We are not required to return null if size == 0, but we currently do.
53+
// It's fine to remove this assert if we start returning non-null pointers.
54+
assert!(ptr.is_null());
55+
assert!(ptr.is_aligned_to(align));
56+
// Regardless of what we return, it must be `free`able.
57+
libc::free(ptr);
58+
}
59+
60+
// Non-power of 2 align
61+
unsafe {
62+
let mut ptr: *mut libc::c_void = ptr::invalid_mut(0x1234567);
63+
let align = 15;
64+
let size = 8;
65+
assert_eq!(libc::posix_memalign(&mut ptr, align, size), libc::EINVAL);
66+
// The pointer is not modified on failure, posix_memalign(3) says:
67+
// > On Linux (and other systems), posix_memalign() does not modify memptr on failure.
68+
// > A requirement standardizing this behavior was added in POSIX.1-2008 TC2.
69+
assert_eq!(ptr.addr(), 0x1234567);
70+
}
71+
72+
// Too small align (smaller than ptr)
73+
unsafe {
74+
let mut ptr: *mut libc::c_void = ptr::invalid_mut(0x1234567);
75+
let align = std::mem::size_of::<usize>() / 2;
76+
let size = 8;
77+
assert_eq!(libc::posix_memalign(&mut ptr, align, size), libc::EINVAL);
78+
// The pointer is not modified on failure, posix_memalign(3) says:
79+
// > On Linux (and other systems), posix_memalign() does not modify memptr on failure.
80+
// > A requirement standardizing this behavior was added in POSIX.1-2008 TC2.
81+
assert_eq!(ptr.addr(), 0x1234567);
82+
}
83+
}

0 commit comments

Comments
 (0)