Compare commits

...

13 Commits

Author SHA1 Message Date
Chris Dickinson
566372c736 fix: address clippy::needless_borrows_for_generic_args 2024-07-30 11:41:39 -07:00
Chris Dickinson
f87eec2214 Revert "chore: allow clippy::needless_borrows_for_generic_args in runtime"
This reverts commit 985f66472f.
2024-07-30 11:40:27 -07:00
Steve Manuel
985f66472f chore: allow clippy::needless_borrows_for_generic_args in runtime 2024-07-29 11:47:16 -06:00
corda.ilaria@gmail.com
60da76cf37 chore: ran clippy 2024-07-29 18:15:58 +02:00
corda.ilaria@gmail.com
f68ba112bc feat: add log_trace 2024-07-26 18:41:01 +02:00
Muhammad Azeez
b7fa319cb9 Add readonly dirs to allowed_paths (#733)
This is a rough POC for allowing people to whitelist a dir as readonly.
When the source path is prefixed with `ro:`, the dir is considered as
readonly. This preserved backward compatibility. This suggestion came up
in https://github.com/extism/go-sdk/pull/1#discussion_r1276700587

Readonly:
```rs
let manifest = Manifest::new([url])
        .with_allowed_path("ro:D:/x/rust/fs/data".to_string(), "/data")
        .with_config_key("path", "/data/data.txt");
```

```
trying to read file:
"Hello World at 1719851282.5109031sHello World at 1719851299.0819795sHello World at 1719851317.8934608s\n"
-----------------------------------------------------
trying to write file:
thread '<unnamed>' panicked at src\lib.rs:24:34:
called `Result::unwrap()` on an `Err` value: Os { code: 58, kind: Unsupported, message: "Not supported" }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at runtime\examples\fs.rs:27:6:
called `Result::unwrap()` on an `Err` value: error while executing at wasm backtrace:
    0: 0x234d2 - fs.wasm!__rust_start_panic
    1: 0x232a1 - fs.wasm!rust_panic
    2: 0x231da - fs.wasm!std::panicking::rust_panic_with_hook::hd3fb69bc0aea298a
    3: 0x22467 - fs.wasm!std::panicking::begin_panic_handler::{{closure}}::h4d99b90b43f79472
    4: 0x223ca - fs.wasm!std::sys_common::backtrace::__rust_end_short_backtrace::h5691573a73161cb1
    5: 0x22bca - fs.wasm!rust_begin_unwind
    6: 0x303e9 - fs.wasm!core::panicking::panic_fmt::hdb62f5cdb45533e4
    7: 0x3234d - fs.wasm!core::result::unwrap_failed::h30d23efcc9e41efc
    8: 0x36c2 - fs.wasm!fs::try_write::inner::h0b3b0df8e129f5cc
    9: 0x29cd - fs.wasm!try_write
   10: 0x35e4a - fs.wasm!try_write.command_export
note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information

Caused by:
    wasm trap: wasm `unreachable` instruction executed

Stack backtrace:
   0: std::backtrace_rs::backtrace::dbghelp64::trace
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\std\src\..\..\backtrace\src\backtrace\dbghelp64.rs:99
   1: std::backtrace_rs::backtrace::trace_unsynchronized
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\std\src\..\..\backtrace\src\backtrace\mod.rs:66
   2: std::backtrace::Backtrace::create
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\std\src\backtrace.rs:331
   3: std::backtrace::Backtrace::capture
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\std\src\backtrace.rs:296
   4: anyhow::error::impl$1::from<wasmtime_environ::trap_encoding::Trap>
             at C:\Users\muham\.cargo\registry\src\index.crates.io-6f17d22bba15001f\anyhow-1.0.86\src\error.rs:565
   5: core::convert::impl$3::into<wasmtime_environ::trap_encoding::Trap,anyhow::Error>
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6\library\core\src\convert\mod.rs:759
   6: wasmtime_environ::impl$1::into_anyhow<wasmtime_environ::trap_encoding::Trap>
             at C:\Users\muham\.cargo\registry\src\index.crates.io-6f17d22bba15001f\wasmtime-environ-22.0.0\src\lib.rs:90
   7: wasmtime::runtime::trap::from_runtime_box
             at C:\Users\muham\.cargo\registry\src\index.crates.io-6f17d22bba15001f\wasmtime-22.0.0\src\runtime\trap.rs:118
   8: wasmtime::runtime::func::invoke_wasm_and_catch_traps::closure$0<extism::current_plugin::CurrentPlugin,wasmtime::runtime::func::impl$1::call_unchecked_raw::closure_env$0<extism::current_plugin::CurrentPlugin> >
             at C:\Users\muham\.cargo\registry\src\index.crates.io-6f17d22bba15001f\wasmtime-22.0.0\src\runtime\func.rs:1597
   9: enum2$<core::result::Result<tuple$<>,alloc::boxed::Box<wasmtime::runtime::vm::traphandlers::Trap,alloc::alloc::Global> > >::map_err<tuple$<>,alloc::boxed::Box<wasmtime::runtime::vm::traphandlers::Trap,alloc::alloc::Global>,anyhow::Error,wasmtime::runtime:
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6\library\core\src\result.rs:829
  10: wasmtime::runtime::func::invoke_wasm_and_catch_traps<extism::current_plugin::CurrentPlugin,wasmtime::runtime::func::impl$1::call_unchecked_raw::closure_env$0<extism::current_plugin::CurrentPlugin> >
             at C:\Users\muham\.cargo\registry\src\index.crates.io-6f17d22bba15001f\wasmtime-22.0.0\src\runtime\func.rs:1597
  11: wasmtime::runtime::func::Func::call_unchecked_raw<extism::current_plugin::CurrentPlugin>
             at C:\Users\muham\.cargo\registry\src\index.crates.io-6f17d22bba15001f\wasmtime-22.0.0\src\runtime\func.rs:1063
  12: wasmtime::runtime::func::Func::call_unchecked<ref_mut$<wasmtime::runtime::store::context::StoreContextMut<extism::current_plugin::CurrentPlugin> > >
             at C:\Users\muham\.cargo\registry\src\index.crates.io-6f17d22bba15001f\wasmtime-22.0.0\src\runtime\func.rs:1049
  13: wasmtime::runtime::func::Func::call_impl_do_call<extism::current_plugin::CurrentPlugin>
             at C:\Users\muham\.cargo\registry\src\index.crates.io-6f17d22bba15001f\wasmtime-22.0.0\src\runtime\func.rs:1243
  14: wasmtime::runtime::func::Func::call<ref_mut$<wasmtime::runtime::store::Store<extism::current_plugin::CurrentPlugin> > >
             at C:\Users\muham\.cargo\registry\src\index.crates.io-6f17d22bba15001f\wasmtime-22.0.0\src\runtime\func.rs:1002
  15: extism::plugin::Plugin::raw_call<ref$<str$>,ref$<str$> >
             at .\src\plugin.rs:753
  16: extism::plugin::Plugin::call<ref$<str$>,ref$<str$>,ref$<str$> >
             at .\src\plugin.rs:900
  17: fs::main
             at .\examples\fs.rs:25
  18: core::ops::function::FnOnce::call_once<void (*)(),tuple$<> >
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6\library\core\src\ops\function.rs:250
  19: core::hint::black_box
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6\library\core\src\hint.rs:337
  20: std::sys_common::backtrace::__rust_begin_short_backtrace<void (*)(),tuple$<> >
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6\library\std\src\sys_common\backtrace.rs:155
  21: std::rt::lang_start::closure$0<tuple$<> >
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6\library\std\src\rt.rs:166
  22: std::rt::lang_start_internal
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\std\src\rt.rs:148
  23: std::rt::lang_start<tuple$<> >
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6\library\std\src\rt.rs:165
  24: main
  25: invoke_main
             at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
  26: __scrt_common_main_seh
             at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
  27: BaseThreadInitThunk
  28: RtlUserThreadStart
stack backtrace:
   0: std::panicking::begin_panic_handler
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\std\src\panicking.rs:645
   1: core::panicking::panic_fmt
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\core\src\panicking.rs:72
   2: core::result::unwrap_failed
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\core\src\result.rs:1654
   3: enum2$<core::result::Result<ref$<str$>,anyhow::Error> >::unwrap
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6\library\core\src\result.rs:1077
   4: fs::main
             at .\examples\fs.rs:25
   5: core::ops::function::FnOnce::call_once<void (*)(),tuple$<> >
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6\library\core\src\ops\function.rs:250
   6: core::hint::black_box
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6\library\core\src\hint.rs:337
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
error: process didn't exit successfully: `D:\dylibso\extism\target\debug\examples\fs.exe` (exit code: 101)
```

Writable:
```rs
    let manifest = Manifest::new([url])
        .with_allowed_path("D:/x/rust/fs/data".to_string(), "/data")
        .with_config_key("path", "/data/data.txt");

```

```
trying to read file:
"Hello World at 1719851282.5109031sHello World at 1719851299.0819795sHello World at 1719851317.8934608s\n"
-----------------------------------------------------
trying to write file:
"Hello World at 1719851282.5109031sHello World at 1719851299.0819795sHello World at 1719851317.8934608s\nHello World at 1719851500.7803263s\n"
done!
```
2024-07-25 19:40:38 +03:00
zach
d04e2e42bf v1.5.0 2024-07-23 11:07:02 -07:00
zach
6d2735cec7 fix: require error messages to be null terminated in C SDK (#745)
Related to https://github.com/extism/python-sdk/issues/23 - there is
currently no way to get the length of the error message, so we need to
make sure it is a valid C string.
2024-07-23 09:35:59 -07:00
zach
b1d0f335b3 doc: fix usage of host_fn macro in doc example (#742) 2024-07-16 16:40:27 -07:00
Steve Manuel
3a7768ffd5 chore: update readme with crate version (#738) 2024-07-11 07:58:06 -05:00
zach
ee8c41ab26 doc: more information about error_set (#737) 2024-07-10 12:43:37 -07:00
zach
8312e98463 test: add benchmark for creating a plugin with the cache disabled (#736) 2024-07-10 11:18:08 -07:00
zach
17a546b2db chore: support for wasmtime 22 (#731) 2024-06-21 18:17:28 -07:00
18 changed files with 320 additions and 128 deletions

View File

@@ -81,10 +81,10 @@ mod tests {
c: true,
};
let raw = Raw(&x).to_bytes().unwrap();
let y = Raw::from_bytes(&raw).unwrap();
let y = Raw::from_bytes(raw).unwrap();
assert_eq!(&x, y.0);
let y: Result<Raw<[u8; std::mem::size_of::<TestRaw>()]>, Error> = Raw::from_bytes(&raw);
let y: Result<Raw<[u8; std::mem::size_of::<TestRaw>()]>, Error> = Raw::from_bytes(raw);
assert!(y.is_ok());
}
}

View File

@@ -546,7 +546,8 @@ pub unsafe fn reset() {
MemoryRoot::new().reset()
}
/// Set the error message offset
/// Set the error message offset, the handle passed to this
/// function should not be freed after this call
#[no_mangle]
pub unsafe fn error_set(h: Handle) {
let root = MemoryRoot::new();

View File

@@ -279,7 +279,7 @@ pub struct Manifest {
/// the path on disk to the path it should be available inside the plugin.
/// For example, `".": "/tmp"` would mount the current directory as `/tmp` inside the module
#[serde(default)]
pub allowed_paths: Option<BTreeMap<PathBuf, PathBuf>>,
pub allowed_paths: Option<BTreeMap<String, PathBuf>>,
/// The plugin timeout in milliseconds
#[serde(default)]
@@ -337,8 +337,7 @@ impl Manifest {
}
/// Add a path to `allowed_paths`
pub fn with_allowed_path(mut self, src: impl AsRef<Path>, dest: impl AsRef<Path>) -> Self {
let src = src.as_ref().to_path_buf();
pub fn with_allowed_path(mut self, src: String, dest: impl AsRef<Path>) -> Self {
let dest = dest.as_ref().to_path_buf();
match &mut self.allowed_paths {
Some(p) => {
@@ -355,7 +354,7 @@ impl Manifest {
}
/// Set `allowed_paths`
pub fn with_allowed_paths(mut self, paths: impl Iterator<Item = (PathBuf, PathBuf)>) -> Self {
pub fn with_allowed_paths(mut self, paths: impl Iterator<Item = (String, PathBuf)>) -> Self {
self.allowed_paths = Some(paths.collect());
self
}

View File

@@ -9,8 +9,9 @@ repository.workspace = true
version.workspace = true
[dependencies]
wasmtime = ">= 20.0.0, < 22.0.0"
wasi-common = ">= 20.0.0, < 22.0.0"
wasmtime = ">= 20.0.0, < 23.0.0"
wasi-common = ">= 20.0.0, < 23.0.0"
wiggle = ">= 20.0.0, < 23.0.0"
anyhow = "1"
serde = {version = "1", features = ["derive"]}
serde_json = "1"

View File

@@ -12,7 +12,7 @@ To use the `extism` crate, you can add it to your Cargo file:
```toml
[dependencies]
extism = "1.2.0"
extism = "1.4.1"
```
## Environment variables

View File

@@ -35,6 +35,21 @@ pub fn create_plugin(c: &mut Criterion) {
});
}
pub fn create_plugin_no_cache(c: &mut Criterion) {
let mut g = c.benchmark_group("create");
g.noise_threshold(1.0);
g.significance_level(0.2);
g.bench_function("create_plugin_no_cache", |b| {
b.iter(|| {
let _plugin = PluginBuilder::new(COUNT_VOWELS)
.with_cache_disabled()
.with_wasi(true)
.build()
.unwrap();
})
});
}
#[derive(Debug, serde::Deserialize, PartialEq)]
struct Count {
count: u32,
@@ -251,6 +266,7 @@ criterion_group!(
reflect_linked,
basic,
create_plugin,
create_plugin_no_cache,
count_vowels
);
criterion_main!(benches);

33
runtime/examples/fs.rs Normal file
View File

@@ -0,0 +1,33 @@
use extism::*;
fn main() {
let url = Wasm::file("../wasm/read_write.wasm");
let manifest = Manifest::new([url])
.with_allowed_path("ro:src/tests/data".to_string(), "/data")
.with_config_key("path", "/data/data.txt");
let mut plugin = PluginBuilder::new(manifest)
.with_wasi(true)
.build()
.unwrap();
println!("trying to read file: ");
let res = plugin.call::<&str, &str>("try_read", "").unwrap();
println!("{:?}", res);
println!("-----------------------------------------------------");
println!("trying to write file: ");
let line = format!(
"Hello World at {:?}\n",
std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.unwrap()
);
let res2 = plugin.call::<&str, &str>("try_write", &line).unwrap();
println!("{:?}", res2);
println!("done!");
}

View File

@@ -160,10 +160,10 @@ impl CurrentPlugin {
}
pub fn memory_bytes_mut(&mut self, handle: MemoryHandle) -> Result<&mut [u8], Error> {
let (linker, mut store) = self.linker_and_store();
if let Some(mem) = linker.get(&mut store, EXTISM_ENV_MODULE, "memory") {
let (linker, store) = self.linker_and_store();
if let Some(mem) = linker.get(&mut *store, EXTISM_ENV_MODULE, "memory") {
let mem = mem.into_memory().unwrap();
let ptr = unsafe { mem.data_ptr(&store).add(handle.offset() as usize) };
let ptr = unsafe { mem.data_ptr(&*store).add(handle.offset() as usize) };
if ptr.is_null() {
return Ok(&mut []);
}
@@ -174,10 +174,10 @@ impl CurrentPlugin {
}
pub fn memory_bytes(&mut self, handle: MemoryHandle) -> Result<&[u8], Error> {
let (linker, mut store) = self.linker_and_store();
if let Some(mem) = linker.get(&mut store, EXTISM_ENV_MODULE, "memory") {
let (linker, store) = self.linker_and_store();
if let Some(mem) = linker.get(&mut *store, EXTISM_ENV_MODULE, "memory") {
let mem = mem.into_memory().unwrap();
let ptr = unsafe { mem.data_ptr(&store).add(handle.offset() as usize) };
let ptr = unsafe { mem.data_ptr(&*store).add(handle.offset() as usize) };
if ptr.is_null() {
return Ok(&[]);
}
@@ -188,17 +188,17 @@ impl CurrentPlugin {
}
pub fn host_context<T: Clone + 'static>(&mut self) -> Result<T, Error> {
let (linker, mut store) = self.linker_and_store();
let Some(Extern::Global(xs)) = linker.get(&mut store, EXTISM_ENV_MODULE, "extism_context")
let (linker, store) = self.linker_and_store();
let Some(Extern::Global(xs)) = linker.get(&mut *store, EXTISM_ENV_MODULE, "extism_context")
else {
anyhow::bail!("unable to locate an extism kernel global: extism_context",)
};
let Val::ExternRef(Some(xs)) = xs.get(&mut store) else {
let Val::ExternRef(Some(xs)) = xs.get(&mut *store) else {
anyhow::bail!("expected extism_context to be an externref value",)
};
match xs.data(&mut store)?.downcast_ref::<T>().cloned() {
match xs.data(&mut *store)?.downcast_ref::<T>().cloned() {
Some(xs) => Ok(xs.clone()),
None => anyhow::bail!("could not downcast extism_context",),
}
@@ -216,7 +216,7 @@ impl CurrentPlugin {
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "alloc") {
f.into_func()
.unwrap()
.call(&mut store, &[Val::I64(n as i64)], output)?;
.call(&mut *store, &[Val::I64(n as i64)], output)?;
} else {
anyhow::bail!("{} unable to allocate memory", self.id);
}
@@ -238,11 +238,11 @@ impl CurrentPlugin {
/// Free a block of Extism plugin memory
pub fn memory_free(&mut self, handle: MemoryHandle) -> Result<(), Error> {
let (linker, mut store) = self.linker_and_store();
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "free") {
let (linker, store) = self.linker_and_store();
if let Some(f) = linker.get(&mut *store, EXTISM_ENV_MODULE, "free") {
f.into_func()
.unwrap()
.call(&mut store, &[Val::I64(handle.offset as i64)], &mut [])?;
.call(&mut *store, &[Val::I64(handle.offset as i64)], &mut [])?;
} else {
anyhow::bail!("unable to locate an extism kernel function: free",)
}
@@ -250,12 +250,12 @@ impl CurrentPlugin {
}
pub fn memory_length(&mut self, offs: u64) -> Result<u64, Error> {
let (linker, mut store) = self.linker_and_store();
let (linker, store) = self.linker_and_store();
let output = &mut [Val::I64(0)];
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "length") {
if let Some(f) = linker.get(&mut *store, EXTISM_ENV_MODULE, "length") {
f.into_func()
.unwrap()
.call(&mut store, &[Val::I64(offs as i64)], output)?;
.call(&mut *store, &[Val::I64(offs as i64)], output)?;
} else {
anyhow::bail!("unable to locate an extism kernel function: length",)
}
@@ -270,12 +270,12 @@ impl CurrentPlugin {
}
pub fn memory_length_unsafe(&mut self, offs: u64) -> Result<u64, Error> {
let (linker, mut store) = self.linker_and_store();
let (linker, store) = self.linker_and_store();
let output = &mut [Val::I64(0)];
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "length_unsafe") {
if let Some(f) = linker.get(&mut *store, EXTISM_ENV_MODULE, "length_unsafe") {
f.into_func()
.unwrap()
.call(&mut store, &[Val::I64(offs as i64)], output)?;
.call(&mut *store, &[Val::I64(offs as i64)], output)?;
} else {
anyhow::bail!("unable to locate an extism kernel function: length_unsafe",)
}
@@ -320,9 +320,20 @@ impl CurrentPlugin {
if let Some(a) = &manifest.allowed_paths {
for (k, v) in a.iter() {
let file = Box::new(wasi_common::sync::dir::Dir::from_cap_std(
wasi_common::sync::Dir::open_ambient_dir(k, auth)?,
));
let readonly = k.starts_with("ro:");
let dir_path = if readonly { &k[3..] } else { k };
let dir = wasi_common::sync::dir::Dir::from_cap_std(
wasi_common::sync::Dir::open_ambient_dir(dir_path, auth)?,
);
let file: Box<dyn wasi_common::dir::WasiDir> = if readonly {
Box::new(readonly_dir::ReadOnlyDir::new(dir))
} else {
Box::new(dir)
};
ctx.push_preopened_dir(file, v)?;
}
}
@@ -403,12 +414,12 @@ impl CurrentPlugin {
/// Clear the current plugin error
pub fn clear_error(&mut self) {
trace!(plugin = self.id.to_string(), "CurrentPlugin::clear_error");
let (linker, mut store) = self.linker_and_store();
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "error_set") {
let (linker, store) = self.linker_and_store();
if let Some(f) = linker.get(&mut *store, EXTISM_ENV_MODULE, "error_set") {
let res = f
.into_func()
.unwrap()
.call(&mut store, &[Val::I64(0)], &mut []);
.call(&mut *store, &[Val::I64(0)], &mut []);
if let Err(e) = res {
error!(
plugin = self.id.to_string(),
@@ -440,10 +451,10 @@ impl CurrentPlugin {
let s = s.as_ref();
debug!(plugin = self.id.to_string(), "set error: {:?}", s);
let handle = self.current_plugin_mut().memory_new(s)?;
let (linker, mut store) = self.linker_and_store();
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "error_set") {
let (linker, store) = self.linker_and_store();
if let Some(f) = linker.get(&mut *store, EXTISM_ENV_MODULE, "error_set") {
f.into_func().unwrap().call(
&mut store,
&mut *store,
&[Val::I64(handle.offset() as i64)],
&mut [],
)?;
@@ -454,10 +465,10 @@ impl CurrentPlugin {
}
pub(crate) fn get_error_position(&mut self) -> (u64, u64) {
let (linker, mut store) = self.linker_and_store();
let (linker, store) = self.linker_and_store();
let output = &mut [Val::I64(0)];
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "error_get") {
if let Err(e) = f.into_func().unwrap().call(&mut store, &[], output) {
if let Some(f) = linker.get(&mut *store, EXTISM_ENV_MODULE, "error_get") {
if let Err(e) = f.into_func().unwrap().call(&mut *store, &[], output) {
error!(
plugin = self.id.to_string(),
"unable to call extism:host/env::error_get: {:?}", e

View File

@@ -279,7 +279,7 @@ impl Function {
/// For example, the following defines a host function named `add_newline` that takes a
/// string parameter and returns a string result:
/// ```rust
/// extism::host_fn!(add_newline(_user_data: (), a: String) -> String { Ok(a + "\n") });
/// extism::host_fn!(add_newline(_user_data: (); a: String) -> String { Ok(a + "\n") });
/// ```
/// A few things worth noting:
/// - The function always returns a `Result` that wraps the specified return type

View File

@@ -18,6 +18,7 @@ pub(crate) mod manifest;
pub(crate) mod pdk;
mod plugin;
mod plugin_builder;
mod readonly_dir;
mod timer;
/// Extism C API

View File

@@ -377,3 +377,14 @@ pub(crate) fn log_error(
) -> Result<(), Error> {
log(tracing::Level::ERROR, caller, input, _output)
}
/// Write to logs (trace)
/// Params: i64 (offset)
/// Returns: none
pub(crate) fn log_trace(
caller: Caller<CurrentPlugin>,
input: &[Val],
_output: &mut [Val],
) -> Result<(), Error> {
log(tracing::Level::TRACE, caller, input, _output)
}

View File

@@ -81,6 +81,8 @@ pub struct Plugin {
pub(crate) store_needs_reset: bool,
pub(crate) debug_options: DebugOptions,
pub(crate) error_msg: Option<Vec<u8>>,
}
unsafe impl Send for Plugin {}
@@ -242,6 +244,7 @@ fn relink(
log_info(I64);
log_debug(I64);
log_error(I64);
log_trace(I64);
);
let mut linked = BTreeSet::new();
@@ -361,6 +364,7 @@ impl Plugin {
store_needs_reset: false,
debug_options,
_functions: imports,
error_msg: None,
};
plugin.current_plugin_mut().store = &mut plugin.store;
@@ -608,17 +612,17 @@ impl Plugin {
// Initialize the guest runtime
pub(crate) fn initialize_guest_runtime(&mut self) -> Result<(), Error> {
let mut store = &mut self.store;
let store = &mut self.store;
if let Some(runtime) = &self.runtime {
trace!(plugin = self.id.to_string(), "Plugin::initialize_runtime");
match runtime {
GuestRuntime::Haskell { init, reactor_init } => {
if let Some(reactor_init) = reactor_init {
reactor_init.call(&mut store, &[], &mut [])?;
reactor_init.call(&mut *store, &[], &mut [])?;
}
let mut results = vec![Val::I32(0); init.ty(&store).results().len()];
let mut results = vec![Val::I32(0); init.ty(&*store).results().len()];
init.call(
&mut store,
&mut *store,
&[Val::I32(0), Val::I32(0)],
results.as_mut_slice(),
)?;
@@ -628,7 +632,7 @@ impl Plugin {
);
}
GuestRuntime::Wasi { init } => {
init.call(&mut store, &[], &mut [])?;
init.call(&mut *store, &[], &mut [])?;
debug!(plugin = self.id.to_string(), "initialied WASI runtime");
}
}
@@ -641,20 +645,20 @@ impl Plugin {
fn output_memory_position(&mut self) -> Result<(u64, u64), Error> {
let out = &mut [Val::I64(0)];
let out_len = &mut [Val::I64(0)];
let mut store = &mut self.store;
let store = &mut self.store;
if let Some(f) = self
.linker
.get(&mut store, EXTISM_ENV_MODULE, "output_offset")
.get(&mut *store, EXTISM_ENV_MODULE, "output_offset")
{
f.into_func().unwrap().call(&mut store, &[], out)?;
f.into_func().unwrap().call(&mut *store, &[], out)?;
} else {
anyhow::bail!("unable to set output")
}
if let Some(f) = self
.linker
.get(&mut store, EXTISM_ENV_MODULE, "output_length")
.get(&mut *store, EXTISM_ENV_MODULE, "output_length")
{
f.into_func().unwrap().call(&mut store, &[], out_len)?;
f.into_func().unwrap().call(&mut *store, &[], out_len)?;
} else {
anyhow::bail!("unable to set output length")
}
@@ -953,11 +957,11 @@ impl Plugin {
pub(crate) fn clear_error(&mut self) -> Result<(), Error> {
trace!(plugin = self.id.to_string(), "clearing error");
let (linker, mut store) = self.linker_and_store();
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "error_set") {
let (linker, store) = self.linker_and_store();
if let Some(f) = linker.get(&mut *store, EXTISM_ENV_MODULE, "error_set") {
f.into_func()
.unwrap()
.call(&mut store, &[Val::I64(0)], &mut [])?;
.call(store, &[Val::I64(0)], &mut [])?;
Ok(())
} else {
anyhow::bail!("Plugin::clear_error failed, extism:host/env::error_set not found")

109
runtime/src/readonly_dir.rs Normal file
View File

@@ -0,0 +1,109 @@
use crate::*;
use wasi_common::{Error, ErrorExt};
pub struct ReadOnlyDir<D: wasi_common::WasiDir> {
inner: std::sync::Arc<D>,
}
impl<D: wasi_common::WasiDir> ReadOnlyDir<D> {
pub fn new(inner: D) -> Self {
ReadOnlyDir {
inner: std::sync::Arc::new(inner),
}
}
}
#[wiggle::async_trait]
impl<D: wasi_common::WasiDir> wasi_common::WasiDir for ReadOnlyDir<D> {
fn as_any(&self) -> &dyn std::any::Any {
self.inner.as_any()
}
async fn open_file(
&self,
symlink_follow: bool,
path: &str,
oflags: wasi_common::file::OFlags,
read: bool,
write: bool,
fdflags: wasi_common::file::FdFlags,
) -> Result<wasi_common::dir::OpenResult, Error> {
if write {
return Err(Error::not_supported());
}
self.inner
.open_file(symlink_follow, path, oflags, read, false, fdflags)
.await
}
async fn create_dir(&self, _path: &str) -> Result<(), Error> {
Err(Error::not_supported())
}
async fn readdir(
&self,
cursor: wasi_common::dir::ReaddirCursor,
) -> Result<
Box<dyn Iterator<Item = Result<wasi_common::dir::ReaddirEntity, Error>> + Send>,
Error,
> {
self.inner.readdir(cursor).await
}
async fn symlink(&self, _old_path: &str, _new_path: &str) -> Result<(), Error> {
Err(Error::not_supported())
}
async fn remove_dir(&self, _path: &str) -> Result<(), Error> {
Err(Error::not_supported())
}
async fn unlink_file(&self, _path: &str) -> Result<(), Error> {
Err(Error::not_supported())
}
async fn read_link(&self, path: &str) -> Result<std::path::PathBuf, Error> {
self.inner.read_link(path).await
}
async fn get_filestat(&self) -> Result<wasi_common::file::Filestat, Error> {
self.inner.get_filestat().await
}
async fn get_path_filestat(
&self,
path: &str,
follow_symlinks: bool,
) -> Result<wasi_common::file::Filestat, Error> {
self.inner.get_path_filestat(path, follow_symlinks).await
}
async fn rename(
&self,
_path: &str,
_dest_dir: &dyn wasi_common::WasiDir,
_dest_path: &str,
) -> Result<(), Error> {
Err(wasi_common::Error::not_supported())
}
async fn hard_link(
&self,
_path: &str,
_target_dir: &dyn wasi_common::WasiDir,
_target_path: &str,
) -> Result<(), Error> {
Err(wasi_common::Error::not_supported())
}
async fn set_times(
&self,
_path: &str,
_atime: std::option::Option<wasi_common::SystemTimeSpec>,
_mtime: std::option::Option<wasi_common::SystemTimeSpec>,
_follow_symlinks: bool,
) -> Result<(), Error> {
Err(wasi_common::Error::not_supported())
}
}

View File

@@ -505,6 +505,8 @@ pub unsafe extern "C" fn extism_plugin_call_with_host_context(
let lock = plugin.instance.clone();
let mut lock = lock.lock().unwrap();
plugin.error_msg = None;
// Get function name
let name = std::ffi::CStr::from_ptr(func_name);
let name = match name.to_str() {
@@ -551,10 +553,19 @@ pub unsafe extern "C" fn extism_plugin_error(plugin: *mut Plugin) -> *const c_ch
return std::ptr::null();
}
plugin
let offs = plugin.output.error_offset;
let ptr = plugin.current_plugin_mut().memory_ptr().add(offs as usize) as *const _;
let len = plugin
.current_plugin_mut()
.memory_ptr()
.add(plugin.output.error_offset as usize) as *const _
.memory_length(offs)
.unwrap_or_default();
let mut data = std::slice::from_raw_parts(ptr, len as usize).to_vec();
data.push(0);
plugin.error_msg = Some(data);
plugin.error_msg.as_ref().unwrap().as_ptr() as *const _
}
/// Get the length of a plugin's output data

View File

@@ -0,0 +1 @@
hello world!

View File

@@ -3,36 +3,32 @@ use quickcheck::*;
const KERNEL: &[u8] = include_bytes!("../extism-runtime.wasm");
fn extism_alloc<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance, n: u64) -> u64 {
fn extism_alloc<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, n: u64) -> u64 {
let out_alloc = &mut [Val::I64(0)];
instance
.get_func(&mut store, "alloc")
.get_func(&mut *store, "alloc")
.unwrap()
.call(&mut store, &[Val::I64(n as i64)], out_alloc)
.call(store, &[Val::I64(n as i64)], out_alloc)
.unwrap();
out_alloc[0].unwrap_i64() as u64
}
fn extism_length<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) -> u64 {
fn extism_length<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) -> u64 {
let out = &mut [Val::I64(0)];
instance
.get_func(&mut store, "length")
.get_func(&mut *store, "length")
.unwrap()
.call(&mut store, &[Val::I64(p as i64)], out)
.call(store, &[Val::I64(p as i64)], out)
.unwrap();
out[0].unwrap_i64() as u64
}
fn extism_length_unsafe<T>(
mut store: &mut wasmtime::Store<T>,
instance: &mut Instance,
p: u64,
) -> u64 {
fn extism_length_unsafe<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) -> u64 {
let out = &mut [Val::I64(0)];
instance
.get_func(&mut store, "length_unsafe")
.get_func(&mut *store, "length_unsafe")
.unwrap()
.call(&mut store, &[Val::I64(p as i64)], out)
.call(store, &[Val::I64(p as i64)], out)
.unwrap();
out[0].unwrap_i64() as u64
}
@@ -47,122 +43,96 @@ fn extism_load_u8<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance
out[0].unwrap_i32() as u8
}
fn extism_load_u64<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) -> u64 {
fn extism_load_u64<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) -> u64 {
let out = &mut [Val::I32(0)];
instance
.get_func(&mut store, "load_u64")
.get_func(&mut *store, "load_u64")
.unwrap()
.call(&mut store, &[Val::I64(p as i64)], out)
.call(store, &[Val::I64(p as i64)], out)
.unwrap();
out[0].unwrap_i64() as u64
}
fn extism_input_load_u8<T>(
mut store: &mut wasmtime::Store<T>,
instance: &mut Instance,
p: u64,
) -> u8 {
fn extism_input_load_u8<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) -> u8 {
let out = &mut [Val::I32(0)];
instance
.get_func(&mut store, "input_load_u8")
.get_func(&mut *store, "input_load_u8")
.unwrap()
.call(&mut store, &[Val::I64(p as i64)], out)
.call(store, &[Val::I64(p as i64)], out)
.unwrap();
out[0].unwrap_i32() as u8
}
fn extism_input_load_u64<T>(
mut store: &mut wasmtime::Store<T>,
store: &mut wasmtime::Store<T>,
instance: &mut Instance,
p: u64,
) -> u64 {
let out = &mut [Val::I32(0)];
instance
.get_func(&mut store, "input_load_u64")
.get_func(&mut *store, "input_load_u64")
.unwrap()
.call(&mut store, &[Val::I64(p as i64)], out)
.call(store, &[Val::I64(p as i64)], out)
.unwrap();
out[0].unwrap_i64() as u64
}
fn extism_store_u8<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64, x: u8) {
fn extism_store_u8<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64, x: u8) {
instance
.get_func(&mut store, "store_u8")
.get_func(&mut *store, "store_u8")
.unwrap()
.call(
&mut store,
&[Val::I64(p as i64), Val::I32(x as i32)],
&mut [],
)
.call(store, &[Val::I64(p as i64), Val::I32(x as i32)], &mut [])
.unwrap();
}
fn extism_store_u64<T>(
mut store: &mut wasmtime::Store<T>,
instance: &mut Instance,
p: u64,
x: u64,
) {
fn extism_store_u64<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64, x: u64) {
instance
.get_func(&mut store, "store_u64")
.get_func(&mut *store, "store_u64")
.unwrap()
.call(
&mut store,
&[Val::I64(p as i64), Val::I64(x as i64)],
&mut [],
)
.call(store, &[Val::I64(p as i64), Val::I64(x as i64)], &mut [])
.unwrap();
}
fn extism_free<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) {
fn extism_free<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) {
instance
.get_func(&mut store, "free")
.get_func(&mut *store, "free")
.unwrap()
.call(&mut store, &[Val::I64(p as i64)], &mut [])
.call(store, &[Val::I64(p as i64)], &mut [])
.unwrap();
}
fn extism_error_set<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) {
fn extism_error_set<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) {
instance
.get_func(&mut store, "error_set")
.get_func(&mut *store, "error_set")
.unwrap()
.call(&mut store, &[Val::I64(p as i64)], &mut [])
.call(store, &[Val::I64(p as i64)], &mut [])
.unwrap();
}
fn extism_error_get<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance) -> u64 {
fn extism_error_get<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance) -> u64 {
let out = &mut [Val::I64(0)];
instance
.get_func(&mut store, "error_get")
.get_func(&mut *store, "error_get")
.unwrap()
.call(&mut store, &[], out)
.call(store, &[], out)
.unwrap();
out[0].unwrap_i64() as u64
}
fn extism_reset<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance) {
fn extism_reset<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance) {
instance
.get_func(&mut store, "reset")
.get_func(&mut *store, "reset")
.unwrap()
.call(&mut store, &[], &mut [])
.call(store, &[], &mut [])
.unwrap();
}
fn extism_input_set<T>(
mut store: &mut wasmtime::Store<T>,
instance: &mut Instance,
p: u64,
l: u64,
) {
fn extism_input_set<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64, l: u64) {
instance
.get_func(&mut store, "input_set")
.get_func(&mut *store, "input_set")
.unwrap()
.call(
&mut store,
&[Val::I64(p as i64), Val::I64(l as i64)],
&mut [],
)
.call(store, &[Val::I64(p as i64), Val::I64(l as i64)], &mut [])
.unwrap();
}

View File

@@ -9,6 +9,7 @@ const WASM_LOOP: &[u8] = include_bytes!("../../../wasm/loop.wasm");
const WASM_GLOBALS: &[u8] = include_bytes!("../../../wasm/globals.wasm");
const WASM_REFLECT: &[u8] = include_bytes!("../../../wasm/reflect.wasm");
const WASM_HTTP: &[u8] = include_bytes!("../../../wasm/http.wasm");
const WASM_FS: &[u8] = include_bytes!("../../../wasm/read_write.wasm");
host_fn!(pub hello_world (a: String) -> String { Ok(a) });
@@ -750,3 +751,26 @@ fn test_linking() {
assert_eq!(plugin.call::<&str, i64>("run", "Hello, world!").unwrap(), 1);
}
}
#[test]
fn test_readonly_dirs() {
let wasm = Wasm::data(WASM_FS);
let manifest = Manifest::new([wasm])
.with_allowed_path("ro:src/tests/data".to_string(), "/data")
.with_config_key("path", "/data/data.txt");
let mut plugin = PluginBuilder::new(manifest)
.with_wasi(true)
.build()
.unwrap();
let res = plugin.call::<&str, &str>("try_read", "").unwrap();
assert_eq!(res, "hello world!");
let line = "hello world 2";
let res2 = plugin.call::<&str, &str>("try_write", line);
assert!(
res2.is_err(),
"Expected try_write to fail, but it succeeded."
);
}

BIN
wasm/read_write.wasm Normal file

Binary file not shown.