Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Module::deserialize_open_file #9571

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions crates/wasmtime/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use core::sync::atomic::{AtomicU64, Ordering};
use object::write::{Object, StandardSegment};
use object::SectionKind;
#[cfg(feature = "std")]
use std::path::Path;
use std::{fs::File, path::Path};
use wasmparser::WasmFeatures;
use wasmtime_environ::obj;
use wasmtime_environ::{FlagValue, ObjectKind, Tunables};
Expand Down Expand Up @@ -693,13 +693,12 @@ impl Engine {
#[cfg(feature = "std")]
pub(crate) fn load_code_file(
&self,
path: &Path,
file: File,
expected: ObjectKind,
) -> Result<Arc<crate::CodeMemory>> {
self.load_code(
crate::runtime::vm::MmapVec::from_file(path).with_context(|| {
format!("failed to create file mapping for: {}", path.display())
})?,
crate::runtime::vm::MmapVec::from_file(file)
.with_context(|| "Failed to create file mapping".to_string())?,
expected,
)
}
Expand Down
7 changes: 6 additions & 1 deletion crates/wasmtime/src/runtime/component/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use crate::component::types;
use crate::component::InstanceExportLookup;
use crate::prelude::*;
use crate::runtime::vm::component::ComponentRuntimeInfo;
#[cfg(feature = "std")]
use crate::runtime::vm::open_file_for_mmap;
use crate::runtime::vm::{
CompiledModuleId, VMArrayCallFunction, VMFuncRef, VMFunctionBody, VMWasmCallFunction,
};
Expand Down Expand Up @@ -228,7 +230,10 @@ impl Component {
/// [`Module::deserialize_file`]: crate::Module::deserialize_file
#[cfg(feature = "std")]
pub unsafe fn deserialize_file(engine: &Engine, path: impl AsRef<Path>) -> Result<Component> {
let code = engine.load_code_file(path.as_ref(), ObjectKind::Component)?;
let file = open_file_for_mmap(path.as_ref())?;
let code = engine
.load_code_file(file, ObjectKind::Component)
.with_context(|| format!("failed to load code for: {}", path.as_ref().display()))?;
Component::from_parts(engine, code, None)
}

Expand Down
30 changes: 27 additions & 3 deletions crates/wasmtime/src/runtime/module.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::prelude::*;
#[cfg(feature = "std")]
use crate::runtime::vm::open_file_for_mmap;
use crate::runtime::vm::{CompiledModuleId, MmapVec, ModuleMemoryImages, VMWasmCallFunction};
use crate::sync::OnceLock;
use crate::{
Expand All @@ -15,7 +17,7 @@ use core::fmt;
use core::ops::Range;
use core::ptr::NonNull;
#[cfg(feature = "std")]
use std::path::Path;
use std::{fs::File, path::Path};
use wasmparser::{Parser, ValidPayload, Validator};
use wasmtime_environ::{
CompiledModuleInfo, EntityIndex, HostPtr, ModuleTypes, ObjectKind, TypeTrace, VMOffsets,
Expand Down Expand Up @@ -343,7 +345,8 @@ impl Module {
/// state of the file.
#[cfg(all(feature = "std", any(feature = "cranelift", feature = "winch")))]
pub unsafe fn from_trusted_file(engine: &Engine, file: impl AsRef<Path>) -> Result<Module> {
let mmap = MmapVec::from_file(file.as_ref())?;
let open_file = open_file_for_mmap(file.as_ref())?;
let mmap = MmapVec::from_file(open_file)?;
if &mmap[0..4] == b"\x7fELF" {
let code = engine.load_code(mmap, ObjectKind::Module)?;
return Module::from_parts(engine, code, None);
Expand Down Expand Up @@ -426,7 +429,28 @@ impl Module {
/// state of the file.
#[cfg(feature = "std")]
pub unsafe fn deserialize_file(engine: &Engine, path: impl AsRef<Path>) -> Result<Module> {
let code = engine.load_code_file(path.as_ref(), ObjectKind::Module)?;
let file = open_file_for_mmap(path.as_ref())?;
Self::deserialize_open_file(engine, file)
.with_context(|| format!("failed deserialization for: {}", path.as_ref().display()))
}

/// Same as [`deserialize_file`], except that it takes an open `File`
/// instead of a path.
///
/// This method is provided because it can be used instead of
/// [`deserialize_file`] in situations where `wasmtime` is running with
/// limited file system permissions. In that case a process
/// with file system access can pass already opened files to `wasmtime`.
///
/// [`deserialize_file`]: Module::deserialize_file
///
/// # Unsafety
///
/// All of the reasons that [`deserialize_file`] is `unsafe` applies to this
/// function as well.
#[cfg(feature = "std")]
pub unsafe fn deserialize_open_file(engine: &Engine, file: File) -> Result<Module> {
let code = engine.load_code_file(file, ObjectKind::Module)?;
Module::from_parts(engine, code, None)
}

Expand Down
2 changes: 2 additions & 0 deletions crates/wasmtime/src/runtime/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ pub use crate::runtime::vm::mmap::Mmap;
pub use crate::runtime::vm::mmap_vec::MmapVec;
pub use crate::runtime::vm::mpk::MpkEnabled;
pub use crate::runtime::vm::store_box::*;
#[cfg(feature = "std")]
pub use crate::runtime::vm::sys::mmap::open_file_for_mmap;
pub use crate::runtime::vm::sys::unwind::UnwindRegistration;
pub use crate::runtime::vm::table::{Table, TableElement};
pub use crate::runtime::vm::traphandlers::*;
Expand Down
8 changes: 4 additions & 4 deletions crates/wasmtime/src/runtime/vm/mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::runtime::vm::sys::mmap;
use crate::{prelude::*, vm::usize_is_multiple_of_host_page_size};
use core::ops::Range;
#[cfg(feature = "std")]
use std::{fs::File, path::Path, sync::Arc};
use std::{fs::File, sync::Arc};

/// A simple struct consisting of a page-aligned pointer to page-aligned
/// and initially-zeroed memory and a length.
Expand Down Expand Up @@ -34,11 +34,11 @@ impl Mmap {
/// The memory mapping and the length of the file within the mapping are
/// returned.
#[cfg(feature = "std")]
pub fn from_file(path: &Path) -> Result<Self> {
let (sys, file) = mmap::Mmap::from_file(path)?;
pub fn from_file(file: Arc<File>) -> Result<Self> {
let sys = mmap::Mmap::from_file(&file)?;
Ok(Mmap {
sys,
file: Some(Arc::new(file)),
file: Some(file),
})
}

Expand Down
19 changes: 9 additions & 10 deletions crates/wasmtime/src/runtime/vm/mmap_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::runtime::vm::Mmap;
use alloc::sync::Arc;
use core::ops::{Deref, DerefMut, Range};
#[cfg(feature = "std")]
use std::{fs::File, path::Path};
use std::fs::File;

/// A type akin to `Vec<u8>`, but backed by `mmap` and able to be split.
///
Expand Down Expand Up @@ -54,17 +54,16 @@ impl MmapVec {
Ok(result)
}

/// Creates a new `MmapVec` which is the `path` specified mmap'd into
/// memory.
/// Creates a new `MmapVec` which is the given `File` mmap'd into memory.
///
/// This function will attempt to open the file located at `path` and will
/// then use that file to learn about its size and map the full contents
/// into memory. This will return an error if the file doesn't exist or if
/// it's too large to be fully mapped into memory.
/// This function will determine the file's size and map the full contents
/// into memory. This will return an error if the file is too large to be
/// fully mapped into memory.
#[cfg(feature = "std")]
pub fn from_file(path: &Path) -> Result<MmapVec> {
let mmap = Mmap::from_file(path)
.with_context(|| format!("failed to create mmap for file: {}", path.display()))?;
pub fn from_file(file: File) -> Result<MmapVec> {
let file = Arc::new(file);
let mmap = Mmap::from_file(Arc::clone(&file))
.with_context(move || format!("failed to create mmap for file {file:?}"))?;
let len = mmap.len();
Ok(MmapVec::new(mmap, len))
}
Expand Down
7 changes: 6 additions & 1 deletion crates/wasmtime/src/runtime/vm/sys/custom/mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ use core::ptr::{self, NonNull};
#[cfg(feature = "std")]
use std::{fs::File, path::Path};

#[cfg(feature = "std")]
pub fn open_file_for_mmap(_path: &Path) -> Result<File> {
anyhow::bail!("not supported on this platform");
}

#[derive(Debug)]
pub struct Mmap {
memory: SendSyncPtr<[u8]>,
Expand Down Expand Up @@ -38,7 +43,7 @@ impl Mmap {
}

#[cfg(feature = "std")]
pub fn from_file(_path: &Path) -> Result<(Self, File)> {
pub fn from_file(_file: &File) -> Result<Self> {
anyhow::bail!("not supported on this platform");
}

Expand Down
6 changes: 5 additions & 1 deletion crates/wasmtime/src/runtime/vm/sys/miri/mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ use std::ops::Range;
use std::path::Path;
use std::ptr::NonNull;

pub fn open_file_for_mmap(_path: &Path) -> Result<File> {
bail!("not supported on miri");
}

#[derive(Debug)]
pub struct Mmap {
memory: SendSyncPtr<[u8]>,
Expand Down Expand Up @@ -46,7 +50,7 @@ impl Mmap {
Ok(Mmap { memory })
}

pub fn from_file(_path: &Path) -> Result<(Self, File)> {
pub fn from_file(_file: &File) -> Result<Self> {
bail!("not supported on miri");
}

Expand Down
13 changes: 8 additions & 5 deletions crates/wasmtime/src/runtime/vm/sys/unix/mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ use std::ptr::{self, NonNull};
#[cfg(feature = "std")]
use std::{fs::File, path::Path};

/// Open a file so that it can be mmap'd for executing.
#[cfg(feature = "std")]
pub fn open_file_for_mmap(path: &Path) -> Result<File> {
File::open(path).err2anyhow().context("failed to open file")
}

#[derive(Debug)]
pub struct Mmap {
memory: SendSyncPtr<[u8]>,
Expand Down Expand Up @@ -78,10 +84,7 @@ impl Mmap {
}

#[cfg(feature = "std")]
pub fn from_file(path: &Path) -> Result<(Self, File)> {
let file = File::open(path)
.err2anyhow()
.context("failed to open file")?;
pub fn from_file(file: &File) -> Result<Self> {
let len = file
.metadata()
.err2anyhow()
Expand All @@ -103,7 +106,7 @@ impl Mmap {
let memory = std::ptr::slice_from_raw_parts_mut(ptr.cast(), len);
let memory = SendSyncPtr::new(NonNull::new(memory).unwrap());

Ok((Mmap { memory }, file))
Ok(Mmap { memory })
}

pub fn make_accessible(&mut self, start: usize, len: usize) -> Result<()> {
Expand Down
32 changes: 18 additions & 14 deletions crates/wasmtime/src/runtime/vm/sys/windows/mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ use windows_sys::Win32::Foundation::*;
use windows_sys::Win32::Storage::FileSystem::*;
use windows_sys::Win32::System::Memory::*;

/// Open a file so that it can be mmap'd for executing.
///
/// Open the file with read/execute access and only share for
/// read. This will enable us to perform the proper mmap below
/// while also disallowing other processes modifying the file
/// and having those modifications show up in our address space.
pub fn open_file_for_mmap(path: &Path) -> Result<File> {
adambratschikaye marked this conversation as resolved.
Show resolved Hide resolved
OpenOptions::new()
.read(true)
.access_mode(FILE_GENERIC_READ | FILE_GENERIC_EXECUTE)
.share_mode(FILE_SHARE_READ)
.open(path)
.err2anyhow()
.context("failed to open file")
}

#[derive(Debug)]
pub struct Mmap {
memory: SendSyncPtr<[u8]>,
Expand Down Expand Up @@ -58,20 +74,8 @@ impl Mmap {
})
}

pub fn from_file(path: &Path) -> Result<(Self, File)> {
pub fn from_file(file: &File) -> Result<Self> {
unsafe {
// Open the file with read/execute access and only share for
// read. This will enable us to perform the proper mmap below
// while also disallowing other processes modifying the file
// and having those modifications show up in our address space.
let file = OpenOptions::new()
.read(true)
.access_mode(FILE_GENERIC_READ | FILE_GENERIC_EXECUTE)
.share_mode(FILE_SHARE_READ)
.open(path)
.err2anyhow()
.context("failed to open file")?;

let len = file
.metadata()
.err2anyhow()
Expand Down Expand Up @@ -131,7 +135,7 @@ impl Mmap {
.context("failed change pages to `PAGE_READONLY`");
}

Ok((ret, file))
Ok(ret)
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use anyhow::{anyhow, bail, Context, Result};
use clap::Parser;
use std::net::TcpListener;
use std::{path::Path, time::Duration};
use std::{fs::File, path::Path, time::Duration};
use wasmtime::{Engine, Module, Precompiled, StoreLimits, StoreLimitsBuilder};
use wasmtime_cli_flags::{opt::WasmtimeOptionValue, CommonOptions};
use wasmtime_wasi::bindings::LinkOptions;
Expand Down Expand Up @@ -160,6 +160,7 @@ impl RunCommon {
Some("-") => "/dev/stdin".as_ref(),
_ => path,
};
let file = File::open(path)?;

// First attempt to load the module as an mmap. If this succeeds then
// detection can be done with the contents of the mmap and if a
Expand All @@ -179,7 +180,7 @@ impl RunCommon {
// which isn't ready to happen at this time). It's hoped though that
// opening a file twice isn't too bad in the grand scheme of things with
// respect to the CLI.
match wasmtime::_internal::MmapVec::from_file(path) {
match wasmtime::_internal::MmapVec::from_file(file) {
Ok(map) => self.load_module_contents(
engine,
path,
Expand Down
18 changes: 17 additions & 1 deletion tests/all/module_serialize.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::bail;
use std::fs;
use std::fs::{self, OpenOptions};
use wasmtime::*;

fn serialize(engine: &Engine, wat: &str) -> Result<Vec<u8>> {
Expand Down Expand Up @@ -103,6 +103,22 @@ fn test_deserialize_from_file() -> Result<()> {
let instance = Instance::new(&mut store, &module, &[])?;
let func = instance.get_typed_func::<(), i32>(&mut store, "run")?;
assert_eq!(func.call(&mut store, ())?, 42);

// Try an alreeady opened file as well.
adambratschikaye marked this conversation as resolved.
Show resolved Hide resolved
let mut open_options = OpenOptions::new();
open_options.read(true);
#[cfg(target_os = "windows")]
{
use std::os::windows::OpenOptionsExt;
open_options.access_mode(FILE_GENERIC_READ | FILE_GENERIC_EXECUTE);
}

let file = open_options.open(&path)?;
let module = unsafe { Module::deserialize_open_file(store.engine(), file)? };
let instance = Instance::new(&mut store, &module, &[])?;
let func = instance.get_typed_func::<(), i32>(&mut store, "run")?;
assert_eq!(func.call(&mut store, ())?, 42);

Ok(())
}
}
Expand Down
Loading