diff --git a/common/state/src/plugin/errors.rs b/common/state/src/plugin/errors.rs index 0ab57ffc4c..40154eb922 100644 --- a/common/state/src/plugin/errors.rs +++ b/common/state/src/plugin/errors.rs @@ -9,6 +9,7 @@ pub enum PluginError { NoSuchModule, Encoding(Box), PluginModuleError(String, String, PluginModuleError), + ProcessExit, } #[derive(Debug)] diff --git a/common/state/src/plugin/exports.rs b/common/state/src/plugin/exports.rs index e27faa14c4..983cdcca42 100644 --- a/common/state/src/plugin/exports.rs +++ b/common/state/src/plugin/exports.rs @@ -1,4 +1,4 @@ -use crate::plugin::wasm_env::HostFunctionEnvironment; +use crate::plugin::wasm_env::{HostFunctionEnvironment, HostFunctionException}; use wasmer::{AsStoreMut, AsStoreRef, FunctionEnvMut, Memory32, Memory64, MemorySize, WasmPtr}; // there is no WASI defined for wasm64, yet, so always use 32bit pointers type MemoryModel = wasmer::Memory32; @@ -105,6 +105,8 @@ pub(crate) fn wasi_env_get( _environ: WasmPtr, MemoryModel>, _environ_buf: WasmPtr, ) -> i32 { + // as the environment is always empty (0 bytes, 0 entries) this function will + // just unconditionally return Success. wasmer_wasix_types::wasi::Errno::Success as i32 } @@ -118,14 +120,18 @@ pub(crate) fn wasi_env_sizes_get( let memory = env.data().memory().clone(); let store = env.as_store_mut(); let mem = memory.view(&store); + const NUMBER_OF_ENVIRONMENT_ENTRIES: u32 = 0; + const NUMBER_OF_ENVIRONMENT_BYTES: u32 = 0; numptr - .write(&mem, 0) - .and_then(|()| bytesptr.write(&mem, 0)) - .map(|()| Errno::Success) - .unwrap_or(Errno::Memviolation) as i32 + .write(&mem, NUMBER_OF_ENVIRONMENT_ENTRIES) + .and_then(|()| bytesptr.write(&mem, NUMBER_OF_ENVIRONMENT_BYTES)) + .map_or(Errno::Memviolation, |()| Errno::Success) as i32 } // proc_exit(rval: exitcode) -pub(crate) fn wasi_proc_exit(env: FunctionEnvMut, _exitcode: i32) { - tracing::warn!("Plugin {} called exit().", env.data().name) +pub(crate) fn wasi_proc_exit( + _env: FunctionEnvMut, + exitcode: i32, +) -> Result<(), HostFunctionException> { + Err(HostFunctionException::ProcessExit(exitcode)) } diff --git a/common/state/src/plugin/mod.rs b/common/state/src/plugin/mod.rs index ea2bb8ace3..5bbd434e89 100644 --- a/common/state/src/plugin/mod.rs +++ b/common/state/src/plugin/mod.rs @@ -22,6 +22,7 @@ use self::{ errors::PluginError, memory_manager::EcsWorld, module::{PluginModule, PreparedEventQuery}, + wasm_env::HostFunctionException, }; use rayon::prelude::*; @@ -102,6 +103,24 @@ impl Plugin { .flat_map(|module| { module.try_execute(ecs, event).map(|x| { x.map_err(|e| { + if let errors::PluginModuleError::RunFunction(runtime_err) = &e { + if let Some(host_except) = + runtime_err.downcast_ref::() + { + match host_except { + HostFunctionException::ProcessExit(code) => { + module.exit_code = Some(*code); + tracing::warn!( + "Module {} binary {} exited with {}", + self.data.name, + module.name(), + *code + ); + return PluginError::ProcessExit; + }, + } + } + } PluginError::PluginModuleError( self.data.name.to_owned(), event.get_function_name().to_owned(), @@ -111,6 +130,13 @@ impl Plugin { }) }) .collect::, _>>() + .map_err(|e| { + if matches!(e, PluginError::ProcessExit) { + // remove the executable from the module which called process exit + self.modules.retain(|m| m.exit_code.is_none()) + } + e + }) } } diff --git a/common/state/src/plugin/module.rs b/common/state/src/plugin/module.rs index eb0fd53530..382505c2fb 100644 --- a/common/state/src/plugin/module.rs +++ b/common/state/src/plugin/module.rs @@ -27,6 +27,7 @@ pub struct PluginModule { store: Store, #[allow(dead_code)] name: String, + pub(crate) exit_code: Option, } impl PluginModule { @@ -125,6 +126,7 @@ impl PluginModule { wasm_state: Arc::new(instance), store, name, + exit_code: None, }) } @@ -151,6 +153,8 @@ impl PluginModule { }; Some(bincode::deserialize(&bytes).map_err(PluginModuleError::Encoding)) } + + pub fn name(&self) -> &str { &self.name } } /// This structure represent a Pre-encoded event object (Useful to avoid diff --git a/common/state/src/plugin/wasm_env.rs b/common/state/src/plugin/wasm_env.rs index 480784d3d0..a2ca60ad6a 100644 --- a/common/state/src/plugin/wasm_env.rs +++ b/common/state/src/plugin/wasm_env.rs @@ -26,6 +26,19 @@ pub struct HostFunctionEnvironmentInit { memory: Memory, } +#[derive(Debug, Clone, Copy)] +// Exception thrown from a native wasm callback +pub enum HostFunctionException { + ProcessExit(i32), +} + +// needed for `std::error::Error` +impl core::fmt::Display for HostFunctionException { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { write!(f, "{:?}", self) } +} + +impl std::error::Error for HostFunctionException {} + impl HostFunctionEnvironment { pub fn new(name: String, ecs: Arc) -> Self { Self {