Review feedback.

This commit is contained in:
Chris Fallin
2022-02-01 15:49:44 -08:00
parent 0ff8f6ab20
commit 01e6bb81fb
7 changed files with 60 additions and 34 deletions

View File

@@ -16,9 +16,8 @@ fn main() {
// will work. // will work.
let os = env::var("CARGO_CFG_TARGET_OS").unwrap(); let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
let is_memfd = env::var("CARGO_FEATURE_MEMFD").is_ok(); let is_memfd = env::var("CARGO_FEATURE_MEMFD").is_ok();
let is_pooling = env::var("CARGO_FEATURE_POOLING_ALLOCATOR").is_ok();
let is_uffd = env::var("CARGO_FEATURE_UFFD").is_ok(); let is_uffd = env::var("CARGO_FEATURE_UFFD").is_ok();
if &os == "linux" && is_memfd && is_pooling && !is_uffd { if &os == "linux" && is_memfd && !is_uffd {
println!("cargo:rustc-cfg=memfd"); println!("cargo:rustc-cfg=memfd");
} }
} }

View File

@@ -771,14 +771,12 @@ impl Drop for MemoryPool {
// drop) for all MemFdSlots, and then drop them here. This is // drop) for all MemFdSlots, and then drop them here. This is
// valid because the one `Mmap` that covers the whole region // valid because the one `Mmap` that covers the whole region
// can just do its one munmap. // can just do its one munmap.
for memfd in std::mem::take(&mut self.memfd_slots) { for mut memfd in std::mem::take(&mut self.memfd_slots) {
if let Some(memfd_slot) = memfd.lock().unwrap().as_mut() { if let Some(memfd_slot) = memfd.get_mut().unwrap() {
unsafe {
memfd_slot.no_clear_on_drop(); memfd_slot.no_clear_on_drop();
} }
} }
} }
}
} }
/// Represents a pool of WebAssembly tables. /// Represents a pool of WebAssembly tables.

View File

@@ -19,7 +19,6 @@
clippy::use_self clippy::use_self
) )
)] )]
#![cfg_attr(memfd, allow(dead_code))]
use std::sync::atomic::AtomicU64; use std::sync::atomic::AtomicU64;

View File

