use crate::error::MemoryError;
pub fn mmap<T>(
len: usize,
prot: i32,
flags: i32,
fd: i32,
offset: isize,
) -> Result<*mut T, MemoryError> {
let mmap_offset = if cfg!(target_pointer_width = "32") {
offset as i32 as libc::off_t
} else {
offset as libc::off_t };
let ptr = unsafe { libc::mmap(std::ptr::null_mut(), len, prot, flags, fd, mmap_offset) };
if ptr == libc::MAP_FAILED {
Err(std::io::Error::last_os_error().into())
} else {
Ok(ptr as *mut T)
}
}
pub fn mprotect<T>(ptr: *mut T, len: usize, prot: i32) -> Result<(), MemoryError> {
if unsafe { libc::mprotect(ptr as *mut libc::c_void, len, prot) } != 0 {
Err(std::io::Error::last_os_error().into())
} else {
Ok(())
}
}
pub fn mlock<T>(ptr: *mut T, len: usize) -> Result<(), MemoryError> {
if unsafe { libc::mlock(ptr as *const libc::c_void, len) } != 0 {
Err(std::io::Error::last_os_error().into())
} else {
Ok(())
}
}
#[cfg(target_os = "linux")]
pub fn madvice<T>(ptr: *mut T, len: usize, advice: i32) -> Result<(), MemoryError> {
if unsafe { libc::madvise(ptr as *mut libc::c_void, len, advice) } != 0 {
Err(std::io::Error::last_os_error().into())
} else {
Ok(())
}
}
pub fn munlock<T>(ptr: *mut T, len: usize) -> Result<(), MemoryError> {
if unsafe { libc::munlock(ptr as *mut libc::c_void, len) } != 0 {
Err(std::io::Error::last_os_error().into())
} else {
Ok(())
}
}
pub fn munmap<T>(ptr: *mut T, len: usize) -> Result<(), MemoryError> {
if unsafe { libc::munmap(ptr as *mut libc::c_void, len) } != 0 {
Err(std::io::Error::last_os_error().into())
} else {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
const PAGE: usize = 4096;
#[test]
fn mmap_error_on_absurd_length() {
let result = mmap::<u8>(
usize::MAX,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
-1,
0,
);
assert!(result.is_err());
}
#[test]
fn mprotect_error_on_unmapped_address() {
let result = mprotect(std::ptr::null_mut::<u8>(), PAGE, libc::PROT_READ);
assert!(result.is_err());
}
#[test]
fn mlock_error_on_out_of_range_address() {
let ptr = (usize::MAX & !(PAGE - 1)) as *mut u8;
let result = mlock(ptr, PAGE);
assert!(result.is_err());
}
fn emulated_kernel() -> bool {
std::env::vars().any(|(k, _)| {
k == "QEMU_LD_PREFIX"
|| k == "CROSS_RUNNER"
|| (k.starts_with("CARGO_TARGET_") && k.ends_with("_RUNNER"))
})
}
#[test]
fn munlock_error_on_unmapped_page() {
let ptr = (usize::MAX & !(PAGE - 1)) as *mut u8;
let result = munlock(ptr, PAGE);
if emulated_kernel() && result.is_ok() {
eprintln!("skipping: qemu user-mode emulation does not reproduce munlock semantics");
return;
}
assert!(result.is_err());
}
#[test]
fn munmap_error_on_unaligned_pointer() {
let result = munmap(std::ptr::dangling_mut::<u8>(), PAGE);
assert!(result.is_err());
}
}