Use rsix to make system calls in Wasmtime. (#3355)
* Use rsix to make system calls in Wasmtime. `rsix` is a system call wrapper crate that we use in `wasi-common`, which can provide the following advantages in the rest of Wasmtime: - It eliminates some `unsafe` blocks in Wasmtime's code. There's still an `unsafe` block in the library, but this way, the `unsafe` is factored out and clearly scoped. - And, it makes error handling more consistent, factoring out code for checking return values and `io::Error::last_os_error()`, and code that does `errno::set_errno(0)`. This doesn't cover *all* system calls; `rsix` doesn't implement signal-handling APIs, and this doesn't cover calls made through `std` or crates like `userfaultfd`, `rand`, and `region`.
This commit is contained in:
@@ -29,6 +29,9 @@ anyhow = "1.0.38"
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
mach = "0.3.2"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
rsix = "0.23.2"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = { version = "0.3.7", features = ["winbase", "memoryapi", "errhandlingapi", "handleapi"] }
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
fn decommit(addr: *mut u8, len: usize, protect: bool) -> Result<()> {
|
||||
if len == 0 {
|
||||
@@ -12,12 +12,8 @@ fn decommit(addr: *mut u8, len: usize, protect: bool) -> Result<()> {
|
||||
}
|
||||
|
||||
// On Linux, this is enough to cause the kernel to initialize the pages to 0 on next access
|
||||
if libc::madvise(addr as _, len, libc::MADV_DONTNEED) != 0 {
|
||||
bail!(
|
||||
"madvise failed to decommit: {}",
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
}
|
||||
rsix::io::madvise(addr as _, len, rsix::io::Advice::LinuxDontNeed)
|
||||
.context("madvise failed to decommit: {}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
use super::{InstancePool, MemoryPool};
|
||||
use crate::instance::Instance;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use rsix::io::{madvise, Advice};
|
||||
use std::thread;
|
||||
use userfaultfd::{Event, FeatureFlags, IoctlFlags, Uffd, UffdBuilder};
|
||||
use wasmtime_environ::{DefinedMemoryIndex, EntityRef, MemoryInitialization};
|
||||
@@ -50,12 +51,7 @@ fn decommit(addr: *mut u8, len: usize) -> Result<()> {
|
||||
// and the user fault handler will receive the event.
|
||||
// If the pages are not monitored by uffd, the kernel will zero the page on next access,
|
||||
// as if it were mmap'd for the first time.
|
||||
if libc::madvise(addr as _, len, libc::MADV_DONTNEED) != 0 {
|
||||
bail!(
|
||||
"madvise failed to decommit: {}",
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
}
|
||||
madvise(addr as _, len, Advice::LinuxDontNeed).context("madvise failed to decommit")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
fn decommit(addr: *mut u8, len: usize, protect: bool) -> Result<()> {
|
||||
if len == 0 {
|
||||
@@ -9,25 +9,18 @@ fn decommit(addr: *mut u8, len: usize, protect: bool) -> Result<()> {
|
||||
// mapping for the pages in the given range.
|
||||
// The new mapping will be to the CoW zero page, so this effectively
|
||||
// zeroes the pages.
|
||||
if unsafe {
|
||||
libc::mmap(
|
||||
unsafe {
|
||||
rsix::io::mmap_anonymous(
|
||||
addr as _,
|
||||
len,
|
||||
if protect {
|
||||
libc::PROT_NONE
|
||||
rsix::io::ProtFlags::NONE
|
||||
} else {
|
||||
libc::PROT_READ | libc::PROT_WRITE
|
||||
rsix::io::ProtFlags::READ | rsix::io::ProtFlags::WRITE
|
||||
},
|
||||
libc::MAP_PRIVATE | libc::MAP_ANON | libc::MAP_FIXED,
|
||||
-1,
|
||||
0,
|
||||
) as *mut u8
|
||||
} != addr
|
||||
{
|
||||
bail!(
|
||||
"mmap failed to remap pages: {}",
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
rsix::io::MapFlags::PRIVATE | rsix::io::MapFlags::FIXED,
|
||||
)
|
||||
.context("mmap failed to remap pages: {}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
//! of memory.
|
||||
|
||||
use anyhow::anyhow;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result};
|
||||
use more_asserts::assert_le;
|
||||
use std::convert::TryFrom;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::ops::Range;
|
||||
use std::path::Path;
|
||||
use std::ptr;
|
||||
@@ -57,8 +56,6 @@ impl Mmap {
|
||||
pub fn from_file(path: &Path) -> Result<Self> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::prelude::*;
|
||||
|
||||
let file = File::open(path).context("failed to open file")?;
|
||||
let len = file
|
||||
.metadata()
|
||||
@@ -66,19 +63,16 @@ impl Mmap {
|
||||
.len();
|
||||
let len = usize::try_from(len).map_err(|_| anyhow!("file too large to map"))?;
|
||||
let ptr = unsafe {
|
||||
libc::mmap(
|
||||
rsix::io::mmap(
|
||||
ptr::null_mut(),
|
||||
len,
|
||||
libc::PROT_READ,
|
||||
libc::MAP_PRIVATE,
|
||||
file.as_raw_fd(),
|
||||
rsix::io::ProtFlags::READ,
|
||||
rsix::io::MapFlags::PRIVATE,
|
||||
&file,
|
||||
0,
|
||||
)
|
||||
.context(format!("mmap failed to allocate {:#x} bytes", len))?
|
||||
};
|
||||
if ptr as isize == -1_isize {
|
||||
return Err(io::Error::last_os_error())
|
||||
.context(format!("mmap failed to allocate {:#x} bytes", len));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
ptr: ptr as usize,
|
||||
@@ -90,6 +84,7 @@ impl Mmap {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::fs::OpenOptions;
|
||||
use std::io;
|
||||
use std::os::windows::prelude::*;
|
||||
use winapi::um::handleapi::*;
|
||||
use winapi::um::memoryapi::*;
|
||||
@@ -175,22 +170,14 @@ impl Mmap {
|
||||
Ok(if accessible_size == mapping_size {
|
||||
// Allocate a single read-write region at once.
|
||||
let ptr = unsafe {
|
||||
libc::mmap(
|
||||
rsix::io::mmap_anonymous(
|
||||
ptr::null_mut(),
|
||||
mapping_size,
|
||||
libc::PROT_READ | libc::PROT_WRITE,
|
||||
libc::MAP_PRIVATE | libc::MAP_ANON,
|
||||
-1,
|
||||
0,
|
||||
rsix::io::ProtFlags::READ | rsix::io::ProtFlags::WRITE,
|
||||
rsix::io::MapFlags::PRIVATE,
|
||||
)
|
||||
.context(format!("mmap failed to allocate {:#x} bytes", mapping_size))?
|
||||
};
|
||||
if ptr as isize == -1_isize {
|
||||
bail!(
|
||||
"mmap failed to allocate {:#x} bytes: {}",
|
||||
mapping_size,
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
}
|
||||
|
||||
Self {
|
||||
ptr: ptr as usize,
|
||||
@@ -200,22 +187,14 @@ impl Mmap {
|
||||
} else {
|
||||
// Reserve the mapping size.
|
||||
let ptr = unsafe {
|
||||
libc::mmap(
|
||||
rsix::io::mmap_anonymous(
|
||||
ptr::null_mut(),
|
||||
mapping_size,
|
||||
libc::PROT_NONE,
|
||||
libc::MAP_PRIVATE | libc::MAP_ANON,
|
||||
-1,
|
||||
0,
|
||||
rsix::io::ProtFlags::NONE,
|
||||
rsix::io::MapFlags::PRIVATE,
|
||||
)
|
||||
.context(format!("mmap failed to allocate {:#x} bytes", mapping_size))?
|
||||
};
|
||||
if ptr as isize == -1_isize {
|
||||
bail!(
|
||||
"mmap failed to allocate {:#x} bytes: {}",
|
||||
mapping_size,
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
}
|
||||
|
||||
let mut result = Self {
|
||||
ptr: ptr as usize,
|
||||
@@ -237,6 +216,8 @@ impl Mmap {
|
||||
/// must be native page-size multiples.
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn accessible_reserved(accessible_size: usize, mapping_size: usize) -> Result<Self> {
|
||||
use anyhow::bail;
|
||||
use std::io;
|
||||
use winapi::um::memoryapi::VirtualAlloc;
|
||||
use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_NOACCESS, PAGE_READWRITE};
|
||||
|
||||
@@ -316,6 +297,8 @@ impl Mmap {
|
||||
/// `self`'s reserved memory.
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn make_accessible(&mut self, start: usize, len: usize) -> Result<()> {
|
||||
use anyhow::bail;
|
||||
use std::io;
|
||||
use winapi::ctypes::c_void;
|
||||
use winapi::um::memoryapi::VirtualAlloc;
|
||||
use winapi::um::winnt::{MEM_COMMIT, PAGE_READWRITE};
|
||||
@@ -398,6 +381,7 @@ impl Mmap {
|
||||
// we don't want our modifications to go back to the original file.
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::io;
|
||||
use winapi::um::memoryapi::*;
|
||||
use winapi::um::winnt::*;
|
||||
|
||||
@@ -440,8 +424,8 @@ impl Drop for Mmap {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn drop(&mut self) {
|
||||
if self.len != 0 {
|
||||
let r = unsafe { libc::munmap(self.ptr as *mut libc::c_void, self.len) };
|
||||
assert_eq!(r, 0, "munmap failed: {}", io::Error::last_os_error());
|
||||
unsafe { rsix::io::munmap(self.ptr as *mut std::ffi::c_void, self.len) }
|
||||
.expect("munmap failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::traphandlers::{tls, wasmtime_longjmp, Trap};
|
||||
use std::cell::RefCell;
|
||||
use std::convert::TryInto;
|
||||
use std::io;
|
||||
use std::mem::{self, MaybeUninit};
|
||||
use std::ptr::{self, null_mut};
|
||||
@@ -291,36 +290,27 @@ pub fn lazy_per_thread_init() -> Result<(), Box<Trap>> {
|
||||
|
||||
// ... but failing that we need to allocate our own, so do all that
|
||||
// here.
|
||||
let page_size: usize = libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap();
|
||||
let page_size: usize = region::page::size();
|
||||
let guard_size = page_size;
|
||||
let alloc_size = guard_size + MIN_STACK_SIZE;
|
||||
|
||||
let ptr = libc::mmap(
|
||||
let ptr = rsix::io::mmap_anonymous(
|
||||
null_mut(),
|
||||
alloc_size,
|
||||
libc::PROT_NONE,
|
||||
libc::MAP_PRIVATE | libc::MAP_ANON,
|
||||
-1,
|
||||
0,
|
||||
);
|
||||
if ptr == libc::MAP_FAILED {
|
||||
return Err(Box::new(Trap::oom()));
|
||||
}
|
||||
rsix::io::ProtFlags::NONE,
|
||||
rsix::io::MapFlags::PRIVATE,
|
||||
)
|
||||
.map_err(|_| Box::new(Trap::oom()))?;
|
||||
|
||||
// Prepare the stack with readable/writable memory and then register it
|
||||
// with `sigaltstack`.
|
||||
let stack_ptr = (ptr as usize + guard_size) as *mut libc::c_void;
|
||||
let r = libc::mprotect(
|
||||
let stack_ptr = (ptr as usize + guard_size) as *mut std::ffi::c_void;
|
||||
rsix::io::mprotect(
|
||||
stack_ptr,
|
||||
MIN_STACK_SIZE,
|
||||
libc::PROT_READ | libc::PROT_WRITE,
|
||||
);
|
||||
assert_eq!(
|
||||
r,
|
||||
0,
|
||||
"mprotect to configure memory for sigaltstack failed: {}",
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
rsix::io::MprotectFlags::READ | rsix::io::MprotectFlags::WRITE,
|
||||
)
|
||||
.expect("mprotect to configure memory for sigaltstack failed");
|
||||
let new_stack = libc::stack_t {
|
||||
ss_sp: stack_ptr,
|
||||
ss_flags: 0,
|
||||
@@ -344,8 +334,8 @@ pub fn lazy_per_thread_init() -> Result<(), Box<Trap>> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// Deallocate the stack memory.
|
||||
let r = libc::munmap(self.mmap_ptr, self.mmap_size);
|
||||
debug_assert_eq!(r, 0, "munmap failed during thread shutdown");
|
||||
let r = rsix::io::munmap(self.mmap_ptr, self.mmap_size);
|
||||
debug_assert!(r.is_ok(), "munmap failed during thread shutdown");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user