From 3fdb6851bcd17bbcf06d4f6d97a62b1ba6a1d88a Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 30 Jan 2024 08:28:12 -0500 Subject: [PATCH 1/4] 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 | 114 ++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 2 deletions(-) diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 35036ce078..0f992e957c 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -262,9 +262,16 @@ 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.into())?; + this.write_scalar(ptr, dest)?; + } "munmap" => { let [addr, length] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?; let result = this.munmap(addr, length)?; 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..08faf76c00 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(); @@ -165,5 +277,7 @@ fn test_mremap() { fn main() { test_mmap(); #[cfg(target_os = "linux")] + test_mmap64(); + #[cfg(target_os = "linux")] test_mremap(); } From 25b49df9dffa106df264fc8b1c8904eee6b1c3c9 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Sat, 10 Feb 2024 08:10:04 -0800 Subject: [PATCH 2/4] Factor out the redundancy between `test_mmap` and `test_mmap64`. --- tests/pass-dep/shims/mmap.rs | 151 ++++++----------------------------- 1 file changed, 24 insertions(+), 127 deletions(-) diff --git a/tests/pass-dep/shims/mmap.rs b/tests/pass-dep/shims/mmap.rs index 08faf76c00..7bbb9dd53c 100644 --- a/tests/pass-dep/shims/mmap.rs +++ b/tests/pass-dep/shims/mmap.rs @@ -5,128 +5,25 @@ use std::io::Error; use std::{ptr, slice}; -fn test_mmap() { +fn test_mmap( + mmap: unsafe extern "C" fn( + *mut libc::c_void, + libc::size_t, + libc::c_int, + libc::c_int, + libc::c_int, + Offset, + ) -> *mut libc::c_void, +) { let page_size = page_size::get(); let ptr = unsafe { - libc::mmap( - 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::mmap( - 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::mmap( - 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::mmap( - 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::mmap( - 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::mmap( - 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_mmap64() { - let page_size = page_size::get(); - let ptr = unsafe { - libc::mmap64( + mmap( ptr::null_mut(), page_size, libc::PROT_READ | libc::PROT_WRITE, libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, -1, - 0, + Default::default(), ) }; assert!(!ptr.is_null()); @@ -147,40 +44,40 @@ fn test_mmap64() { // Test all of our error conditions let ptr = unsafe { - libc::mmap64( + mmap( 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, + Default::default(), ) }; assert_eq!(ptr, libc::MAP_FAILED); assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); let ptr = unsafe { - libc::mmap64( + mmap( ptr::null_mut(), 0, // Can't map no memory libc::PROT_READ | libc::PROT_WRITE, libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, -1, - 0, + Default::default(), ) }; assert_eq!(ptr, libc::MAP_FAILED); assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); let ptr = unsafe { - libc::mmap64( + mmap( 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, + Default::default(), ) }; assert_eq!(ptr, libc::MAP_FAILED); @@ -189,13 +86,13 @@ fn test_mmap64() { // 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( + mmap( ptr::null_mut(), page_size, prot, libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, -1, - 0, + Default::default(), ) }; assert_eq!(ptr, libc::MAP_FAILED); @@ -205,13 +102,13 @@ fn test_mmap64() { // We report an error for mappings whose length cannot be rounded up to a multiple of // the page size. let ptr = unsafe { - libc::mmap64( + mmap( ptr::null_mut(), usize::MAX - 1, libc::PROT_READ | libc::PROT_WRITE, libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, -1, - 0, + Default::default(), ) }; assert_eq!(ptr, libc::MAP_FAILED); @@ -275,9 +172,9 @@ fn test_mremap() { } fn main() { - test_mmap(); + test_mmap(libc::mmap); #[cfg(target_os = "linux")] - test_mmap64(); + test_mmap(libc::mmap64); #[cfg(target_os = "linux")] test_mremap(); } From 7a56a76dcbd97b99f5bac2234fd9038fcb20800f Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Sun, 11 Feb 2024 05:09:27 -0800 Subject: [PATCH 3/4] Make the `mmap64` implementation Linux-specific. `mmap64` is only defined on Linux, so move the implementation to Linux-specific code. --- src/shims/unix/foreign_items.rs | 6 ------ src/shims/unix/linux/foreign_items.rs | 9 +++++++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 0f992e957c..87cfe2907b 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -266,12 +266,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { 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.into())?; - this.write_scalar(ptr, dest)?; - } "munmap" => { let [addr, length] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?; let result = this.munmap(addr, length)?; diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index 6937e0f089..ee4e88e689 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -9,6 +9,7 @@ use shims::unix::fs::EvalContextExt as _; use shims::unix::linux::fd::EvalContextExt as _; use shims::unix::linux::mem::EvalContextExt as _; use shims::unix::linux::sync::futex; +use shims::unix::mem::EvalContextExt as _; use shims::unix::sync::EvalContextExt as _; use shims::unix::thread::EvalContextExt as _; @@ -217,6 +218,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_null(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.into())?; + this.write_scalar(ptr, dest)?; + } + _ => return Ok(EmulateForeignItemResult::NotSupported), }; From d914d5ebe027e2a46d476b821e54318b7dad118b Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Sun, 11 Feb 2024 05:26:53 -0800 Subject: [PATCH 4/4] Move `mmap64`'s implementation under "File related shims". --- src/shims/unix/linux/foreign_items.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index ee4e88e689..a05a6470a5 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -44,6 +44,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let result = this.linux_readdir64(dirp)?; this.write_scalar(result, 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.into())?; + this.write_scalar(ptr, dest)?; + } + // Linux-only "sync_file_range" => { let [fd, offset, nbytes, flags] = @@ -218,14 +226,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_null(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.into())?; - this.write_scalar(ptr, dest)?; - } - _ => return Ok(EmulateForeignItemResult::NotSupported), };