use errno::errno;
use libc::{syscall, EINVAL, ENOMEM, EPERM};
use crate::errors::RSealError;
unsafe fn raw_mseal(
addr: *mut libc::c_void,
len: libc::size_t,
flags: libc::c_ulong,
) -> libc::c_int {
#[cfg(target_arch = "x86_64")]
let syscall_num = libc::SYS_mseal as libc::c_long;
#[cfg(not(target_arch = "x86_64"))]
let syscall_num = SYS_mseal as libc::c_long;
syscall(syscall_num, addr, len, flags) as libc::c_int
}
pub unsafe fn seal_memory(
addr: *mut libc::c_void,
len: libc::size_t,
flags: libc::c_ulong,
) -> Result<(), RSealError> {
if addr.is_null() {
return Err(RSealError::InvalidInput("Address pointer is null".into()));
}
if len == 0 {
return Err(RSealError::InvalidInput("Length cannot be zero".into()));
}
let page_size = unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) } as usize;
if (addr as usize) % page_size != 0 {
return Err(RSealError::InvalidInput(format!(
"Address must be page-aligned ({} bytes)",
page_size
)));
}
if len % page_size != 0 {
return Err(RSealError::InvalidInput(format!(
"Length must be a multiple of page size ({} bytes)",
page_size
)));
}
let result = raw_mseal(addr, len, flags);
if result == 0 {
Ok(())
} else {
let current_errno = errno();
match current_errno.0 {
EINVAL => Err(RSealError::InvalidInput(format!(
"EINVAL: Invalid argument to mseal: {}",
current_errno
))),
ENOMEM => Err(RSealError::MemoryError(format!(
"ENOMEM: Not enough memory or address range invalid: {}",
current_errno
))),
EPERM => Err(RSealError::PermissionError(format!(
"EPERM: Operation not permitted, possibly already sealed: {}",
current_errno
))),
_ => {
if current_errno.0 == libc::ENOSYS {
Err(RSealError::SyscallNotImplemented(format!(
"ENOSYS: mseal syscall not implemented: {}",
current_errno
)))
} else {
Err(RSealError::UnknownError(format!(
"Unknown error from mseal, errno: {}",
current_errno
)))
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use libc::{mmap, mprotect, munmap, MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE};
use std::ptr;
#[test]
fn test_seal_memory_basic() {
unsafe {
let page_size = libc::sysconf(libc::_SC_PAGE_SIZE) as usize;
let mem_size = page_size; let addr = mmap(
ptr::null_mut(),
mem_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0,
);
if addr == libc::MAP_FAILED {
panic!("mmap failed: {}", std::io::Error::last_os_error());
}
let seal_result = seal_memory(addr, mem_size, 0);
if let Err(RSealError::SyscallNotImplemented(_)) = seal_result {
eprintln!("Skipping test_seal_memory_basic: mseal syscall not implemented");
return;
}
assert!(seal_result.is_ok(), "seal_memory failed: {:?}", seal_result);
let mprotect_result = mprotect(addr, mem_size, PROT_READ);
assert_ne!(
mprotect_result, 0,
"mprotect unexpectedly succeeded after mseal"
);
assert_eq!(
errno::errno().0,
EPERM,
"Expected EPERM after mseal, got {:?}",
errno::errno()
);
if munmap(addr, mem_size) != 0 {
eprintln!(
"Warning: munmap failed during cleanup: {}",
std::io::Error::last_os_error()
);
}
}
}
#[test]
fn test_seal_memory_invalid_addr() {
unsafe {
let page_size = libc::sysconf(libc::_SC_PAGE_SIZE) as usize;
let mem_size = page_size;
let invalid_addr = 1 as *mut libc::c_void;
let seal_result = seal_memory(invalid_addr, mem_size, 0);
assert!(
seal_result.is_err(),
"seal_memory should have failed with invalid addr"
);
assert!(
matches!(seal_result, Err(RSealError::InvalidInput(_))),
"Expected InvalidInput error"
);
}
}
#[test]
fn test_seal_memory_not_page_aligned_addr() {
unsafe {
let page_size = libc::sysconf(libc::_SC_PAGE_SIZE) as usize;
let mem_size = page_size;
let addr = mmap(
ptr::null_mut(),
mem_size + 1, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0,
);
if addr == libc::MAP_FAILED {
panic!("mmap failed: {}", std::io::Error::last_os_error());
}
let unaligned_addr = addr.add(1) as *mut libc::c_void;
let seal_result = seal_memory(unaligned_addr, mem_size, 0); assert!(
seal_result.is_err(),
"seal_memory should have failed with unaligned addr"
);
assert!(
matches!(seal_result, Err(RSealError::InvalidInput(_))),
"Expected InvalidInput error"
);
if munmap(addr, mem_size + 1) != 0 {
eprintln!(
"Warning: munmap failed during cleanup: {}",
std::io::Error::last_os_error()
);
}
}
}
#[test]
fn test_seal_memory_invalid_len() {
unsafe {
let page_size = libc::sysconf(libc::_SC_PAGE_SIZE) as usize;
let mem_size = page_size - 1;
let addr = mmap(
ptr::null_mut(),
page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0,
);
if addr == libc::MAP_FAILED {
panic!("mmap failed: {}", std::io::Error::last_os_error());
}
let seal_result = seal_memory(addr, mem_size, 0); assert!(
seal_result.is_err(),
"seal_memory should have failed with invalid len"
);
assert!(
matches!(seal_result, Err(RSealError::InvalidInput(_))),
"Expected InvalidInput error"
);
if munmap(addr, page_size) != 0 {
eprintln!(
"Warning: munmap failed during cleanup: {}",
std::io::Error::last_os_error()
);
}
}
}
#[test]
fn test_seal_memory_zero_len() {
unsafe {
let page_size = libc::sysconf(libc::_SC_PAGE_SIZE) as usize;
let mem_size = page_size;
let addr = mmap(
ptr::null_mut(),
mem_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0,
);
if addr == libc::MAP_FAILED {
panic!("mmap failed: {}", std::io::Error::last_os_error());
}
let seal_result = seal_memory(addr, 0, 0); assert!(
seal_result.is_err(),
"seal_memory should have failed with zero len"
);
assert!(
matches!(seal_result, Err(RSealError::InvalidInput(_))),
"Expected InvalidInput error"
);
if munmap(addr, mem_size) != 0 {
eprintln!(
"Warning: munmap failed during cleanup: {}",
std::io::Error::last_os_error()
);
}
}
}
#[test]
fn test_seal_memory_null_addr() {
unsafe {
let page_size = libc::sysconf(libc::_SC_PAGE_SIZE) as usize;
let mem_size = page_size;
let seal_result = seal_memory(ptr::null_mut(), mem_size, 0); assert!(
seal_result.is_err(),
"seal_memory should have failed with null addr"
);
assert!(
matches!(seal_result, Err(RSealError::InvalidInput(_))),
"Expected InvalidInput error"
);
}
}
}