Skip to content

Commit

Permalink
Merge #957
Browse files Browse the repository at this point in the history
957: Add support for new WASI snapshot, backwards compat too r=MarkMcCaskey a=MarkMcCaskey


# Review

- [x] Add a short description of the the change to the CHANGELOG.md file


Co-authored-by: Mark McCaskey <mark@wasmer.io>
Co-authored-by: Mark McCaskey <5770194+markmccaskey@users.noreply.github.com>
  • Loading branch information
3 people authored Nov 13, 2019
2 parents d64d070 + 064ffd3 commit 55faeff
Show file tree
Hide file tree
Showing 9 changed files with 406 additions and 41 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## **[Unreleased]**

- [#957](https://github.com/wasmerio/wasmer/pull/957) Change the meaning of `wasmer_wasi::is_wasi_module` to detect any type of WASI module, add support for new wasi snapshot_preview1
- [#925](https://github.com/wasmerio/wasmer/pull/925) Host functions can be closures with a captured environment.
- [#917](https://github.com/wasmerio/wasmer/pull/917) Host functions (aka imported functions) may not have `&mut vm::Ctx` as first argument, i.e. the presence of the `&mut vm::Ctx` argument is optional.
- [#915](https://github.com/wasmerio/wasmer/pull/915) All backends share the same definition of `Trampoline` (defined in `wasmer-runtime-core`).
Expand Down
5 changes: 5 additions & 0 deletions lib/wasi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,8 @@ wasmer-runtime-core = { path = "../runtime-core", version = "0.10.1" }

[target.'cfg(windows)'.dependencies]
winapi = "0.3"

[features]
# Enable support for the older snapshot0 WASI modules
snapshot0 = []
default = ["snapshot0"]
92 changes: 88 additions & 4 deletions lib/wasi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

//! Wasmer's WASI implementation
//!
//! Use `generate_import_object` to create an `ImportObject`. This `ImportObject`
//! Use `generate_import_object` to create an [`ImportObject`]. This [`ImportObject`]
//! can be combined with a module to create an `Instance` which can execute WASI
//! Wasm functions.
//!
Expand All @@ -37,7 +37,7 @@ use self::syscalls::*;
use std::ffi::c_void;
use std::path::PathBuf;

pub use self::utils::is_wasi_module;
pub use self::utils::{get_wasi_version, is_wasi_module, WasiVersion};

use wasmer_runtime_core::{func, import::ImportObject, imports};

Expand All @@ -47,7 +47,8 @@ pub struct ExitCode {
pub code: syscalls::types::__wasi_exitcode_t,
}

/// Creates a Wasi [`ImportObject`] with [`WasiState`].
/// Creates a Wasi [`ImportObject`] with [`WasiState`] with the latest snapshot
/// of WASI.
pub fn generate_import_object(
args: Vec<Vec<u8>>,
envs: Vec<Vec<u8>>,
Expand Down Expand Up @@ -79,7 +80,7 @@ pub fn generate_import_object(
imports! {
// This generates the wasi state.
state_gen,
"wasi_unstable" => {
"wasi_snapshot_preview1" => {
"args_get" => func!(args_get),
"args_sizes_get" => func!(args_sizes_get),
"clock_res_get" => func!(clock_res_get),
Expand Down Expand Up @@ -128,3 +129,86 @@ pub fn generate_import_object(
},
}
}

#[cfg(feature = "snapshot0")]
/// Creates a legacy Wasi [`ImportObject`] with [`WasiState`].
pub fn generate_import_object_snapshot0(
args: Vec<Vec<u8>>,
envs: Vec<Vec<u8>>,
preopened_files: Vec<PathBuf>,
mapped_dirs: Vec<(String, PathBuf)>,
) -> ImportObject {
let state_gen = move || {
// TODO: look into removing all these unnecessary clones
fn state_destructor(data: *mut c_void) {
unsafe {
drop(Box::from_raw(data as *mut WasiState));
}
}
let preopened_files = preopened_files.clone();
let mapped_dirs = mapped_dirs.clone();
//let wasi_builder = create_wasi_instance();

let state = Box::new(WasiState {
fs: WasiFs::new(&preopened_files, &mapped_dirs).expect("Could not create WASI FS"),
args: args.clone(),
envs: envs.clone(),
});

(
Box::into_raw(state) as *mut c_void,
state_destructor as fn(*mut c_void),
)
};
imports! {
// This generates the wasi state.
state_gen,
"wasi_unstable" => {
"args_get" => func!(args_get),
"args_sizes_get" => func!(args_sizes_get),
"clock_res_get" => func!(clock_res_get),
"clock_time_get" => func!(clock_time_get),
"environ_get" => func!(environ_get),
"environ_sizes_get" => func!(environ_sizes_get),
"fd_advise" => func!(fd_advise),
"fd_allocate" => func!(fd_allocate),
"fd_close" => func!(fd_close),
"fd_datasync" => func!(fd_datasync),
"fd_fdstat_get" => func!(fd_fdstat_get),
"fd_fdstat_set_flags" => func!(fd_fdstat_set_flags),
"fd_fdstat_set_rights" => func!(fd_fdstat_set_rights),
"fd_filestat_get" => func!(legacy::snapshot0::fd_filestat_get),
"fd_filestat_set_size" => func!(fd_filestat_set_size),
"fd_filestat_set_times" => func!(fd_filestat_set_times),
"fd_pread" => func!(fd_pread),
"fd_prestat_get" => func!(fd_prestat_get),
"fd_prestat_dir_name" => func!(fd_prestat_dir_name),
"fd_pwrite" => func!(fd_pwrite),
"fd_read" => func!(fd_read),
"fd_readdir" => func!(fd_readdir),
"fd_renumber" => func!(fd_renumber),
"fd_seek" => func!(legacy::snapshot0::fd_seek),
"fd_sync" => func!(fd_sync),
"fd_tell" => func!(fd_tell),
"fd_write" => func!(fd_write),
"path_create_directory" => func!(path_create_directory),
"path_filestat_get" => func!(legacy::snapshot0::path_filestat_get),
"path_filestat_set_times" => func!(path_filestat_set_times),
"path_link" => func!(path_link),
"path_open" => func!(path_open),
"path_readlink" => func!(path_readlink),
"path_remove_directory" => func!(path_remove_directory),
"path_rename" => func!(path_rename),
"path_symlink" => func!(path_symlink),
"path_unlink_file" => func!(path_unlink_file),
"poll_oneoff" => func!(legacy::snapshot0::poll_oneoff),
"proc_exit" => func!(proc_exit),
"proc_raise" => func!(proc_raise),
"random_get" => func!(random_get),
"sched_yield" => func!(sched_yield),
"sock_recv" => func!(sock_recv),
"sock_send" => func!(sock_send),
"sock_shutdown" => func!(sock_shutdown),
},
}
}
6 changes: 6 additions & 0 deletions lib/wasi/src/syscalls/legacy/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//! These modules provide wrappers and implementations for older version of WASI.
//!
//! If you are relying on legacy WASI, please upgrade for the best experience.
#[cfg(feature = "snapshot0")]
pub mod snapshot0;
178 changes: 178 additions & 0 deletions lib/wasi/src/syscalls/legacy/snapshot0.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
use crate::ptr::{Array, WasmPtr};
use crate::syscalls;
use crate::syscalls::types::{self, snapshot0};
use wasmer_runtime_core::vm::Ctx;

/// Wrapper around `syscalls::fd_filestat_get` with extra logic to handle the size
/// difference of `wasi_filestat_t`
///
/// WARNING: this function involves saving, clobbering, and restoring unrelated
/// Wasm memory. If the memory clobbered by the current syscall is also used by
/// that syscall, then it may break.
pub fn fd_filestat_get(
ctx: &mut Ctx,
fd: types::__wasi_fd_t,
buf: WasmPtr<snapshot0::__wasi_filestat_t>,
) -> types::__wasi_errno_t {
let memory = ctx.memory(0);

// transmute the WasmPtr<T1> into a WasmPtr<T2> where T2 > T1, this will read extra memory.
// The edge case of this causing an OOB is not handled, if the new field is OOB, then the entire
// memory access will fail.
let new_buf: WasmPtr<types::__wasi_filestat_t> = unsafe { std::mem::transmute(buf) };

// Copy the data including the extra data
let new_filestat_setup: types::__wasi_filestat_t =
wasi_try!(new_buf.deref(memory)).get().clone();

// Set up complete, make the call with the pointer that will write to the
// struct and some unrelated memory after the struct.
let result = syscalls::fd_filestat_get(ctx, fd, new_buf);

// reborrow memory
let memory = ctx.memory(0);

// get the values written to memory
let new_filestat = wasi_try!(new_buf.deref(memory)).get();
// translate the new struct into the old struct in host memory
let old_stat = snapshot0::__wasi_filestat_t {
st_dev: new_filestat.st_dev,
st_ino: new_filestat.st_ino,
st_filetype: new_filestat.st_filetype,
st_nlink: new_filestat.st_nlink as u32,
st_size: new_filestat.st_size,
st_atim: new_filestat.st_atim,
st_mtim: new_filestat.st_mtim,
st_ctim: new_filestat.st_ctim,
};

// write back the original values at the pointer's memory locations
// (including the memory unrelated to the pointer)
wasi_try!(new_buf.deref(memory)).set(new_filestat_setup);

// Now that this memory is back as it was, write the translated filestat
// into memory leaving it as it should be
wasi_try!(buf.deref(memory)).set(old_stat);

result
}

/// Wrapper around `syscalls::path_filestat_get` with extra logic to handle the size
/// difference of `wasi_filestat_t`
pub fn path_filestat_get(
ctx: &mut Ctx,
fd: types::__wasi_fd_t,
flags: types::__wasi_lookupflags_t,
path: WasmPtr<u8, Array>,
path_len: u32,
buf: WasmPtr<snapshot0::__wasi_filestat_t>,
) -> types::__wasi_errno_t {
// see `fd_filestat_get` in this file for an explanation of this strange behavior
let memory = ctx.memory(0);

let new_buf: WasmPtr<types::__wasi_filestat_t> = unsafe { std::mem::transmute(buf) };
let new_filestat_setup: types::__wasi_filestat_t =
wasi_try!(new_buf.deref(memory)).get().clone();

let result = syscalls::path_filestat_get(ctx, fd, flags, path, path_len, new_buf);

let memory = ctx.memory(0);
let new_filestat = wasi_try!(new_buf.deref(memory)).get();
let old_stat = snapshot0::__wasi_filestat_t {
st_dev: new_filestat.st_dev,
st_ino: new_filestat.st_ino,
st_filetype: new_filestat.st_filetype,
st_nlink: new_filestat.st_nlink as u32,
st_size: new_filestat.st_size,
st_atim: new_filestat.st_atim,
st_mtim: new_filestat.st_mtim,
st_ctim: new_filestat.st_ctim,
};

wasi_try!(new_buf.deref(memory)).set(new_filestat_setup);
wasi_try!(buf.deref(memory)).set(old_stat);

result
}

/// Wrapper around `syscalls::fd_seek` with extra logic to remap the values
/// of `__wasi_whence_t`
pub fn fd_seek(
ctx: &mut Ctx,
fd: types::__wasi_fd_t,
offset: types::__wasi_filedelta_t,
whence: snapshot0::__wasi_whence_t,
newoffset: WasmPtr<types::__wasi_filesize_t>,
) -> types::__wasi_errno_t {
let new_whence = match whence {
snapshot0::__WASI_WHENCE_CUR => types::__WASI_WHENCE_CUR,
snapshot0::__WASI_WHENCE_END => types::__WASI_WHENCE_END,
snapshot0::__WASI_WHENCE_SET => types::__WASI_WHENCE_SET,
// if it's invalid, let the new fd_seek handle it
_ => whence,
};
syscalls::fd_seek(ctx, fd, offset, new_whence, newoffset)
}

/// Wrapper around `syscalls::poll_oneoff` with extra logic to add the removed
/// userdata field back
pub fn poll_oneoff(
ctx: &mut Ctx,
in_: WasmPtr<snapshot0::__wasi_subscription_t, Array>,
out_: WasmPtr<types::__wasi_event_t, Array>,
nsubscriptions: u32,
nevents: WasmPtr<u32>,
) -> types::__wasi_errno_t {
// in this case the new type is smaller than the old type, so it all fits into memory,
// we just need to readjust and copy it

// we start by adjusting `in_` into a format that the new code can understand
let memory = ctx.memory(0);
let mut in_origs: Vec<snapshot0::__wasi_subscription_t> = vec![];
for in_sub in wasi_try!(in_.deref(memory, 0, nsubscriptions)) {
in_origs.push(in_sub.get().clone());
}

// get a pointer to the smaller new type
let in_new_type_ptr: WasmPtr<types::__wasi_subscription_t, Array> =
unsafe { std::mem::transmute(in_) };

for (in_sub_new, orig) in wasi_try!(in_new_type_ptr.deref(memory, 0, nsubscriptions))
.iter()
.zip(in_origs.iter())
{
in_sub_new.set(types::__wasi_subscription_t {
userdata: orig.userdata,
type_: orig.type_,
u: if orig.type_ == types::__WASI_EVENTTYPE_CLOCK {
types::__wasi_subscription_u {
clock: types::__wasi_subscription_clock_t {
clock_id: unsafe { orig.u.clock.clock_id },
timeout: unsafe { orig.u.clock.timeout },
precision: unsafe { orig.u.clock.precision },
flags: unsafe { orig.u.clock.flags },
},
}
} else {
types::__wasi_subscription_u {
fd_readwrite: unsafe { orig.u.fd_readwrite },
}
},
});
}

// make the call
let result = syscalls::poll_oneoff(ctx, in_new_type_ptr, out_, nsubscriptions, nevents);

// replace the old values of in, in case the calling code reuses the memory
let memory = ctx.memory(0);

for (in_sub, orig) in wasi_try!(in_.deref(memory, 0, nsubscriptions))
.iter()
.zip(in_origs.into_iter())
{
in_sub.set(orig);
}

result
}
3 changes: 3 additions & 0 deletions lib/wasi/src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ pub mod unix;
#[cfg(any(target_os = "windows"))]
pub mod windows;

#[cfg(any(feature = "snapshot0"))]
pub mod legacy;

use self::types::*;
use crate::{
ptr::{Array, WasmPtr},
Expand Down
Loading

0 comments on commit 55faeff

Please sign in to comment.