@@ -42,11 +42,15 @@ pub struct MemoryMemFd {
/// Length of image. Note that initial memory size may be larger; /// Length of image. Note that initial memory size may be larger;
/// leading and trailing zeroes are truncated (handled by /// leading and trailing zeroes are truncated (handled by
/// anonymous backing memfd). /// anonymous backing memfd).
///
/// Must be a multiple of the system page size.
pub len: usize, pub len: usize,
/// Image starts this many bytes into heap space. Note that the /// Image starts this many bytes into heap space. Note that the
/// memfd's offsets are always equal to the heap offsets, so we /// memfd's offsets are always equal to the heap offsets, so we
/// map at an offset into the fd as well. (This simplifies /// map at an offset into the fd as well. (This simplifies
/// construction.) /// construction.)
///
/// Must be a multiple of the system page size.
pub offset: usize, pub offset: usize,
} }
@@ -231,6 +235,9 @@ impl ModuleMemFds {
memfd.add_seal(memfd::FileSeal::SealWrite)?; memfd.add_seal(memfd::FileSeal::SealWrite)?;
memfd.add_seal(memfd::FileSeal::SealSeal)?; memfd.add_seal(memfd::FileSeal::SealSeal)?;
assert_eq!(offset % page_size, 0);
assert_eq!(len % page_size, 0);
memories.push(Some(Arc::new(MemoryMemFd { memories.push(Some(Arc::new(MemoryMemFd {
fd: memfd, fd: memfd,
offset: usize::try_from(offset).unwrap(), offset: usize::try_from(offset).unwrap(),
@@ -294,6 +301,8 @@ pub struct MemFdSlot {
} }
impl MemFdSlot { impl MemFdSlot {
/// Create a new MemFdSlot. Assumes that there is an anonymous
/// mmap backing in the given range to start.
pub(crate) fn create(base_addr: *mut c_void, static_size: usize) -> Self { pub(crate) fn create(base_addr: *mut c_void, static_size: usize) -> Self {
let base = base_addr as usize; let base = base_addr as usize;
MemFdSlot { MemFdSlot {
@@ -311,7 +320,7 @@ impl MemFdSlot {
/// address space when dropped. This should be used only when the /// address space when dropped. This should be used only when the
/// caller will clear or reuse the address space in some other /// caller will clear or reuse the address space in some other
/// way. /// way.
pub(crate) unsafe fn no_clear_on_drop(&mut self) { pub(crate) fn no_clear_on_drop(&mut self) {
self.clear_on_drop = false; self.clear_on_drop = false;
} }
@@ -384,8 +393,22 @@ impl MemFdSlot {
// zeroes). Anonymous zero pages are fast: the kernel // zeroes). Anonymous zero pages are fast: the kernel
// pre-zeroes them, and even if it runs out of those, a memset // pre-zeroes them, and even if it runs out of those, a memset
// is half as expensive as a memcpy (only writes, no reads). // is half as expensive as a memcpy (only writes, no reads).
self.map_anon_memory(rustix::io::ProtFlags::READ | rustix::io::ProtFlags::WRITE) //
// We map these inaccessible at first then mprotect() the
// whole of the initial heap size to R+W below.
//
// Special case: we can skip if the last instantiation had no
// image. This means that the whole slot is filled with an
// anonymous mmap backing (and it will have already been
// cleared by the madvise). This also lets us skip an mmap the
// first time a MemFdSlot is used, because we require the
// caller to give us a fixed address in an
// already-mmaped-with-anon-memory region. This is important
// for the on-demand allocator.
if self.image.is_some() {
self.map_anon_memory(rustix::io::ProtFlags::empty())
.map_err(|e| InstantiationError::Resource(e.into()))?; .map_err(|e| InstantiationError::Resource(e.into()))?;
}
// The initial memory image, if given. If not, we just get a // The initial memory image, if given. If not, we just get a
// memory filled with zeroes. // memory filled with zeroes.
@@ -410,10 +433,14 @@ impl MemFdSlot {
} }
} }
// mprotect above `initial_size_bytes`. // mprotect the initial `initial_size_bytes` to be accessible.
self.initial_size = initial_size_bytes; self.initial_size = initial_size_bytes;
self.cur_size = initial_size_bytes; self.cur_size = initial_size_bytes;
self.protect_past_initial_size() self.set_protection(
0,
initial_size_bytes,
rustix::io::MprotectFlags::READ | rustix::io::MprotectFlags::WRITE,
)
.map_err(|e| InstantiationError::Resource(e.into()))?; .map_err(|e| InstantiationError::Resource(e.into()))?;
self.dirty = true; self.dirty = true;
@@ -433,22 +460,27 @@ impl MemFdSlot {
)?; )?;
} }
// mprotect the region beyond the initial heap size back to PROT_NONE. // mprotect the initial heap region beyond the initial heap size back to PROT_NONE.
self.protect_past_initial_size()?; self.set_protection(
self.initial_size,
self.static_size - self.initial_size,
rustix::io::MprotectFlags::empty(),
)?;
self.dirty = false; self.dirty = false;
Ok(()) Ok(())
} }
fn protect_past_initial_size(&self) -> Result<()> { fn set_protection(
let mprotect_start = self.base + self.initial_size; &self,
let mprotect_len = self.static_size - self.initial_size; start: usize,
if mprotect_len > 0 { len: usize,
flags: rustix::io::MprotectFlags,
) -> Result<()> {
assert!(start.checked_add(len).unwrap() <= self.static_size);
let mprotect_start = self.base.checked_add(start).unwrap();
if len > 0 {
unsafe { unsafe {
rustix::io::mprotect( rustix::io::mprotect(mprotect_start as *mut _, len, flags)?;
mprotect_start as *mut _,
mprotect_len,
rustix::io::MprotectFlags::empty(),
)?;
} }
} }

View File

@@ -54,7 +54,7 @@ impl MemFdSlot {
panic!("instantiate() on invalid MemFdSlot"); panic!("instantiate() on invalid MemFdSlot");
} }
pub(crate) unsafe fn no_clear_on_drop(&mut self) {} pub(crate) fn no_clear_on_drop(&mut self) {}
pub(crate) fn clear_and_remain_ready(&mut self) -> Result<()> { pub(crate) fn clear_and_remain_ready(&mut self) -> Result<()> {
Ok(()) Ok(())

View File

@@ -162,13 +162,11 @@ impl MmapMemory {
let len = request_bytes - pre_guard_bytes; let len = request_bytes - pre_guard_bytes;
let mut memfd_slot = MemFdSlot::create(base as *mut _, len); let mut memfd_slot = MemFdSlot::create(base as *mut _, len);
memfd_slot.instantiate(minimum, Some(image))?; memfd_slot.instantiate(minimum, Some(image))?;
unsafe { // On drop, we will unmap our mmap'd range that this
// On drop, we will unmap our mmap'd range that // memfd_slot was mapped on top of, so there is no
// this memfd_slot was mapped on top of, so there // need for the memfd_slot to wipe it with an
// is no need for the memfd_slot to wipe it with // anonymous mapping first.
// an anonymous mapping first.
memfd_slot.no_clear_on_drop(); memfd_slot.no_clear_on_drop();
}
Some(memfd_slot) Some(memfd_slot)
} }
None => None, None => None,

View File

@@ -90,4 +90,4 @@ all-arch = ["wasmtime-cranelift/all-arch"]
# need portable signal handling. # need portable signal handling.
posix-signals-on-macos = ["wasmtime-runtime/posix-signals-on-macos"] posix-signals-on-macos = ["wasmtime-runtime/posix-signals-on-macos"]
memfd = ["wasmtime-runtime/memfd", "pooling-allocator"] memfd = ["wasmtime-runtime/memfd"]