diff options
| author | bors <bors@rust-lang.org> | 2024-05-19 09:50:25 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-05-19 09:50:25 +0000 |
| commit | 3726afad7edc24a4ae0c3a16f6f2a18921d5c405 (patch) | |
| tree | 9acb38606099fdb26df797211bdf059b1f63f41a | |
| parent | 8d7c8ac50acc59d80c6d10508b3e2b91c86d33f9 (diff) | |
| parent | 430298c3adc16e09d9fbd8f295b404fdb682d9eb (diff) | |
| download | rust-3726afad7edc24a4ae0c3a16f6f2a18921d5c405.tar.gz rust-3726afad7edc24a4ae0c3a16f6f2a18921d5c405.zip | |
Auto merge of #3585 - devnexen:aligned_alloc, r=RalfJung
support aligned_alloc for unixes. Fixes https://github.com/rust-lang/miri/issues/3577
6 files changed, 171 insertions, 44 deletions
diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index d0f36bd4757..bd84de81e69 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -172,4 +172,45 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } } + + fn aligned_alloc( + &mut self, + align: &OpTy<'tcx, Provenance>, + size: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Pointer<Option<Provenance>>> { + let this = self.eval_context_mut(); + let align = this.read_target_usize(align)?; + let size = this.read_target_usize(size)?; + + // Alignment must be a power of 2, and "supported by the implementation". + // We decide that "supported by the implementation" means that the + // size must be a multiple of the alignment. (This restriction seems common + // enough that it is stated on <https://en.cppreference.com/w/c/memory/aligned_alloc> + // as a general rule, but the actual standard has no such rule.) + // If any of these are violated, we have to return NULL. + // All fundamental alignments must be supported. + // + // macOS and Illumos are buggy in that they require the alignment + // to be at least the size of a pointer, so they do not support all fundamental + // alignments. We do not emulate those platform bugs. + // + // Linux also sets errno to EINVAL, but that's non-standard behavior that we do not + // emulate. + // FreeBSD says some of these cases are UB but that's violating the C standard. + // http://en.cppreference.com/w/cpp/memory/c/aligned_alloc + // Linux: https://linux.die.net/man/3/aligned_alloc + // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=aligned_alloc&apropos=0&sektion=3&manpath=FreeBSD+9-current&format=html + match size.checked_rem(align) { + Some(0) if align.is_power_of_two() => { + let align = align.max(this.malloc_align(size).bytes()); + let ptr = this.allocate_ptr( + Size::from_bytes(size), + Align::from_bytes(align).unwrap(), + MiriMemoryKind::C.into(), + )?; + Ok(ptr.into()) + } + _ => Ok(Pointer::null()), + } + } } diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 78d297d4b04..74fb2fb4b2b 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -295,6 +295,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } } + "aligned_alloc" => { + // This is a C11 function, we assume all Unixes have it. + // (MSVC explicitly does not support this.) + let [align, size] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let res = this.aligned_alloc(align, size)?; + this.write_pointer(res, dest)?; + } // Dynamic symbol loading "dlsym" => { diff --git a/src/tools/miri/src/shims/wasi/foreign_items.rs b/src/tools/miri/src/shims/wasi/foreign_items.rs index 12bf0490932..d911f43eb62 100644 --- a/src/tools/miri/src/shims/wasi/foreign_items.rs +++ b/src/tools/miri/src/shims/wasi/foreign_items.rs @@ -26,6 +26,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let result = this.posix_memalign(memptr, align, size)?; this.write_scalar(result, dest)?; } + "aligned_alloc" => { + let [align, size] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let res = this.aligned_alloc(align, size)?; + this.write_pointer(res, dest)?; + } _ => return Ok(EmulateItemResult::NotSupported), } diff --git a/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.rs b/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.rs new file mode 100644 index 00000000000..9a33cdccd27 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.rs @@ -0,0 +1,15 @@ +//@ignore-target-windows: Windows does not support the standard C11 aligned_alloc. + +fn main() { + // libc doesn't have this function (https://github.com/rust-lang/libc/issues/3689), + // so we declare it ourselves. + extern "C" { + fn aligned_alloc(alignment: libc::size_t, size: libc::size_t) -> *mut libc::c_void; + } + + // Make sure even zero-sized allocations need to be freed. + + unsafe { + aligned_alloc(2, 0); //~ERROR: memory leaked + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.stderr b/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.stderr new file mode 100644 index 00000000000..b0756d57212 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.stderr @@ -0,0 +1,15 @@ +error: memory leaked: ALLOC (C heap, size: 0, align: 2), allocated here: + --> $DIR/aligned_alloc_size_zero_leak.rs:LL:CC + | +LL | aligned_alloc(2, 0); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: BACKTRACE: + = note: inside `main` at $DIR/aligned_alloc_size_zero_leak.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs index 5bd205dd085..aa383a99bc2 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs @@ -148,53 +148,55 @@ fn test_calloc() { #[cfg(not(target_os = "windows"))] fn test_memalign() { - // A normal allocation. - unsafe { - let mut ptr: *mut libc::c_void = ptr::null_mut(); - let align = 8; - let size = 64; - assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); - assert!(!ptr.is_null()); - assert!(ptr.is_aligned_to(align)); - ptr.cast::<u8>().write_bytes(1, size); - libc::free(ptr); - } + for _ in 0..16 { + // A normal allocation. + unsafe { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 8; + let size = 64; + assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); + assert!(!ptr.is_null()); + assert!(ptr.is_aligned_to(align)); + ptr.cast::<u8>().write_bytes(1, size); + libc::free(ptr); + } - // Align > size. - unsafe { - let mut ptr: *mut libc::c_void = ptr::null_mut(); - let align = 64; - let size = 8; - assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); - assert!(!ptr.is_null()); - assert!(ptr.is_aligned_to(align)); - ptr.cast::<u8>().write_bytes(1, size); - libc::free(ptr); - } + // Align > size. + unsafe { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 64; + let size = 8; + assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); + assert!(!ptr.is_null()); + assert!(ptr.is_aligned_to(align)); + ptr.cast::<u8>().write_bytes(1, size); + libc::free(ptr); + } - // Size not multiple of align - unsafe { - let mut ptr: *mut libc::c_void = ptr::null_mut(); - let align = 16; - let size = 31; - assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); - assert!(!ptr.is_null()); - assert!(ptr.is_aligned_to(align)); - ptr.cast::<u8>().write_bytes(1, size); - libc::free(ptr); - } + // Size not multiple of align + unsafe { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 16; + let size = 31; + assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); + assert!(!ptr.is_null()); + assert!(ptr.is_aligned_to(align)); + ptr.cast::<u8>().write_bytes(1, size); + libc::free(ptr); + } - // Size == 0 - unsafe { - let mut ptr: *mut libc::c_void = ptr::null_mut(); - let align = 64; - let size = 0; - assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); - // Non-null pointer is returned if size == 0. - // (This is not a guarantee, it just reflects our current behavior.) - assert!(!ptr.is_null()); - assert!(ptr.is_aligned_to(align)); - libc::free(ptr); + // Size == 0 + unsafe { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 64; + let size = 0; + assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); + // Non-null pointer is returned if size == 0. + // (This is not a guarantee, it just reflects our current behavior.) + assert!(!ptr.is_null()); + assert!(ptr.is_aligned_to(align)); + libc::free(ptr); + } } // Non-power of 2 align @@ -241,6 +243,44 @@ fn test_reallocarray() { } } +#[cfg(not(target_os = "windows"))] +fn test_aligned_alloc() { + // libc doesn't have this function (https://github.com/rust-lang/libc/issues/3689), + // so we declare it ourselves. + extern "C" { + fn aligned_alloc(alignment: libc::size_t, size: libc::size_t) -> *mut libc::c_void; + } + // size not a multiple of the alignment + unsafe { + let p = aligned_alloc(16, 3); + assert_eq!(p, ptr::null_mut()); + } + + // alignment not power of 2 + unsafe { + let p = aligned_alloc(63, 8); + assert_eq!(p, ptr::null_mut()); + } + + // repeated tests on correct alignment/size + for _ in 0..16 { + // alignment 1, size 4 should succeed and actually must align to 4 (because C says so...) + unsafe { + let p = aligned_alloc(1, 4); + assert!(!p.is_null()); + assert!(p.is_aligned_to(4)); + libc::free(p); + } + + unsafe { + let p = aligned_alloc(64, 64); + assert!(!p.is_null()); + assert!(p.is_aligned_to(64)); + libc::free(p); + } + } +} + fn main() { test_malloc(); test_calloc(); @@ -254,6 +294,8 @@ fn main() { target_os = "wasi", )))] test_reallocarray(); + #[cfg(not(target_os = "windows"))] + test_aligned_alloc(); test_memcpy(); test_strcpy(); |
