From d5d3e99f9a25870b042c734a417f529df48f5101 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 30 Jan 2024 08:28:12 -0500 Subject: [PATCH] Implement the `mmap64` foreign item. `mmap64` is like `mmap` but uses a 64-bit integer instead of `off_t` for the offset parameter. --- src/shims/unix/foreign_items.rs | 7 ++ src/shims/unix/mem.rs | 3 +- tests/pass-dep/shims/mmap.rs | 112 ++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 2 deletions(-) diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 35036ce078..1746ebdc85 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -262,6 +262,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "mmap" => { let [addr, length, prot, flags, fd, offset] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?; + let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?; + let ptr = this.mmap(addr, length, prot, flags, fd, offset)?; + this.write_scalar(ptr, dest)?; + } + "mmap64" => { + let [addr, length, prot, flags, fd, offset] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?; + let offset = this.read_scalar(offset)?.to_i64()?; let ptr = this.mmap(addr, length, prot, flags, fd, offset)?; this.write_scalar(ptr, dest)?; } diff --git a/src/shims/unix/mem.rs b/src/shims/unix/mem.rs index d7dc17fa89..d3470893db 100644 --- a/src/shims/unix/mem.rs +++ b/src/shims/unix/mem.rs @@ -26,7 +26,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { prot: &OpTy<'tcx, Provenance>, flags: &OpTy<'tcx, Provenance>, fd: &OpTy<'tcx, Provenance>, - offset: &OpTy<'tcx, Provenance>, + offset: i128, ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); @@ -36,7 +36,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let prot = this.read_scalar(prot)?.to_i32()?; let flags = this.read_scalar(flags)?.to_i32()?; let fd = this.read_scalar(fd)?.to_i32()?; - let offset = this.read_target_usize(offset)?; let map_private = this.eval_libc_i32("MAP_PRIVATE"); let map_anonymous = this.eval_libc_i32("MAP_ANONYMOUS"); diff --git a/tests/pass-dep/shims/mmap.rs b/tests/pass-dep/shims/mmap.rs index e19f54d068..98fd2b0089 100644 --- a/tests/pass-dep/shims/mmap.rs +++ b/tests/pass-dep/shims/mmap.rs @@ -116,6 +116,118 @@ fn test_mmap() { assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); } +#[cfg(target_os = "linux")] +fn test_mmap64() { + let page_size = page_size::get(); + let ptr = unsafe { + libc::mmap64( + ptr::null_mut(), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ) + }; + assert!(!ptr.is_null()); + + // Ensure that freshly mapped allocations are zeroed + let slice = unsafe { slice::from_raw_parts_mut(ptr as *mut u8, page_size) }; + assert!(slice.iter().all(|b| *b == 0)); + + // Do some writes, make sure they worked + for b in slice.iter_mut() { + *b = 1; + } + assert!(slice.iter().all(|b| *b == 1)); + + // Ensure that we can munmap + let res = unsafe { libc::munmap(ptr, page_size) }; + assert_eq!(res, 0i32); + + // Test all of our error conditions + let ptr = unsafe { + libc::mmap64( + ptr::null_mut(), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_SHARED, // Can't be both private and shared + -1, + 0, + ) + }; + assert_eq!(ptr, libc::MAP_FAILED); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); + + let ptr = unsafe { + libc::mmap64( + ptr::null_mut(), + 0, // Can't map no memory + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ) + }; + assert_eq!(ptr, libc::MAP_FAILED); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); + + let ptr = unsafe { + libc::mmap64( + ptr::invalid_mut(page_size * 64), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + // We don't support MAP_FIXED + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED, + -1, + 0, + ) + }; + assert_eq!(ptr, libc::MAP_FAILED); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOTSUP); + + // We don't support protections other than read+write + for prot in [libc::PROT_NONE, libc::PROT_EXEC, libc::PROT_READ, libc::PROT_WRITE] { + let ptr = unsafe { + libc::mmap64( + ptr::null_mut(), + page_size, + prot, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ) + }; + assert_eq!(ptr, libc::MAP_FAILED); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOTSUP); + } + + // We report an error for mappings whose length cannot be rounded up to a multiple of + // the page size. + let ptr = unsafe { + libc::mmap64( + ptr::null_mut(), + usize::MAX - 1, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ) + }; + assert_eq!(ptr, libc::MAP_FAILED); + + // We report an error when trying to munmap an address which is not a multiple of the page size + let res = unsafe { libc::munmap(ptr::invalid_mut(1), page_size) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); + + // We report an error when trying to munmap a length that cannot be rounded up to a multiple of + // the page size. + let res = unsafe { libc::munmap(ptr::invalid_mut(page_size), usize::MAX - 1) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); +} + #[cfg(target_os = "linux")] fn test_mremap() { let page_size = page_size::get();