From a82ea3c1e7dd9f6f0e216ed0b12a8f33c73759dc Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 1 Mar 2023 16:51:53 +1100 Subject: [PATCH 01/12] Added some fixes for the browser version to work again --- lib/wasi/src/bin_factory/exec.rs | 41 ++++----------------- lib/wasi/src/runtime/task_manager/mod.rs | 9 ++++- lib/wasi/src/runtime/task_manager/tokio.rs | 15 ++++++-- lib/wasi/src/syscalls/wasix/proc_fork.rs | 37 ++----------------- lib/wasi/src/syscalls/wasix/thread_spawn.rs | 23 +----------- 5 files changed, 31 insertions(+), 94 deletions(-) diff --git a/lib/wasi/src/bin_factory/exec.rs b/lib/wasi/src/bin_factory/exec.rs index c72a76d6e83..e76274b95a5 100644 --- a/lib/wasi/src/bin_factory/exec.rs +++ b/lib/wasi/src/bin_factory/exec.rs @@ -104,22 +104,11 @@ pub fn spawn_exec_module( let tasks_outer = tasks.clone(); let task = { - let spawn_type = memory_spawn; let mut store = store; - - move || { + move |module, memory| { // Create the WasiFunctionEnv let mut wasi_env = env; wasi_env.runtime = runtime; - let memory = match wasi_env.tasks().build_memory(spawn_type) { - Ok(m) => m, - Err(err) => { - error!("wasi[{}]::wasm could not build memory error ({})", pid, err); - wasi_env.cleanup(Some(Errno::Noexec as ExitCode)); - return; - } - }; - let thread = WasiThreadGuard::new(wasi_env.thread.clone()); let mut wasi_env = WasiFunctionEnv::new(&mut store, wasi_env); @@ -199,28 +188,12 @@ pub fn spawn_exec_module( } }; - // TODO: handle this better - required because of Module not being Send. - #[cfg(feature = "js")] - let task = { - struct UnsafeWrapper { - inner: Box, - } - - unsafe impl Send for UnsafeWrapper {} - - let inner = UnsafeWrapper { - inner: Box::new(task), - }; - - move || { - (inner.inner)(); - } - }; - - tasks_outer.task_wasm(Box::new(task)).map_err(|err| { - error!("wasi[{}]::failed to launch module - {}", pid, err); - VirtualBusError::UnknownError - })? + tasks_outer + .task_wasm(Box::new(task), module, memory_spawn) + .map_err(|err| { + error!("wasi[{}]::failed to launch module - {}", pid, err); + VirtualBusError::UnknownError + })? }; Ok(join_handle) diff --git a/lib/wasi/src/runtime/task_manager/mod.rs b/lib/wasi/src/runtime/task_manager/mod.rs index 70fd9566443..96b69b8e61f 100644 --- a/lib/wasi/src/runtime/task_manager/mod.rs +++ b/lib/wasi/src/runtime/task_manager/mod.rs @@ -6,7 +6,7 @@ use std::{pin::Pin, time::Duration}; use ::tokio::runtime::Handle; use futures::Future; -use wasmer::MemoryType; +use wasmer::{MemoryType, Module}; #[cfg(feature = "js")] use wasmer::VMMemory; @@ -67,7 +67,12 @@ pub trait VirtualTaskManager: std::fmt::Debug + Send + Sync + 'static { /// Starts an asynchronous task will will run on a dedicated thread /// pulled from the worker pool that has a stateful thread local variable /// It is ok for this task to block execution and any async futures within its scope - fn task_wasm(&self, task: Box) -> Result<(), WasiThreadError>; + fn task_wasm( + &self, + task: Box) + Send + 'static>, + module: Module, + spawn_type: SpawnType, + ) -> Result<(), WasiThreadError>; /// Starts an asynchronous task will will run on a dedicated thread /// pulled from the worker pool. It is ok for this task to block execution diff --git a/lib/wasi/src/runtime/task_manager/tokio.rs b/lib/wasi/src/runtime/task_manager/tokio.rs index 81f0c708c53..5554da0efe2 100644 --- a/lib/wasi/src/runtime/task_manager/tokio.rs +++ b/lib/wasi/src/runtime/task_manager/tokio.rs @@ -2,7 +2,10 @@ use std::{pin::Pin, time::Duration}; use futures::Future; use tokio::runtime::Handle; -use wasmer::vm::{VMMemory, VMSharedMemory}; +use wasmer::{ + vm::{VMMemory, VMSharedMemory}, + Module, +}; use crate::os::task::thread::WasiThreadError; @@ -120,10 +123,16 @@ impl VirtualTaskManager for TokioTaskManager { } /// See [`VirtualTaskManager::enter`]. - fn task_wasm(&self, task: Box) -> Result<(), WasiThreadError> { + fn task_wasm( + &self, + task: Box) + Send + 'static>, + module: Module, + spawn_type: SpawnType, + ) -> Result<(), WasiThreadError> { + let memory = self.build_memory(spawn_type)?; self.0.spawn_blocking(move || { // Invoke the callback - task(); + task(module, memory); }); Ok(()) } diff --git a/lib/wasi/src/syscalls/wasix/proc_fork.rs b/lib/wasi/src/syscalls/wasix/proc_fork.rs index 22ba49086df..69100d46c7c 100644 --- a/lib/wasi/src/syscalls/wasix/proc_fork.rs +++ b/lib/wasi/src/syscalls/wasix/proc_fork.rs @@ -194,7 +194,8 @@ pub fn proc_fork( let mut store = fork_store; let module = fork_module; - let task = move || { + let spawn_type = SpawnType::NewThread(fork_memory); + let task = move |module, memory| { // Create the WasiFunctionEnv let pid = child_env.pid(); let tid = child_env.tid(); @@ -202,20 +203,6 @@ pub fn proc_fork( let mut ctx = WasiFunctionEnv::new(&mut store, child_env); // fork_store, fork_module, - let spawn_type = SpawnType::NewThread(fork_memory); - - let memory = match tasks.build_memory(spawn_type) { - Ok(m) => m, - Err(err) => { - error!( - "wasi[{}:{}]::{} failed - could not build instance memory - {}", - pid, tid, fork_op, err - ); - ctx.data(&store).cleanup(Some(Errno::Noexec as ExitCode)); - return; - } - }; - // Let's instantiate the module with the imports. let (mut import_object, init) = import_object_for_all_wasi_versions(&module, &mut store, &ctx.env); @@ -298,26 +285,8 @@ pub fn proc_fork( drop(child_handle); }; - // TODO: handle this better - required because of Module not being Send. - #[cfg(feature = "js")] - let task = { - struct UnsafeWrapper { - inner: Box, - } - - unsafe impl Send for UnsafeWrapper {} - - let inner = UnsafeWrapper { - inner: Box::new(task), - }; - - move || { - (inner.inner)(); - } - }; - tasks_outer - .task_wasm(Box::new(task)) + .task_wasm(Box::new(task), module, spawn_type) .map_err(|err| { warn!( "wasi[{}:{}]::failed to fork as the process could not be spawned - {}", diff --git a/lib/wasi/src/syscalls/wasix/thread_spawn.rs b/lib/wasi/src/syscalls/wasix/thread_spawn.rs index dd12dcdcec0..a61cbd02b3f 100644 --- a/lib/wasi/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasi/src/syscalls/wasix/thread_spawn.rs @@ -299,33 +299,14 @@ pub fn thread_spawn( let thread_module = env.inner().instance.module().clone(); let tasks2 = tasks.clone(); - let task = move || { + let task = move |thread_module, mut thread_memory| { // FIXME: should not use unwrap() here! (initializiation refactor) - let mut thread_memory = tasks2.build_memory(spawn_type).unwrap(); let mut store = Some(store); execute_module(&mut store, thread_module, &mut thread_memory); }; - // TODO: handle this better - required because of Module not being Send. - #[cfg(feature = "js")] - let task = { - struct UnsafeWrapper { - inner: Box, - } - - unsafe impl Send for UnsafeWrapper {} - - let inner = UnsafeWrapper { - inner: Box::new(task), - }; - - move || { - (inner.inner)(); - } - }; - wasi_try!(tasks - .task_wasm(Box::new(task),) + .task_wasm(Box::new(task), thread_module, spawn_type) .map_err(|err| { Into::::into(err) })); } _ => { From d2c381e7502357757f8b1fc25ec93f8a268c2c8d Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 1 Mar 2023 17:30:00 +1100 Subject: [PATCH 02/12] The VMMemory must be convertable into a JsValue --- lib/api/src/js/vm.rs | 6 ++++++ lib/wasi/src/bin_factory/exec.rs | 5 ++--- lib/wasi/src/runtime/task_manager/mod.rs | 5 +++-- lib/wasi/src/runtime/task_manager/tokio.rs | 7 ++++--- lib/wasi/src/syscalls/wasix/proc_fork.rs | 6 +++--- lib/wasi/src/syscalls/wasix/thread_spawn.rs | 4 ++-- 6 files changed, 20 insertions(+), 13 deletions(-) diff --git a/lib/api/src/js/vm.rs b/lib/api/src/js/vm.rs index 08e43ffc233..349663b00fa 100644 --- a/lib/api/src/js/vm.rs +++ b/lib/api/src/js/vm.rs @@ -109,6 +109,12 @@ impl VMMemory { } } +impl From for JsValue { + fn from(value: VMMemory) -> Self { + JsValue::from(value.memory) + } +} + #[derive(Clone, Debug, PartialEq)] pub struct VMGlobal { pub(crate) global: Global, diff --git a/lib/wasi/src/bin_factory/exec.rs b/lib/wasi/src/bin_factory/exec.rs index e76274b95a5..12775e0d265 100644 --- a/lib/wasi/src/bin_factory/exec.rs +++ b/lib/wasi/src/bin_factory/exec.rs @@ -104,8 +104,7 @@ pub fn spawn_exec_module( let tasks_outer = tasks.clone(); let task = { - let mut store = store; - move |module, memory| { + move |mut store, module, memory| { // Create the WasiFunctionEnv let mut wasi_env = env; wasi_env.runtime = runtime; @@ -189,7 +188,7 @@ pub fn spawn_exec_module( }; tasks_outer - .task_wasm(Box::new(task), module, memory_spawn) + .task_wasm(Box::new(task), store, module, memory_spawn) .map_err(|err| { error!("wasi[{}]::failed to launch module - {}", pid, err); VirtualBusError::UnknownError diff --git a/lib/wasi/src/runtime/task_manager/mod.rs b/lib/wasi/src/runtime/task_manager/mod.rs index 96b69b8e61f..372a351fc9d 100644 --- a/lib/wasi/src/runtime/task_manager/mod.rs +++ b/lib/wasi/src/runtime/task_manager/mod.rs @@ -6,7 +6,7 @@ use std::{pin::Pin, time::Duration}; use ::tokio::runtime::Handle; use futures::Future; -use wasmer::{MemoryType, Module}; +use wasmer::{MemoryType, Module, Store}; #[cfg(feature = "js")] use wasmer::VMMemory; @@ -69,7 +69,8 @@ pub trait VirtualTaskManager: std::fmt::Debug + Send + Sync + 'static { /// It is ok for this task to block execution and any async futures within its scope fn task_wasm( &self, - task: Box) + Send + 'static>, + task: Box) + Send + 'static>, + store: Store, module: Module, spawn_type: SpawnType, ) -> Result<(), WasiThreadError>; diff --git a/lib/wasi/src/runtime/task_manager/tokio.rs b/lib/wasi/src/runtime/task_manager/tokio.rs index 5554da0efe2..48a35887cd8 100644 --- a/lib/wasi/src/runtime/task_manager/tokio.rs +++ b/lib/wasi/src/runtime/task_manager/tokio.rs @@ -4,7 +4,7 @@ use futures::Future; use tokio::runtime::Handle; use wasmer::{ vm::{VMMemory, VMSharedMemory}, - Module, + Module, Store, }; use crate::os::task::thread::WasiThreadError; @@ -125,14 +125,15 @@ impl VirtualTaskManager for TokioTaskManager { /// See [`VirtualTaskManager::enter`]. fn task_wasm( &self, - task: Box) + Send + 'static>, + task: Box) + Send + 'static>, + store: Store, module: Module, spawn_type: SpawnType, ) -> Result<(), WasiThreadError> { let memory = self.build_memory(spawn_type)?; self.0.spawn_blocking(move || { // Invoke the callback - task(module, memory); + task(store, module, memory); }); Ok(()) } diff --git a/lib/wasi/src/syscalls/wasix/proc_fork.rs b/lib/wasi/src/syscalls/wasix/proc_fork.rs index 69100d46c7c..26622acd889 100644 --- a/lib/wasi/src/syscalls/wasix/proc_fork.rs +++ b/lib/wasi/src/syscalls/wasix/proc_fork.rs @@ -191,11 +191,11 @@ pub fn proc_fork( let tasks = tasks.clone(); let tasks_outer = tasks.clone(); - let mut store = fork_store; + let store = fork_store; let module = fork_module; let spawn_type = SpawnType::NewThread(fork_memory); - let task = move |module, memory| { + let task = move |mut store, module, memory| { // Create the WasiFunctionEnv let pid = child_env.pid(); let tid = child_env.tid(); @@ -286,7 +286,7 @@ pub fn proc_fork( }; tasks_outer - .task_wasm(Box::new(task), module, spawn_type) + .task_wasm(Box::new(task), store, module, spawn_type) .map_err(|err| { warn!( "wasi[{}:{}]::failed to fork as the process could not be spawned - {}", diff --git a/lib/wasi/src/syscalls/wasix/thread_spawn.rs b/lib/wasi/src/syscalls/wasix/thread_spawn.rs index a61cbd02b3f..fb650f9ea85 100644 --- a/lib/wasi/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasi/src/syscalls/wasix/thread_spawn.rs @@ -299,14 +299,14 @@ pub fn thread_spawn( let thread_module = env.inner().instance.module().clone(); let tasks2 = tasks.clone(); - let task = move |thread_module, mut thread_memory| { + let task = move |store, thread_module, mut thread_memory| { // FIXME: should not use unwrap() here! (initializiation refactor) let mut store = Some(store); execute_module(&mut store, thread_module, &mut thread_memory); }; wasi_try!(tasks - .task_wasm(Box::new(task), thread_module, spawn_type) + .task_wasm(Box::new(task), store, thread_module, spawn_type) .map_err(|err| { Into::::into(err) })); } _ => { From b8ecd90795ff13b9d388a75afb3bfb276433a9d1 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Tue, 28 Feb 2023 15:59:55 -0800 Subject: [PATCH 03/12] Fixed missing memory export in WASI as the API got simplified --- lib/wasi/src/bin_factory/exec.rs | 19 +++++++++++-------- lib/wasi/src/state/env.rs | 19 +++++++++++-------- lib/wasi/src/state/func_env.rs | 27 +++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/lib/wasi/src/bin_factory/exec.rs b/lib/wasi/src/bin_factory/exec.rs index 12775e0d265..9250a9a89d3 100644 --- a/lib/wasi/src/bin_factory/exec.rs +++ b/lib/wasi/src/bin_factory/exec.rs @@ -115,13 +115,14 @@ pub fn spawn_exec_module( // Let's instantiate the module with the imports. let (mut import_object, init) = import_object_for_all_wasi_versions(&module, &mut store, &wasi_env.env); - if let Some(memory) = memory { - import_object.define( - "env", - "memory", - Memory::new_from_existing(&mut store, memory), - ); - } + let imported_memory = if let Some(memory) = memory { + let imported_memory = Memory::new_from_existing(&mut store, memory); + import_object.define("env", "memory", imported_memory.clone()); + Some(imported_memory) + } else { + None + }; + let instance = match Instance::new(&mut store, &module, &import_object) { Ok(a) => a, Err(err) => { @@ -136,7 +137,9 @@ pub fn spawn_exec_module( init(&instance, &store).unwrap(); // Initialize the WASI environment - if let Err(err) = wasi_env.initialize(&mut store, instance.clone()) { + if let Err(err) = + wasi_env.initialize_with_memory(&mut store, instance.clone(), imported_memory) + { error!("wasi[{}]::wasi initialize error ({})", pid, err); wasi_env .data(&store) diff --git a/lib/wasi/src/state/env.rs b/lib/wasi/src/state/env.rs index a5e3c2d9c5e..276bd1ce6e5 100644 --- a/lib/wasi/src/state/env.rs +++ b/lib/wasi/src/state/env.rs @@ -447,13 +447,14 @@ impl WasiEnv { // Let's instantiate the module with the imports. let (mut import_object, instance_init_callback) = import_object_for_all_wasi_versions(&module, &mut store, &func_env.env); - if let Some(memory) = memory { - import_object.define( - "env", - "memory", - Memory::new_from_existing(&mut store, memory), - ); - } + + let imported_memory = if let Some(memory) = memory { + let imported_memory = Memory::new_from_existing(&mut store, memory); + import_object.define("env", "memory", imported_memory.clone()); + Some(imported_memory) + } else { + None + }; // Construct the instance. let instance = match Instance::new(&mut store, &module, &import_object) { @@ -471,7 +472,9 @@ impl WasiEnv { instance_init_callback(&instance, &store).unwrap(); // Initialize the WASI environment - if let Err(err) = func_env.initialize(&mut store, instance.clone()) { + if let Err(err) = + func_env.initialize_with_memory(&mut store, instance.clone(), imported_memory) + { tracing::error!("wasi[{}]::wasi initialize error ({})", pid, err); func_env .data(&store) diff --git a/lib/wasi/src/state/func_env.rs b/lib/wasi/src/state/func_env.rs index dbb45e61006..900017f8fa1 100644 --- a/lib/wasi/src/state/func_env.rs +++ b/lib/wasi/src/state/func_env.rs @@ -1,5 +1,5 @@ use tracing::trace; -use wasmer::{AsStoreMut, AsStoreRef, ExportError, FunctionEnv, Imports, Instance, Module}; +use wasmer::{AsStoreMut, AsStoreRef, ExportError, FunctionEnv, Imports, Instance, Memory, Module}; use wasmer_wasi_types::wasi::ExitCode; use crate::{ @@ -51,6 +51,19 @@ impl WasiFunctionEnv { &mut self, store: &mut impl AsStoreMut, instance: Instance, + ) -> Result<(), ExportError> { + self.initialize_with_memory(store, instance, None) + } + + /// Initializes the WasiEnv using the instance exports and a provided optional memory + /// (this must be executed before attempting to use it) + /// (as the stores can not by themselves be passed between threads we can store the module + /// in a thread-local variables and use it later - for multithreading) + pub fn initialize_with_memory( + &mut self, + store: &mut impl AsStoreMut, + instance: Instance, + memory: Option, ) -> Result<(), ExportError> { // List all the exports and imports for ns in instance.module().exports() { @@ -65,7 +78,17 @@ impl WasiFunctionEnv { // First we get the malloc function which if it exists will be used to // create the pthread_self structure - let memory = instance.exports.get_memory("memory")?.clone(); + let memory = instance.exports.get_memory("memory").map_or_else( + |e| { + if let Some(memory) = memory { + Ok(memory) + } else { + Err(e) + } + }, + |v| Ok(v.clone()), + )?; + let new_inner = WasiInstanceHandles::new(memory, store, instance); let env = self.data_mut(store); From 5fa6f90b44fb1027845842278d6b6251f31c9860 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 2 Mar 2023 07:33:02 +1100 Subject: [PATCH 04/12] Fixed an issue where the globals were not being properly captured and restored for JS --- lib/api/src/js/store.rs | 18 +++++++++++++++--- lib/vm/src/store.rs | 7 +++++++ lib/wasi/src/utils/store.rs | 19 +------------------ 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/lib/api/src/js/store.rs b/lib/api/src/js/store.rs index 7fa22cfa855..8b66fd890f9 100644 --- a/lib/api/src/js/store.rs +++ b/lib/api/src/js/store.rs @@ -309,14 +309,26 @@ mod objects { self.globals.iter() } + /// Return an vector of all globals and converted to u128 + pub fn as_u128_globals(&self) -> Vec { + self.iter_globals() + .map(|v| v.global.value().as_f64().unwrap() as u128) + .collect() + } + /// Set a global, at index idx. Will panic if idx is out of range /// Safety: the caller should check taht the raw value is compatible /// with destination VMGlobal type - pub fn set_global_unchecked(&self, idx: usize, val: u128) { + pub fn set_global_unchecked(&self, idx: usize, new_val: u128) { assert!(idx < self.globals.len()); - let value = JsValue::from(val); - self.globals[idx].global.set_value(&value); + let g = &self.globals[idx].global; + let cur_val = g.value().as_f64().unwrap(); + let new_val = new_val as f64; + if cur_val != new_val { + let new_value = JsValue::from(new_val); + g.set_value(&new_value); + } } } diff --git a/lib/vm/src/store.rs b/lib/vm/src/store.rs index c28421474d8..32f7742cc15 100644 --- a/lib/vm/src/store.rs +++ b/lib/vm/src/store.rs @@ -108,6 +108,13 @@ impl StoreObjects { self.globals.iter() } + /// Return an immutable iterator over all globals and convert them to u128 + pub fn as_u128_globals(&self) -> Vec { + self.iter_globals() + .map(|v| unsafe { v.vmglobal().as_ref().val.u128 }) + .collect() + } + /// Set a global, at index idx. Will panic if idx is out of range /// Safety: the caller should check taht the raw value is compatible /// with destination VMGlobal type diff --git a/lib/wasi/src/utils/store.rs b/lib/wasi/src/utils/store.rs index 2ab5f33f1b4..161ae58d4f8 100644 --- a/lib/wasi/src/utils/store.rs +++ b/lib/wasi/src/utils/store.rs @@ -17,24 +17,7 @@ impl InstanceSnapshot { pub fn capture_snapshot(store: &mut impl wasmer::AsStoreMut) -> InstanceSnapshot { let objs = store.objects_mut(); - let globals = objs - .iter_globals() - .map(|v| { - // Safety: - // We have a mutable reference to the store, - // which means no-one else can alter the globals or drop the memory. - #[cfg(feature = "sys")] - unsafe { - v.vmglobal().as_ref().val.u128 - } - #[cfg(not(feature = "sys"))] - { - let _ = v; - unimplemented!("capture_snapshot is not implemented for js") - } - }) - .collect(); - + let globals = objs.as_u128_globals(); InstanceSnapshot { globals } } From a72605a2fe40db8199185cedfff1a3d8964f33ec Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 2 Mar 2023 07:48:55 +1100 Subject: [PATCH 05/12] Fixed an issue where tokio time was being used inside JS runtimes which is not supported --- lib/wasi/src/state/env.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/wasi/src/state/env.rs b/lib/wasi/src/state/env.rs index 276bd1ce6e5..52fbc8830a3 100644 --- a/lib/wasi/src/state/env.rs +++ b/lib/wasi/src/state/env.rs @@ -898,13 +898,16 @@ impl WasiEnv { let (tx, rx) = std::sync::mpsc::channel(); self.tasks() .task_dedicated(Box::new(move || { + let tasks_inner = tasks.clone(); tasks.runtime().block_on(async { - let fut = tokio::time::timeout(CLEANUP_TIMEOUT, state.fs.close_all()); - - if let Err(err) = fut.await { - tracing::warn!( - "WasiEnv::cleanup has timed out after {CLEANUP_TIMEOUT:?}: {err}" - ); + let timeout = tasks_inner.sleep_now(CLEANUP_TIMEOUT); + tokio::select! { + _ = timeout => { + tracing::warn!( + "WasiEnv::cleanup has timed out after {CLEANUP_TIMEOUT:?}" + ); + }, + _ = state.fs.close_all() => { } } }); tx.send(()).ok(); From fa41ae375dc8fbf7a28788592718435e57219a72 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 2 Mar 2023 07:59:30 +1100 Subject: [PATCH 06/12] Restored the uses package dependencies --- lib/wasi/src/os/console/mod.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/wasi/src/os/console/mod.rs b/lib/wasi/src/os/console/mod.rs index 08283f4eae1..591b7d5fda0 100644 --- a/lib/wasi/src/os/console/mod.rs +++ b/lib/wasi/src/os/console/mod.rs @@ -222,14 +222,19 @@ impl Console { let wasi_process = env.process.clone(); - // TODO: fetching dependencies should be moved to the builder! - // if let Err(err) = env.uses(self.uses.clone()) { - // tasks.block_on(async { - // let _ = self.runtime.stderr(format!("{}\r\n", err).as_bytes()).await; - // }); - // tracing::debug!("failed to load used dependency - {}", err); - // return Err(crate::vbus::VirtualBusError::BadRequest); - // } + if let Err(err) = env.uses(self.uses.clone()) { + let mut stderr = self.stderr.clone(); + tasks.block_on(async { + wasmer_vfs::AsyncWriteExt::write_all( + &mut stderr, + format!("{}\r\n", err).as_bytes(), + ) + .await + .ok(); + }); + tracing::debug!("failed to load used dependency - {}", err); + return Err(VirtualBusError::BadRequest); + } // Build the config // Run the binary From e81a037b3208bbcfce8cd3cef54ebe1e54b83405 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 2 Mar 2023 10:19:04 +1100 Subject: [PATCH 07/12] Fixed a bug on the process join callback and the globals --- lib/api/src/js/externals/global.rs | 12 +++++++++--- lib/wasi/src/os/task/process.rs | 2 +- lib/wasi/src/syscalls/wasix/proc_join.rs | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/api/src/js/externals/global.rs b/lib/api/src/js/externals/global.rs index 2572cbb1fbe..9b3f46ef8a0 100644 --- a/lib/api/src/js/externals/global.rs +++ b/lib/api/src/js/externals/global.rs @@ -130,9 +130,15 @@ impl Global { /// ``` pub fn get(&self, store: &impl AsStoreRef) -> Value { unsafe { - let raw = self.handle.global.value().as_f64().unwrap(); - let ty = self.handle.ty; - Value::from_raw(store, ty.ty, raw) + match self.handle.global.value().as_f64() { + Some(raw) => { + let ty = self.handle.ty; + Value::from_raw(store, ty.ty, raw) + }, + None => { + Value::null() + } + } } } diff --git a/lib/wasi/src/os/task/process.rs b/lib/wasi/src/os/task/process.rs index 38fb54d2907..1d6a016b142 100644 --- a/lib/wasi/src/os/task/process.rs +++ b/lib/wasi/src/os/task/process.rs @@ -325,7 +325,7 @@ impl WasiProcess { children.clone() }; if children.is_empty() { - return Err(Errno::Child); + return Ok(None); } let mut waits = Vec::new(); diff --git a/lib/wasi/src/syscalls/wasix/proc_join.rs b/lib/wasi/src/syscalls/wasix/proc_join.rs index f55839be17d..ccbaa796f6d 100644 --- a/lib/wasi/src/syscalls/wasix/proc_join.rs +++ b/lib/wasi/src/syscalls/wasix/proc_join.rs @@ -29,6 +29,15 @@ pub fn proc_join( let mut process = ctx.data_mut().process.clone(); let child_exit = wasi_try_ok!(__asyncify(&mut ctx, None, async move { process.join_any_child().await + }).map_err(|err| { + trace!( + "wasi[{}:{}]::child join failed (pid={}) - {}", + ctx.data().pid(), + ctx.data().tid(), + pid, + err + ); + err })?); return match child_exit { Some((pid, exit_code)) => { @@ -68,6 +77,15 @@ pub fn proc_join( let exit_code = wasi_try_ok!(__asyncify(&mut ctx, None, async move { let code = process.join().await.unwrap_or(Errno::Child as u32); Ok(code) + }).map_err(|err| { + trace!( + "wasi[{}:{}]::child join failed (pid={}) - {}", + ctx.data().pid(), + ctx.data().tid(), + pid, + err + ); + err })?); trace!("child ({}) exited with {}", pid.raw(), exit_code); From 06524345c72b7ef53b59b6e772c49e1c2f2b6f46 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 2 Mar 2023 10:57:44 +1100 Subject: [PATCH 08/12] Last fixes to restore web functionality --- lib/api/src/js/externals/global.rs | 4 +-- lib/vm/src/store.rs | 2 +- lib/wasi/src/bin_factory/exec.rs | 4 --- lib/wasi/src/bin_factory/module_cache.rs | 44 ++++++++++++------------ lib/wasi/src/syscalls/wasix/proc_join.rs | 29 +++++++++------- lib/wasi/src/wapm/mod.rs | 9 +++++ 6 files changed, 50 insertions(+), 42 deletions(-) diff --git a/lib/api/src/js/externals/global.rs b/lib/api/src/js/externals/global.rs index 9b3f46ef8a0..a01bb651e70 100644 --- a/lib/api/src/js/externals/global.rs +++ b/lib/api/src/js/externals/global.rs @@ -134,10 +134,8 @@ impl Global { Some(raw) => { let ty = self.handle.ty; Value::from_raw(store, ty.ty, raw) - }, - None => { - Value::null() } + None => Value::null(), } } } diff --git a/lib/vm/src/store.rs b/lib/vm/src/store.rs index 32f7742cc15..8b2633929d9 100644 --- a/lib/vm/src/store.rs +++ b/lib/vm/src/store.rs @@ -108,7 +108,7 @@ impl StoreObjects { self.globals.iter() } - /// Return an immutable iterator over all globals and convert them to u128 + /// Return an vector of all globals and converted to u128 pub fn as_u128_globals(&self) -> Vec { self.iter_globals() .map(|v| unsafe { v.vmglobal().as_ref().val.u128 }) diff --git a/lib/wasi/src/bin_factory/exec.rs b/lib/wasi/src/bin_factory/exec.rs index 9250a9a89d3..8579efcd93b 100644 --- a/lib/wasi/src/bin_factory/exec.rs +++ b/lib/wasi/src/bin_factory/exec.rs @@ -29,11 +29,7 @@ pub fn spawn_exec( #[cfg(not(feature = "sys"))] let compiler = "generic"; - #[cfg(feature = "sys")] let module = compiled_modules.get_compiled_module(&store, binary.hash().as_str(), compiler); - #[cfg(not(feature = "sys"))] - let module = compiled_modules.get_compiled_module(binary.hash().as_str(), compiler); - let module = match (module, binary.entry.as_ref()) { (Some(a), _) => a, (None, Some(entry)) => { diff --git a/lib/wasi/src/bin_factory/module_cache.rs b/lib/wasi/src/bin_factory/module_cache.rs index 7eb43100f01..ffc55e26b13 100644 --- a/lib/wasi/src/bin_factory/module_cache.rs +++ b/lib/wasi/src/bin_factory/module_cache.rs @@ -187,6 +187,7 @@ impl ModuleCache { pub fn get_compiled_module( &self, #[cfg(feature = "sys")] engine: &impl wasmer::AsEngineRef, + #[cfg(feature = "js")] engine: &impl wasmer::AsStoreRef, data_hash: &str, compiler: &str, ) -> Option { @@ -215,34 +216,33 @@ impl ModuleCache { } } - #[cfg(feature = "sys")] - { - // slow path - let path = std::path::Path::new(self.cache_compile_dir.as_str()) - .join(format!("{}.bin", key).as_str()); - if let Ok(data) = std::fs::read(path) { - let mut decoder = weezl::decode::Decoder::new(weezl::BitOrder::Msb, 8); - if let Ok(data) = decoder.decode(&data[..]) { - let module_bytes = bytes::Bytes::from(data); - - // Load the module - let module = unsafe { Module::deserialize(engine, &module_bytes[..]).unwrap() }; - - if let Some(cache) = &self.cached_modules { - let mut cache = cache.write().unwrap(); - cache.insert(key.clone(), module.clone()); - } + // slow path + let path = std::path::Path::new(self.cache_compile_dir.as_str()) + .join(format!("{}.bin", key).as_str()); + if let Ok(data) = std::fs::read(path.as_path()) { + tracing::trace!("bin file found: {:?} [len={}]", path.as_path(), data.len()); + let mut decoder = weezl::decode::Decoder::new(weezl::BitOrder::Msb, 8); + if let Ok(data) = decoder.decode(&data[..]) { + let module_bytes = bytes::Bytes::from(data); - THREAD_LOCAL_CACHED_MODULES.with(|cache| { - let mut cache = cache.borrow_mut(); - cache.insert(key.clone(), module.clone()); - }); - return Some(module); + // Load the module + let module = unsafe { Module::deserialize(engine, &module_bytes[..]).unwrap() }; + + if let Some(cache) = &self.cached_modules { + let mut cache = cache.write().unwrap(); + cache.insert(key.clone(), module.clone()); } + + THREAD_LOCAL_CACHED_MODULES.with(|cache| { + let mut cache = cache.borrow_mut(); + cache.insert(key.clone(), module.clone()); + }); + return Some(module); } } // Not found + tracing::trace!("bin file not found: {:?}", path); None } diff --git a/lib/wasi/src/syscalls/wasix/proc_join.rs b/lib/wasi/src/syscalls/wasix/proc_join.rs index ccbaa796f6d..f8bae8fc40f 100644 --- a/lib/wasi/src/syscalls/wasix/proc_join.rs +++ b/lib/wasi/src/syscalls/wasix/proc_join.rs @@ -27,18 +27,22 @@ pub fn proc_join( // If the ID is maximum then it means wait for any of the children if pid == u32::MAX { let mut process = ctx.data_mut().process.clone(); - let child_exit = wasi_try_ok!(__asyncify(&mut ctx, None, async move { - process.join_any_child().await - }).map_err(|err| { - trace!( - "wasi[{}:{}]::child join failed (pid={}) - {}", - ctx.data().pid(), - ctx.data().tid(), - pid, + let child_exit = + wasi_try_ok!(__asyncify( + &mut ctx, + None, + async move { process.join_any_child().await } + ) + .map_err(|err| { + trace!( + "wasi[{}:{}]::child join failed (pid={}) - {}", + ctx.data().pid(), + ctx.data().tid(), + pid, + err + ); err - ); - err - })?); + })?); return match child_exit { Some((pid, exit_code)) => { trace!( @@ -77,7 +81,8 @@ pub fn proc_join( let exit_code = wasi_try_ok!(__asyncify(&mut ctx, None, async move { let code = process.join().await.unwrap_or(Errno::Child as u32); Ok(code) - }).map_err(|err| { + }) + .map_err(|err| { trace!( "wasi[{}:{}]::child join failed (pid={}) - {}", ctx.data().pid(), diff --git a/lib/wasi/src/wapm/mod.rs b/lib/wasi/src/wapm/mod.rs index 227a70bc413..4adaa99c04f 100644 --- a/lib/wasi/src/wapm/mod.rs +++ b/lib/wasi/src/wapm/mod.rs @@ -409,6 +409,15 @@ where atom.len(), command ); + if pck.entry.is_none() { + trace!("defaulting entry to command [{}]", command); + let entry: &'static [u8] = { + let atom: &'_ [u8] = atom; + std::mem::transmute(atom) + }; + pck.entry = Some(entry.into()); + } + let mut commands = pck.commands.write().unwrap(); commands.push(BinaryPackageCommand::new_with_ownership( command.clone(), From f1c31c65ca10c5a7301473ca88c0526497c189c8 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 3 Mar 2023 16:04:09 +1100 Subject: [PATCH 09/12] All IO operates are now zero copy when on non-browsers while browsers still copy bytes --- lib/api/src/access.rs | 223 ++++++++++++++++++++ lib/api/src/js/mem_access.rs | 26 +++ lib/api/src/lib.rs | 2 + lib/api/src/sys/mem_access.rs | 97 +++++++-- lib/api/src/sys/ptr.rs | 10 + lib/cli/Cargo.toml | 2 +- lib/wasi/src/state/builder.rs | 7 + lib/wasi/src/syscalls/mod.rs | 67 ++---- lib/wasi/src/syscalls/wasi/fd_read.rs | 151 ++++++------- lib/wasi/src/syscalls/wasi/fd_write.rs | 111 ++++++---- lib/wasi/src/syscalls/wasix/proc_fork.rs | 7 + lib/wasi/src/syscalls/wasix/sock_recv.rs | 84 ++------ lib/wasi/src/syscalls/wasix/sock_send.rs | 55 ++--- lib/wasi/src/syscalls/wasix/sock_send_to.rs | 67 +++--- 14 files changed, 580 insertions(+), 329 deletions(-) create mode 100644 lib/api/src/access.rs diff --git a/lib/api/src/access.rs b/lib/api/src/access.rs new file mode 100644 index 00000000000..e0d72b82c5e --- /dev/null +++ b/lib/api/src/access.rs @@ -0,0 +1,223 @@ +use std::mem::MaybeUninit; + +use crate::{WasmRef, WasmSlice}; + +pub(super) enum SliceCow<'a, T> { + #[allow(dead_code)] + Borrowed(&'a mut [T]), + #[allow(dead_code)] + Owned(Vec, bool), +} + +impl<'a, T> AsRef<[T]> for SliceCow<'a, T> { + fn as_ref(&self) -> &[T] { + match self { + Self::Borrowed(buf) => buf.as_ref(), + Self::Owned(buf, _) => buf.as_ref(), + } + } +} + +impl<'a, T> AsMut<[T]> for SliceCow<'a, T> { + fn as_mut(&mut self) -> &mut [T] { + // Note: Zero padding is not required here as its a typed copy which does + // not leak the bytes into the memory + // https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes + match self { + Self::Borrowed(buf) => buf, + Self::Owned(buf, modified) => { + *modified = true; + buf.as_mut() + } + } + } +} + +/// Provides direct memory access to a piece of memory that +/// is owned by WASM +pub struct WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + pub(super) slice: WasmSlice<'a, T>, + pub(super) buf: SliceCow<'a, T>, +} + +impl<'a, T> AsRef<[T]> for WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn as_ref(&self) -> &[T] { + self.buf.as_ref() + } +} + +impl<'a, T> AsMut<[T]> for WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn as_mut(&mut self) -> &mut [T] { + self.buf.as_mut() + } +} + +impl<'a, T> WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + /// Returns an iterator of all the elements in the slice + pub fn iter(&'a self) -> std::slice::Iter<'a, T> { + self.as_ref().iter() + } + + /// Returns an iterator of all the elements in the slice + pub fn iter_mut(&'a mut self) -> std::slice::IterMut<'a, T> { + self.buf.as_mut().iter_mut() + } + + /// Number of elements in this slice + pub fn len(&self) -> usize { + self.buf.as_ref().len() + } +} + +impl<'a> WasmSliceAccess<'a, u8> { + /// Writes to the address pointed to by this `WasmPtr` in a memory. + #[inline] + pub fn copy_from_slice(&mut self, src: &[u8]) { + let dst = self.buf.as_mut(); + dst.copy_from_slice(src); + } +} + +impl<'a, T> Drop for WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn drop(&mut self) { + if let SliceCow::Owned(buf, modified) = &self.buf { + if *modified == true { + self.slice.write_slice(buf.as_ref()).ok(); + } + } + } +} + +pub(super) enum RefCow<'a, T> { + #[allow(dead_code)] + Borrowed(&'a mut T), + #[allow(dead_code)] + Owned(T, bool), +} + +impl<'a, T> AsRef for RefCow<'a, T> { + fn as_ref(&self) -> &T { + match self { + Self::Borrowed(val) => *val, + Self::Owned(val, _) => val, + } + } +} + +impl<'a, T> AsMut for RefCow<'a, T> { + fn as_mut(&mut self) -> &mut T { + // Note: Zero padding is not required here as its a typed copy which does + // not leak the bytes into the memory + // https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes + match self { + Self::Borrowed(val) => *val, + Self::Owned(val, modified) => { + *modified = true; + val + } + } + } +} + +/// Provides direct memory access to a piece of memory that +/// is owned by WASM +pub struct WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + pub(super) ptr: WasmRef<'a, T>, + pub(super) buf: RefCow<'a, T>, +} + +impl<'a, T> AsRef for WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn as_ref(&self) -> &T { + self.buf.as_ref() + } +} + +impl<'a, T> AsMut for WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn as_mut(&mut self) -> &mut T { + self.buf.as_mut() + } +} + +impl<'a, T> WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + /// Reads the address pointed to by this `WasmPtr` in a memory. + #[inline] + pub fn read(&self) -> T + where + T: Clone, + { + self.as_ref().clone() + } + + /// Writes to the address pointed to by this `WasmPtr` in a memory. + #[inline] + pub fn write(&mut self, val: T) { + // Note: Zero padding is not required here as its a typed copy which does + // not leak the bytes into the memory + // https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes + *(self.as_mut()) = val; + } +} + +impl<'a, T> Drop for WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn drop(&mut self) { + if let RefCow::Owned(val, modified) = &self.buf { + if *modified == true { + self.ptr.write(*val).ok(); + } + } + } +} + +impl<'a, T> WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + /// Returns a mutable slice that is not yet initialized + pub fn as_mut_uninit(&mut self) -> &mut [MaybeUninit] { + let ret: &mut [T] = self.buf.as_mut(); + let ret: &mut [MaybeUninit] = unsafe { std::mem::transmute(ret) }; + ret + } +} + +impl<'a, T> WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + /// Returns a reference to an unitialized reference to this value + pub fn as_mut_uninit(&mut self) -> &mut MaybeUninit { + let ret: &mut T = self.buf.as_mut(); + let ret: &mut MaybeUninit = unsafe { std::mem::transmute(ret) }; + ret + } +} diff --git a/lib/api/src/js/mem_access.rs b/lib/api/src/js/mem_access.rs index de82f5c6e1c..00c68eac2fd 100644 --- a/lib/api/src/js/mem_access.rs +++ b/lib/api/src/js/mem_access.rs @@ -405,3 +405,29 @@ impl<'a, T: ValueType> DoubleEndedIterator for WasmSliceIter<'a, T> { } impl<'a, T: ValueType> ExactSizeIterator for WasmSliceIter<'a, T> {} + +impl<'a, T> WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn new(slice: WasmSlice<'a, T>) -> Result { + let buf = slice.read_to_vec()?; + Ok(Self { + slice, + buf: SliceCow::Owned(buf), + }) + } +} + +impl<'a, T> WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn new(ptr: WasmRef<'a, T>) -> Result { + let val = slice.read()?; + Ok(Self { + ptr, + buf: RefCow::Owned(val), + }) + } +} diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index a7d5c730567..e1cb011e4b7 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -441,5 +441,7 @@ mod js; #[cfg(feature = "js")] pub use js::*; +mod access; mod into_bytes; +pub use access::WasmSliceAccess; pub use into_bytes::IntoBytes; diff --git a/lib/api/src/sys/mem_access.rs b/lib/api/src/sys/mem_access.rs index 3075ba60e21..246c637974f 100644 --- a/lib/api/src/sys/mem_access.rs +++ b/lib/api/src/sys/mem_access.rs @@ -1,4 +1,7 @@ -use crate::RuntimeError; +use crate::{ + access::{RefCow, SliceCow, WasmRefAccess}, + RuntimeError, WasmSliceAccess, +}; #[allow(unused_imports)] use crate::{Memory, Memory32, Memory64, MemorySize, MemoryView, WasmPtr}; use std::{ @@ -102,26 +105,19 @@ impl<'a, T: ValueType> WasmRef<'a, T> { /// Reads the location pointed to by this `WasmRef`. #[inline] pub fn read(self) -> Result { - let mut out = MaybeUninit::uninit(); - let buf = - unsafe { slice::from_raw_parts_mut(out.as_mut_ptr() as *mut u8, mem::size_of::()) }; - self.buffer.read(self.offset, buf)?; - Ok(unsafe { out.assume_init() }) + Ok(self.access()?.read()) } /// Writes to the location pointed to by this `WasmRef`. #[inline] pub fn write(self, val: T) -> Result<(), MemoryAccessError> { - let mut data = MaybeUninit::new(val); - let data = unsafe { - slice::from_raw_parts_mut( - data.as_mut_ptr() as *mut MaybeUninit, - mem::size_of::(), - ) - }; - val.zero_padding_bytes(data); - let data = unsafe { slice::from_raw_parts(data.as_ptr() as *const _, data.len()) }; - self.buffer.write(self.offset, data) + Ok(self.access()?.write(val)) + } + + /// Gains direct access to the memory of this slice + #[inline] + pub fn access(self) -> Result, MemoryAccessError> { + WasmRefAccess::new(self) } } @@ -240,6 +236,12 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { WasmSliceIter { slice: self } } + /// Gains direct access to the memory of this slice + #[inline] + pub fn access(self) -> Result, MemoryAccessError> { + WasmSliceAccess::new(self) + } + /// Reads an element of this slice. #[inline] pub fn read(self, idx: u64) -> Result { @@ -404,3 +406,66 @@ impl<'a, T: ValueType> DoubleEndedIterator for WasmSliceIter<'a, T> { } impl<'a, T: ValueType> ExactSizeIterator for WasmSliceIter<'a, T> {} + +impl<'a, T> WasmSliceAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn new(slice: WasmSlice<'a, T>) -> Result { + let total_len = slice + .len + .checked_mul(mem::size_of::() as u64) + .ok_or(MemoryAccessError::Overflow)?; + let end = slice + .offset + .checked_add(total_len) + .ok_or(MemoryAccessError::Overflow)?; + if end > slice.buffer.len as u64 { + #[cfg(feature = "tracing")] + warn!( + "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", + total_len, end, slice.buffer.len + ); + return Err(MemoryAccessError::HeapOutOfBounds); + } + let buf = unsafe { + let buf_ptr: *mut u8 = slice.buffer.base.add(slice.offset as usize); + let buf_ptr: *mut T = std::mem::transmute(buf_ptr); + std::slice::from_raw_parts_mut(buf_ptr, slice.len as usize) + }; + Ok(Self { + slice, + buf: SliceCow::Borrowed(buf), + }) + } +} + +impl<'a, T> WasmRefAccess<'a, T> +where + T: wasmer_types::ValueType, +{ + fn new(ptr: WasmRef<'a, T>) -> Result { + let total_len = mem::size_of::() as u64; + let end = ptr + .offset + .checked_add(total_len) + .ok_or(MemoryAccessError::Overflow)?; + if end > ptr.buffer.len as u64 { + #[cfg(feature = "tracing")] + warn!( + "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})", + total_len, end, ptr.buffer.len + ); + return Err(MemoryAccessError::HeapOutOfBounds); + } + let val = unsafe { + let val_ptr: *mut u8 = ptr.buffer.base.add(ptr.offset as usize); + let val_ptr: *mut T = std::mem::transmute(val_ptr); + &mut *val_ptr + }; + Ok(Self { + ptr, + buf: RefCow::Borrowed(val), + }) + } +} diff --git a/lib/api/src/sys/ptr.rs b/lib/api/src/sys/ptr.rs index 285688b8fff..371a9cc7eb0 100644 --- a/lib/api/src/sys/ptr.rs +++ b/lib/api/src/sys/ptr.rs @@ -1,3 +1,4 @@ +use crate::access::WasmRefAccess; use crate::sys::{externals::MemoryView, FromToNativeWasmType}; use crate::NativeWasmTypeInto; use crate::{MemoryAccessError, WasmRef, WasmSlice}; @@ -193,6 +194,15 @@ impl WasmPtr { } Ok(vec) } + + /// Creates a `WasmAccess` + #[inline] + pub fn access<'a>( + &self, + view: &'a MemoryView, + ) -> Result, MemoryAccessError> { + self.deref(view).access() + } } impl WasmPtr { diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index d60553d1ef3..9301fea0802 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -164,7 +164,7 @@ llvm = [ "compiler", ] debug = ["fern", "wasmer-wasi/logging"] -disable-all-logging = ["wasmer-wasi/disable-all-logging"] +disable-all-logging = ["wasmer-wasi/disable-all-logging", "log/release_max_level_off"] headless = [] headless-minimal = ["headless", "disable-all-logging", "wasi"] diff --git a/lib/wasi/src/state/builder.rs b/lib/wasi/src/state/builder.rs index 4957fb5ea99..e06e5c791eb 100644 --- a/lib/wasi/src/state/builder.rs +++ b/lib/wasi/src/state/builder.rs @@ -755,6 +755,13 @@ impl WasiEnvBuilder { env.data(store).thread.set_status_running(); let res = crate::run_wasi_func_start(start, store); + tracing::trace!( + "wasi[{}:{}]::main exit (code = {:?})", + env.data(store).pid(), + env.data(store).tid(), + res + ); + let exit_code = match &res { Ok(_) => 0, Err(err) => err.as_exit_code().unwrap_or(1), diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 1aa18162409..420de3eab0b 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -26,7 +26,6 @@ pub use wasix::*; pub mod legacy; -use std::mem::MaybeUninit; pub(crate) use std::{ borrow::{Borrow, Cow}, cell::RefCell, @@ -47,6 +46,7 @@ pub(crate) use std::{ thread::LocalKey, time::Duration, }; +use std::{io::IoSlice, mem::MaybeUninit}; pub(crate) use bytes::{Bytes, BytesMut}; pub(crate) use cooked_waker::IntoWaker; @@ -158,32 +158,6 @@ pub(crate) fn write_bytes( result } -pub(crate) fn copy_to_slice( - memory: &MemoryView, - iovs_arr_cell: WasmSlice<__wasi_ciovec_t>, - mut write_loc: &mut [MaybeUninit], -) -> Result { - let mut bytes_written = 0usize; - for iov in iovs_arr_cell.iter() { - let iov_inner = iov.read().map_err(mem_error_to_wasi)?; - - let amt = from_offset::(iov_inner.buf_len)?; - - let (left, right) = write_loc.split_at_mut(amt); - let bytes = WasmPtr::::new(iov_inner.buf) - .slice(memory, iov_inner.buf_len) - .map_err(mem_error_to_wasi)?; - - if amt != bytes.read_to_slice(left).map_err(mem_error_to_wasi)? { - return Err(Errno::Fault); - } - - write_loc = right; - bytes_written += amt; - } - Ok(bytes_written) -} - pub(crate) fn copy_from_slice( mut read_loc: &[u8], memory: &MemoryView, @@ -191,20 +165,21 @@ pub(crate) fn copy_from_slice( ) -> Result { let mut bytes_read = 0usize; - for iov in iovs_arr.iter() { - let iov_inner = iov.read().map_err(mem_error_to_wasi)?; + let iovs_arr = iovs_arr.access().map_err(mem_error_to_wasi)?; + for iovs in iovs_arr.iter() { + let mut buf = WasmPtr::::new(iovs.buf) + .slice(&memory, iovs.buf_len) + .map_err(mem_error_to_wasi)? + .access() + .map_err(mem_error_to_wasi)?; - let to_read = from_offset::(iov_inner.buf_len)?; + let to_read = from_offset::(iovs.buf_len)?; let to_read = to_read.min(read_loc.len()); if to_read == 0 { break; } let (left, right) = read_loc.split_at(to_read); - - let buf = WasmPtr::::new(iov_inner.buf) - .slice(memory, to_read.try_into().map_err(|_| Errno::Overflow)?) - .map_err(mem_error_to_wasi)?; - buf.write_slice(left).map_err(mem_error_to_wasi)?; + buf.copy_from_slice(left); read_loc = right; bytes_read += to_read; @@ -219,21 +194,17 @@ pub(crate) fn read_bytes( ) -> Result { let mut bytes_read = 0usize; - // We allocate the raw_bytes first once instead of - // N times in the loop. - let mut raw_bytes: Vec = vec![0; 10240]; + let iovs_arr = iovs_arr.access().map_err(mem_error_to_wasi)?; + for iovs in iovs_arr.iter() { + let mut buf = WasmPtr::::new(iovs.buf) + .slice(&memory, iovs.buf_len) + .map_err(mem_error_to_wasi)? + .access() + .map_err(mem_error_to_wasi)?; - for iov in iovs_arr.iter() { - let iov_inner = iov.read().map_err(mem_error_to_wasi)?; - raw_bytes.clear(); - let to_read = from_offset::(iov_inner.buf_len)?; - raw_bytes.resize(to_read, 0); - let has_read = reader.read(&mut raw_bytes).map_err(map_io_err)?; + let to_read = buf.len(); + let has_read = reader.read(buf.as_mut()).map_err(map_io_err)?; - let buf = WasmPtr::::new(iov_inner.buf) - .slice(memory, iov_inner.buf_len) - .map_err(mem_error_to_wasi)?; - buf.write_slice(&raw_bytes).map_err(mem_error_to_wasi)?; bytes_read += has_read; if has_read != to_read { return Ok(bytes_read); diff --git a/lib/wasi/src/syscalls/wasi/fd_read.rs b/lib/wasi/src/syscalls/wasi/fd_read.rs index 09ab495236a..91cf7eeae4b 100644 --- a/lib/wasi/src/syscalls/wasi/fd_read.rs +++ b/lib/wasi/src/syscalls/wasi/fd_read.rs @@ -160,8 +160,9 @@ fn fd_read_internal( ) -> Result, WasiError> { wasi_try_ok_ok!(WasiEnv::process_signals_and_exit(ctx)?); - let mut env = ctx.data(); - let state = env.state.clone(); + let env = ctx.data(); + let memory = env.memory_view(&ctx); + let state = env.state(); let fd_entry = wasi_try_ok_ok!(state.fs.get_fd(fd)); let is_stdio = fd_entry.is_stdio; @@ -175,19 +176,6 @@ fn fd_read_internal( let inode = fd_entry.inode; let fd_flags = fd_entry.flags; - let max_size = { - let memory = env.memory_view(ctx); - let iovs_arr = wasi_try_mem_ok_ok!(iovs.slice(&memory, iovs_len)); - let mut max_size = 0usize; - for iovs in iovs_arr.iter() { - let iovs = wasi_try_mem_ok_ok!(iovs.read()); - let buf_len: usize = - wasi_try_ok_ok!(iovs.buf_len.try_into().map_err(|_| Errno::Overflow)); - max_size += buf_len; - } - max_size - }; - let (bytes_read, can_update_cursor) = { let mut guard = inode.write(); match guard.deref_mut() { @@ -196,8 +184,8 @@ fn fd_read_internal( let handle = handle.clone(); drop(guard); - let data = wasi_try_ok_ok!(__asyncify( - ctx, + let read = wasi_try_ok_ok!(__asyncify_light( + env, if fd_flags.contains(Fdflags::NONBLOCK) { Some(Duration::ZERO) } else { @@ -212,33 +200,40 @@ fn fd_read_internal( .map_err(map_io_err)?; } - let mut buf = Vec::with_capacity(max_size); - - let amt = handle.read_buf(&mut buf).await.map_err(|err| { - let err = From::::from(err); - match err { - Errno::Again => { - if is_stdio { - Errno::Badf - } else { - Errno::Again + let mut total_read = 0usize; + + let iovs_arr = + iovs.slice(&memory, iovs_len).map_err(mem_error_to_wasi)?; + let iovs_arr = iovs_arr.access().map_err(mem_error_to_wasi)?; + for iovs in iovs_arr.iter() { + let mut buf = WasmPtr::::new(iovs.buf) + .slice(&memory, iovs.buf_len) + .map_err(mem_error_to_wasi)? + .access() + .map_err(mem_error_to_wasi)?; + + total_read += + handle.read(buf.as_mut()).await.map_err(|err| { + let err = From::::from(err); + match err { + Errno::Again => { + if is_stdio { + Errno::Badf + } else { + Errno::Again + } + } + a => a, } - } - a => a, - } - })?; - Ok(buf) + })?; + } + Ok(total_read) } )? .map_err(|err| match err { Errno::Timedout => Errno::Again, a => a, })); - env = ctx.data(); - - let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok_ok!(iovs.slice(&memory, iovs_len)); - let read = wasi_try_ok_ok!(read_bytes(&data[..], &memory, iovs_arr)); (read, true) } else { return Ok(Err(Errno::Badf)); @@ -250,28 +245,31 @@ fn fd_read_internal( drop(guard); let tasks = env.tasks().clone(); - let res = __asyncify( - ctx, + let res = __asyncify_light( + env, if fd_flags.contains(Fdflags::NONBLOCK) { Some(Duration::ZERO) } else { None }, async { - let mut buf = Vec::with_capacity(max_size); - unsafe { - buf.set_len(max_size); + let mut total_read = 0usize; + + let iovs_arr = + iovs.slice(&memory, iovs_len).map_err(mem_error_to_wasi)?; + let iovs_arr = iovs_arr.access().map_err(mem_error_to_wasi)?; + for iovs in iovs_arr.iter() { + let mut buf = WasmPtr::::new(iovs.buf) + .slice(&memory, iovs.buf_len) + .map_err(mem_error_to_wasi)? + .access() + .map_err(mem_error_to_wasi)?; + + total_read += socket + .recv(tasks.deref(), buf.as_mut_uninit(), fd_flags) + .await?; } - socket - .recv(tasks.deref(), &mut buf, fd_flags) - .await - .map(|amt| { - unsafe { - buf.set_len(amt); - } - let buf: Vec = unsafe { std::mem::transmute(buf) }; - buf - }) + Ok(total_read) }, )? .map_err(|err| match err { @@ -281,16 +279,7 @@ fn fd_read_internal( match res { Err(Errno::Connaborted) | Err(Errno::Connreset) => (0, false), res => { - let data = wasi_try_ok_ok!(res); - env = ctx.data(); - - let data_len = data.len(); - let mut reader = &data[..]; - let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok_ok!(iovs.slice(&memory, iovs_len)); - let bytes_read = wasi_try_ok_ok!( - read_bytes(reader, &memory, iovs_arr).map(|_| data_len) - ); + let bytes_read = wasi_try_ok_ok!(res); (bytes_read, false) } } @@ -300,36 +289,37 @@ fn fd_read_internal( drop(guard); - let data = wasi_try_ok_ok!(__asyncify( - ctx, + let bytes_read = wasi_try_ok_ok!(__asyncify_light( + env, if fd_flags.contains(Fdflags::NONBLOCK) { Some(Duration::ZERO) } else { None }, async move { - // TODO: optimize with MaybeUninit - let mut data = vec![0u8; max_size]; - let amt = wasmer_vfs::AsyncReadExt::read(&mut pipe, &mut data[..]) - .await - .map_err(map_io_err)?; - data.truncate(amt); - Ok(data) + let mut total_read = 0usize; + + let iovs_arr = + iovs.slice(&memory, iovs_len).map_err(mem_error_to_wasi)?; + let iovs_arr = iovs_arr.access().map_err(mem_error_to_wasi)?; + for iovs in iovs_arr.iter() { + let mut buf = WasmPtr::::new(iovs.buf) + .slice(&memory, iovs.buf_len) + .map_err(mem_error_to_wasi)? + .access() + .map_err(mem_error_to_wasi)?; + + total_read += + wasmer_vfs::AsyncReadExt::read(&mut pipe, buf.as_mut()).await?; + } + Ok(total_read) } )? .map_err(|err| match err { Errno::Timedout => Errno::Again, a => a, })); - env = ctx.data(); - let data_len = data.len(); - let mut reader = &data[..]; - - let memory = env.memory_view(ctx); - let iovs_arr = wasi_try_mem_ok_ok!(iovs.slice(&memory, iovs_len)); - let bytes_read = - wasi_try_ok_ok!(read_bytes(reader, &memory, iovs_arr).map(|_| data_len)); (bytes_read, false) } Kind::Dir { .. } | Kind::Root { .. } => { @@ -364,12 +354,11 @@ fn fd_read_internal( // Yield until the notifications are triggered let tasks_inner = env.tasks().clone(); - let val = wasi_try_ok_ok!(__asyncify(ctx, None, async { poller.await })? + let val = wasi_try_ok_ok!(__asyncify_light(env, None, async { poller.await })? .map_err(|err| match err { Errno::Timedout => Errno::Again, a => a, })); - env = ctx.data(); let mut memory = env.memory_view(ctx); let reader = val.to_ne_bytes(); diff --git a/lib/wasi/src/syscalls/wasi/fd_write.rs b/lib/wasi/src/syscalls/wasi/fd_write.rs index eb500a03141..bda5bf0d18e 100644 --- a/lib/wasi/src/syscalls/wasi/fd_write.rs +++ b/lib/wasi/src/syscalls/wasi/fd_write.rs @@ -115,6 +115,8 @@ fn fd_write_internal( let fd_flags = fd_entry.flags; let (bytes_written, can_update_cursor) = { + let iovs_arr = wasi_try_mem_ok!(iovs_arr.access()); + let (mut memory, _) = env.get_memory_and_wasi_state(&ctx, 0); let mut guard = fd_entry.inode.write(); match guard.deref_mut() { @@ -123,18 +125,8 @@ fn fd_write_internal( let handle = handle.clone(); drop(guard); - let buf_len: M::Offset = iovs_arr - .iter() - .filter_map(|a| a.read().ok()) - .map(|a| a.buf_len) - .sum(); - let buf_len: usize = - wasi_try_ok!(buf_len.try_into().map_err(|_| Errno::Inval)); - let mut buf = Vec::with_capacity(buf_len); - wasi_try_ok!(write_bytes(&mut buf, &memory, iovs_arr)); - - let written = wasi_try_ok!(__asyncify( - &mut ctx, + let written = wasi_try_ok!(__asyncify_light( + env, if fd_entry.flags.contains(Fdflags::NONBLOCK) { Some(Duration::ZERO) } else { @@ -149,7 +141,17 @@ fn fd_write_internal( .map_err(map_io_err)?; } - handle.write(&buf[..]).await.map_err(map_io_err) + let mut written = 0usize; + for iovs in iovs_arr.iter() { + let buf = WasmPtr::::new(iovs.buf) + .slice(&memory, iovs.buf_len) + .map_err(mem_error_to_wasi)? + .access() + .map_err(mem_error_to_wasi)?; + written += + handle.write(buf.as_ref()).await.map_err(map_io_err)?; + } + Ok(written) } )? .map_err(|err| match err { @@ -166,33 +168,32 @@ fn fd_write_internal( let socket = socket.clone(); drop(guard); - let buf_len: M::Offset = iovs_arr - .iter() - .filter_map(|a| a.read().ok()) - .map(|a| a.buf_len) - .sum(); - let buf_len: usize = wasi_try_ok!(buf_len.try_into().map_err(|_| Errno::Inval)); - let mut buf = Vec::with_capacity(buf_len); - wasi_try_ok!(write_bytes(&mut buf, &memory, iovs_arr)); - let tasks = env.tasks().clone(); - let written = wasi_try_ok!(__asyncify(&mut ctx, None, async move { - socket.send(tasks.deref(), &buf, fd_flags).await + let written = wasi_try_ok!(__asyncify_light(env, None, async move { + let mut sent = 0usize; + for iovs in iovs_arr.iter() { + let buf = WasmPtr::::new(iovs.buf) + .slice(&memory, iovs.buf_len) + .map_err(mem_error_to_wasi)? + .access() + .map_err(mem_error_to_wasi)?; + sent += socket.send(tasks.deref(), buf.as_ref(), fd_flags).await?; + } + Ok(sent) })?); (written, false) } Kind::Pipe { pipe } => { - let buf_len: M::Offset = iovs_arr - .iter() - .filter_map(|a| a.read().ok()) - .map(|a| a.buf_len) - .sum(); - let buf_len: usize = wasi_try_ok!(buf_len.try_into().map_err(|_| Errno::Inval)); - let mut buf = Vec::with_capacity(buf_len); - wasi_try_ok!(write_bytes(&mut buf, &memory, iovs_arr)); - - let written = - wasi_try_ok!(std::io::Write::write(pipe, &buf[..]).map_err(map_io_err)); + let mut written = 0usize; + for iovs in iovs_arr.iter() { + let buf = wasi_try_ok!(WasmPtr::::new(iovs.buf) + .slice(&memory, iovs.buf_len) + .map_err(mem_error_to_wasi)); + let buf = wasi_try_ok!(buf.access().map_err(mem_error_to_wasi)); + written += wasi_try_ok!( + std::io::Write::write(pipe, buf.as_ref()).map_err(map_io_err) + ); + } (written, false) } Kind::Dir { .. } | Kind::Root { .. } => { @@ -200,23 +201,41 @@ fn fd_write_internal( return Ok(Errno::Isdir); } Kind::EventNotifications(inner) => { - let mut val: [MaybeUninit; 8] = - unsafe { MaybeUninit::uninit().assume_init() }; - let written = wasi_try_ok!(copy_to_slice(&memory, iovs_arr, &mut val[..])); - if written != val.len() { - return Ok(Errno::Inval); - } - let val = u64::from_ne_bytes(unsafe { std::mem::transmute(val) }); + let mut written = 0usize; + for iovs in iovs_arr.iter() { + let buf_len: usize = + wasi_try_ok!(iovs.buf_len.try_into().map_err(|_| Errno::Inval)); + let will_be_written = buf_len; + + let val_cnt = buf_len / std::mem::size_of::(); + let val_cnt: M::Offset = + wasi_try_ok!(val_cnt.try_into().map_err(|_| Errno::Inval)); - inner.write(val); + let vals = wasi_try_ok!(WasmPtr::::new(iovs.buf) + .slice(&memory, val_cnt as M::Offset) + .map_err(mem_error_to_wasi)); + let vals = wasi_try_ok!(vals.access().map_err(mem_error_to_wasi)); + for val in vals.iter() { + inner.write(*val); + } + written += will_be_written; + } (written, false) } Kind::Symlink { .. } => return Ok(Errno::Inval), Kind::Buffer { buffer } => { - let written = - wasi_try_ok!(write_bytes(&mut buffer[offset..], &memory, iovs_arr)); - (written, true) + let mut written = 0usize; + for iovs in iovs_arr.iter() { + let buf = wasi_try_ok!(WasmPtr::::new(iovs.buf) + .slice(&memory, iovs.buf_len) + .map_err(mem_error_to_wasi)); + let buf = wasi_try_ok!(buf.access().map_err(mem_error_to_wasi)); + written += wasi_try_ok!( + std::io::Write::write(buffer, buf.as_ref()).map_err(map_io_err) + ); + } + (written, false) } } }; diff --git a/lib/wasi/src/syscalls/wasix/proc_fork.rs b/lib/wasi/src/syscalls/wasix/proc_fork.rs index 26622acd889..3f8aea7b88e 100644 --- a/lib/wasi/src/syscalls/wasix/proc_fork.rs +++ b/lib/wasi/src/syscalls/wasix/proc_fork.rs @@ -275,6 +275,13 @@ pub fn proc_fork( let start = ctx.data(&store).inner().thread_spawn.clone().unwrap(); start.call(&mut store, 0, 0); } + trace!( + "wasi[{}:{}]::proc_{} - child exited (code = {})", + ctx.data(&store).pid(), + ctx.data(&store).tid(), + fork_op, + ret + ); // Clean up the environment ctx.cleanup((&mut store), Some(ret as ExitCode)); diff --git a/lib/wasi/src/syscalls/wasix/sock_recv.rs b/lib/wasi/src/syscalls/wasix/sock_recv.rs index 8ac20477027..05db3734b50 100644 --- a/lib/wasi/src/syscalls/wasix/sock_recv.rs +++ b/lib/wasi/src/syscalls/wasix/sock_recv.rs @@ -102,69 +102,31 @@ fn sock_recv_internal( let mut env = ctx.data(); let memory = env.memory_view(ctx); - let iovs_arr = wasi_try_mem_ok_ok!(ri_data.slice(&memory, ri_data_len)); - let max_size = { - let mut max_size = 0usize; - for iovs in iovs_arr.iter() { - let iovs = wasi_try_mem_ok_ok!(iovs.read()); - let buf_len: usize = - wasi_try_ok_ok!(iovs.buf_len.try_into().map_err(|_| Errno::Overflow)); - max_size += buf_len; - } - max_size - }; - - let res = { - if max_size <= 10240 { - let mut buf: [MaybeUninit; 10240] = unsafe { MaybeUninit::uninit().assume_init() }; - let writer = &mut buf[..max_size]; - let amt = wasi_try_ok_ok!(__sock_asyncify( - env, - sock, - Rights::SOCK_RECV, - |socket, fd| async move { socket.recv(env.tasks().deref(), writer, fd.flags).await }, - )); + let data = wasi_try_ok_ok!(__sock_asyncify( + env, + sock, + Rights::SOCK_RECV, + |socket, fd| async move { + let iovs_arr = ri_data + .slice(&memory, ri_data_len) + .map_err(mem_error_to_wasi)?; + let iovs_arr = iovs_arr.access().map_err(mem_error_to_wasi)?; - if amt > 0 { - let buf: &[MaybeUninit] = &buf[..amt]; - let buf: &[u8] = unsafe { std::mem::transmute(buf) }; - copy_from_slice(buf, &memory, iovs_arr).map(|_| amt) - } else { - Ok(0) - } - } else { - let data = wasi_try_ok_ok!(__sock_asyncify( - env, - sock, - Rights::SOCK_RECV, - |socket, fd| async move { - let mut buf = Vec::with_capacity(max_size); - unsafe { - buf.set_len(max_size); - } - socket - .recv(env.tasks().deref(), &mut buf, fd.flags) - .await - .map(|amt| { - unsafe { - buf.set_len(amt); - } - let buf: Vec = unsafe { std::mem::transmute(buf) }; - buf - }) - }, - )); + let mut total_read = 0; + for iovs in iovs_arr.iter() { + let mut buf = WasmPtr::::new(iovs.buf) + .slice(&memory, iovs.buf_len) + .map_err(mem_error_to_wasi)? + .access() + .map_err(mem_error_to_wasi)?; - let data_len = data.len(); - if data_len > 0 { - let mut reader = &data[..]; - read_bytes(reader, &memory, iovs_arr).map(|_| data_len) - } else { - Ok(0) + total_read += socket + .recv(env.tasks().deref(), buf.as_mut_uninit(), fd.flags) + .await?; } - } - }; - - Ok(res) + Ok(total_read) + }, + )); + Ok(Ok(data)) } diff --git a/lib/wasi/src/syscalls/wasix/sock_send.rs b/lib/wasi/src/syscalls/wasix/sock_send.rs index 7dca883a360..2df64c05974 100644 --- a/lib/wasi/src/syscalls/wasix/sock_send.rs +++ b/lib/wasi/src/syscalls/wasix/sock_send.rs @@ -26,39 +26,28 @@ pub fn sock_send( ) -> Result { let env = ctx.data(); let memory = env.memory_view(&ctx); - let iovs_arr = wasi_try_mem_ok!(si_data.slice(&memory, si_data_len)); let runtime = env.runtime.clone(); - let buf_len: M::Offset = { - iovs_arr - .iter() - .filter_map(|a| a.read().ok()) - .map(|a| a.buf_len) - .sum() - }; - let buf_len: usize = wasi_try_ok!(buf_len.try_into().map_err(|_| Errno::Overflow)); - let res = { - if buf_len <= 10240 { - let mut buf: [MaybeUninit; 10240] = unsafe { MaybeUninit::uninit().assume_init() }; - let writer = &mut buf[..buf_len]; - let written = wasi_try_ok!(copy_to_slice(&memory, iovs_arr, writer)); - - let reader = &buf[..written]; - let reader: &[u8] = unsafe { std::mem::transmute(reader) }; - - __sock_asyncify(env, sock, Rights::SOCK_SEND, |socket, fd| async move { - socket.send(env.tasks().deref(), reader, fd.flags).await - }) - } else { - let mut buf = Vec::with_capacity(buf_len); - wasi_try_ok!(write_bytes(&mut buf, &memory, iovs_arr)); + __sock_asyncify(env, sock, Rights::SOCK_SEND, |socket, fd| async move { + let iovs_arr = si_data + .slice(&memory, si_data_len) + .map_err(mem_error_to_wasi)?; + let iovs_arr = iovs_arr.access().map_err(mem_error_to_wasi)?; - let reader = &buf; - __sock_asyncify(env, sock, Rights::SOCK_SEND, |socket, fd| async move { - socket.send(env.tasks().deref(), reader, fd.flags).await - }) - } + let mut sent = 0usize; + for iovs in iovs_arr.iter() { + let buf = WasmPtr::::new(iovs.buf) + .slice(&memory, iovs.buf_len) + .map_err(mem_error_to_wasi)? + .access() + .map_err(mem_error_to_wasi)?; + sent += socket + .send(env.tasks().deref(), buf.as_ref(), fd.flags) + .await?; + } + Ok(sent) + }) }; let mut ret = Errno::Success; @@ -66,11 +55,10 @@ pub fn sock_send( Ok(bytes_written) => { debug!( %bytes_written, - "wasi[{}:{}]::sock_send (fd={}, buf_len={}, flags={:?})", + "wasi[{}:{}]::sock_send (fd={}, flags={:?})", ctx.data().pid(), ctx.data().tid(), sock, - buf_len, si_flags ); bytes_written @@ -79,11 +67,10 @@ pub fn sock_send( let socket_err = err.name(); debug!( %socket_err, - "wasi[{}:{}]::sock_send (fd={}, buf_len={}, flags={:?})", + "wasi[{}:{}]::sock_send (fd={}, flags={:?})", ctx.data().pid(), ctx.data().tid(), sock, - buf_len, si_flags ); ret = err; @@ -91,9 +78,9 @@ pub fn sock_send( } }; + let memory = env.memory_view(&ctx); let bytes_written: M::Offset = wasi_try_ok!(bytes_written.try_into().map_err(|_| Errno::Overflow)); wasi_try_mem_ok!(ret_data_len.write(&memory, bytes_written)); - Ok(ret) } diff --git a/lib/wasi/src/syscalls/wasix/sock_send_to.rs b/lib/wasi/src/syscalls/wasix/sock_send_to.rs index 4c3220b8fe5..eb98eda42e8 100644 --- a/lib/wasi/src/syscalls/wasix/sock_send_to.rs +++ b/lib/wasi/src/syscalls/wasix/sock_send_to.rs @@ -34,14 +34,6 @@ pub fn sock_send_to( let memory = env.memory_view(&ctx); let iovs_arr = wasi_try_mem_ok!(si_data.slice(&memory, si_data_len)); - let buf_len: M::Offset = { - iovs_arr - .iter() - .filter_map(|a| a.read().ok()) - .map(|a| a.buf_len) - .sum() - }; - let buf_len: usize = wasi_try_ok!(buf_len.try_into().map_err(|_| Errno::Inval)); let (addr_ip, addr_port) = { let memory = env.memory_view(&ctx); wasi_try_ok!(read_ip_port(&memory, addr)) @@ -49,45 +41,36 @@ pub fn sock_send_to( let addr = SocketAddr::new(addr_ip, addr_port); let bytes_written = { - if buf_len <= 10240 { - let mut buf: [MaybeUninit; 10240] = unsafe { MaybeUninit::uninit().assume_init() }; - let writer = &mut buf[..buf_len]; - let written = wasi_try_ok!(copy_to_slice(&memory, iovs_arr, writer)); - - let reader = &buf[..written]; - let reader: &[u8] = unsafe { std::mem::transmute(reader) }; + wasi_try_ok!(__sock_asyncify( + env, + sock, + Rights::SOCK_SEND_TO, + |socket, fd| async move { + let iovs_arr = si_data + .slice(&memory, si_data_len) + .map_err(mem_error_to_wasi)?; + let iovs_arr = iovs_arr.access().map_err(mem_error_to_wasi)?; - wasi_try_ok!(__sock_asyncify( - env, - sock, - Rights::SOCK_SEND, - |socket, fd| async move { - socket - .send_to::(env.tasks().deref(), reader, addr, fd.flags) - .await - }, - )) - } else { - let mut buf = Vec::with_capacity(buf_len); - wasi_try_ok!(write_bytes(&mut buf, &memory, iovs_arr)); - - let reader = &buf; - wasi_try_ok!(__sock_asyncify( - env, - sock, - Rights::SOCK_SEND_TO, - |socket, fd| async move { - socket - .send_to::(env.tasks().deref(), reader, addr, fd.flags) - .await - }, - )) - } + let mut sent = 0usize; + for iovs in iovs_arr.iter() { + let buf = WasmPtr::::new(iovs.buf) + .slice(&memory, iovs.buf_len) + .map_err(mem_error_to_wasi)? + .access() + .map_err(mem_error_to_wasi)?; + sent += socket + .send_to::(env.tasks().deref(), buf.as_ref(), addr, fd.flags) + .await?; + } + Ok(sent) + }, + )) }; + let memory = env.memory_view(&ctx); let bytes_written: M::Offset = wasi_try_ok!(bytes_written.try_into().map_err(|_| Errno::Overflow)); - wasi_try_mem_ok!(ret_data_len.write(&memory, bytes_written as M::Offset)); + wasi_try_mem_ok!(ret_data_len.write(&memory, bytes_written)); Ok(Errno::Success) } From 59b37e4b75a5b0d5cd701858defbfbc99dbace13 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 3 Mar 2023 17:52:05 +1100 Subject: [PATCH 10/12] Fixed for the JS version --- lib/api/src/js/mem_access.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/api/src/js/mem_access.rs b/lib/api/src/js/mem_access.rs index 00c68eac2fd..3d5de97b412 100644 --- a/lib/api/src/js/mem_access.rs +++ b/lib/api/src/js/mem_access.rs @@ -1,3 +1,5 @@ +use crate::WasmSliceAccess; +use crate::access::{SliceCow, RefCow, WasmRefAccess}; use crate::js::externals::memory::MemoryBuffer; use crate::js::RuntimeError; use crate::js::{Memory32, Memory64, MemoryView, WasmPtr}; @@ -121,6 +123,12 @@ impl<'a, T: ValueType> WasmRef<'a, T> { let data = unsafe { slice::from_raw_parts(data.as_ptr() as *const _, data.len()) }; self.buffer.write(self.offset, data) } + + /// Gains direct access to the memory of this slice + #[inline] + pub fn access(self) -> Result, MemoryAccessError> { + WasmRefAccess::new(self) + } } impl<'a, T: ValueType> fmt::Debug for WasmRef<'a, T> { @@ -250,6 +258,12 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { self.index(idx).write(val) } + /// Gains direct access to the memory of this slice + #[inline] + pub fn access(self) -> Result, MemoryAccessError> { + WasmSliceAccess::new(self) + } + /// Reads the entire slice into the given buffer. /// /// The length of the buffer must match the length of the slice. @@ -414,7 +428,7 @@ where let buf = slice.read_to_vec()?; Ok(Self { slice, - buf: SliceCow::Owned(buf), + buf: SliceCow::Owned(buf, false), }) } } @@ -424,10 +438,10 @@ where T: wasmer_types::ValueType, { fn new(ptr: WasmRef<'a, T>) -> Result { - let val = slice.read()?; + let val = ptr.read()?; Ok(Self { ptr, - buf: RefCow::Owned(val), + buf: RefCow::Owned(val, false), }) } } From f9296e4e57319d6839993752ee8f29f8164b3c0d Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 3 Mar 2023 17:57:01 +1100 Subject: [PATCH 11/12] Lint fixes --- lib/api/src/js/mem_access.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/api/src/js/mem_access.rs b/lib/api/src/js/mem_access.rs index 3d5de97b412..5c7aeaa7831 100644 --- a/lib/api/src/js/mem_access.rs +++ b/lib/api/src/js/mem_access.rs @@ -1,8 +1,8 @@ -use crate::WasmSliceAccess; -use crate::access::{SliceCow, RefCow, WasmRefAccess}; +use crate::access::{RefCow, SliceCow, WasmRefAccess}; use crate::js::externals::memory::MemoryBuffer; use crate::js::RuntimeError; use crate::js::{Memory32, Memory64, MemoryView, WasmPtr}; +use crate::WasmSliceAccess; use std::{ convert::TryInto, fmt, From 6ac35fd24123b95f85255e44dc6554b73c77d0b7 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Fri, 3 Mar 2023 19:17:32 -0800 Subject: [PATCH 12/12] Fix linting --- lib/api/src/access.rs | 14 ++++++++++---- lib/api/src/sys/mem_access.rs | 3 ++- lib/wasi/src/syscalls/mod.rs | 4 ++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/api/src/access.rs b/lib/api/src/access.rs index e0d72b82c5e..da34c8ec407 100644 --- a/lib/api/src/access.rs +++ b/lib/api/src/access.rs @@ -12,8 +12,8 @@ pub(super) enum SliceCow<'a, T> { impl<'a, T> AsRef<[T]> for SliceCow<'a, T> { fn as_ref(&self) -> &[T] { match self { - Self::Borrowed(buf) => buf.as_ref(), - Self::Owned(buf, _) => buf.as_ref(), + Self::Borrowed(buf) => buf, + Self::Owned(buf, _) => buf, } } } @@ -79,6 +79,11 @@ where pub fn len(&self) -> usize { self.buf.as_ref().len() } + + /// If the slice is empty + pub fn is_empty(&self) -> bool { + self.buf.as_ref().is_empty() + } } impl<'a> WasmSliceAccess<'a, u8> { @@ -96,7 +101,7 @@ where { fn drop(&mut self) { if let SliceCow::Owned(buf, modified) = &self.buf { - if *modified == true { + if *modified { self.slice.write_slice(buf.as_ref()).ok(); } } @@ -168,6 +173,7 @@ where { /// Reads the address pointed to by this `WasmPtr` in a memory. #[inline] + #[allow(clippy::clone_on_copy)] pub fn read(&self) -> T where T: Clone, @@ -191,7 +197,7 @@ where { fn drop(&mut self) { if let RefCow::Owned(val, modified) = &self.buf { - if *modified == true { + if *modified { self.ptr.write(*val).ok(); } } diff --git a/lib/api/src/sys/mem_access.rs b/lib/api/src/sys/mem_access.rs index 246c637974f..239597eb257 100644 --- a/lib/api/src/sys/mem_access.rs +++ b/lib/api/src/sys/mem_access.rs @@ -111,7 +111,8 @@ impl<'a, T: ValueType> WasmRef<'a, T> { /// Writes to the location pointed to by this `WasmRef`. #[inline] pub fn write(self, val: T) -> Result<(), MemoryAccessError> { - Ok(self.access()?.write(val)) + self.access()?.write(val); + Ok(()) } /// Gains direct access to the memory of this slice diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 420de3eab0b..685be1653d5 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -168,7 +168,7 @@ pub(crate) fn copy_from_slice( let iovs_arr = iovs_arr.access().map_err(mem_error_to_wasi)?; for iovs in iovs_arr.iter() { let mut buf = WasmPtr::::new(iovs.buf) - .slice(&memory, iovs.buf_len) + .slice(memory, iovs.buf_len) .map_err(mem_error_to_wasi)? .access() .map_err(mem_error_to_wasi)?; @@ -197,7 +197,7 @@ pub(crate) fn read_bytes( let iovs_arr = iovs_arr.access().map_err(mem_error_to_wasi)?; for iovs in iovs_arr.iter() { let mut buf = WasmPtr::::new(iovs.buf) - .slice(&memory, iovs.buf_len) + .slice(memory, iovs.buf_len) .map_err(mem_error_to_wasi)? .access() .map_err(mem_error_to_wasi)?;