Compare commits

..

15 Commits

Author SHA1 Message Date
zach
24622cd511 wip 2024-06-14 18:52:50 -07:00
zach
00f9155b9e feat: switch to owi conc, avoid needing wasm2wat 2024-06-14 18:46:27 -07:00
zach
7c770e2f1c feat(kernel): add proof for error handling 2024-06-14 18:46:27 -07:00
zach
33ad239c50 wip: reuse proof 2024-06-14 18:46:27 -07:00
zach
a62e37c21b cleanup: reduce the size of the allocation used in load_store example 2024-06-14 18:46:27 -07:00
zach
20ee6620c6 cleanup: improve verification 2024-06-14 18:46:27 -07:00
zach
db0b04696c cleanup: move proofs to examples directory 2024-06-14 18:46:27 -07:00
zach
6cc22bf2de cleanup: use new names from owi 2024-06-14 18:46:11 -07:00
zach
52d3c4ffd6 cleanup: add some comments to verification tests 2024-06-14 18:46:11 -07:00
zach
b96b28231f cleanup: ignore verification artifacts 2024-06-14 18:46:11 -07:00
zach
c1180d576c checkpoint: use owi crate from git 2024-06-14 18:46:11 -07:00
zach
48af68facc cleanup: improve owi verification 2024-06-14 18:46:11 -07:00
zach
38e6476797 wip 2024-06-14 18:46:09 -07:00
zach
6a18512fc0 v1.4.1 2024-06-14 10:12:55 -07:00
Gavin Hayes
8a95a18920 docs: add CPAN link for perl-sdk (#729) 2024-06-14 12:32:51 -04:00
16 changed files with 152 additions and 142 deletions

View File

@@ -51,7 +51,7 @@ started:
| Java SDK | <img alt="Java SDK" src="https://extism.org/img/sdk-languages/java-android.svg" width="50px"/> | https://github.com/extism/java-sdk | [Sonatype](https://central.sonatype.com/artifact/org.extism.sdk/extism) |
| .NET SDK | <img alt=".NET SDK" src="https://extism.org/img/sdk-languages/dotnet.svg" width="50px"/> | https://github.com/extism/dotnet-sdk <br/>(supports C# & F#!) | [Nuget](https://www.nuget.org/packages/Extism.Sdk) |
| OCaml SDK | <img alt="OCaml SDK" src="https://extism.org/img/sdk-languages/ocaml.svg" width="50px"/> | https://github.com/extism/ocaml-sdk | [opam](https://opam.ocaml.org/packages/extism/) |
| Perl SDK | <img alt="Perl SDK" src="https://extism.org/img/sdk-languages/perl.svg" width="50px"/> | https://github.com/extism/perl-sdk | N/A |
| Perl SDK | <img alt="Perl SDK" src="https://extism.org/img/sdk-languages/perl.svg" width="50px"/> | https://github.com/extism/perl-sdk | [CPAN](https://metacpan.org/pod/Extism) |
| PHP SDK | <img alt="PHP SDK" src="https://extism.org/img/sdk-languages/php.svg" width="50px"/> | https://github.com/extism/php-sdk | [Packagist](https://packagist.org/packages/extism/extism) |
| Python SDK | <img alt="Python SDK" src="https://extism.org/img/sdk-languages/python.svg" width="50px"/> | https://github.com/extism/python-sdk | [PyPi](https://pypi.org/project/extism/) |
| Ruby SDK | <img alt="Ruby SDK" src="https://extism.org/img/sdk-languages/ruby.svg" width="50px"/> | https://github.com/extism/ruby-sdk | [RubyGems](https://rubygems.org/gems/extism) |

3
kernel/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
owi-out
*.wat
proofs

View File

@@ -7,6 +7,7 @@ edition = "2021"
[dev-dependencies]
wasm-bindgen-test = "0.3.39"
owi = {git = "https://github.com/dylibso/owi-rs"}
[features]
default = ["bounds-checking"]
@@ -15,4 +16,4 @@ bounds-checking = []
[workspace]
members = [
"."
]
]

View File

@@ -1,5 +1,7 @@
#!/usr/bin/env bash
set -e
export CARGO_FLAGS=""
while getopts d flag

View File

@@ -0,0 +1,21 @@
#![no_main]
#![no_std]
use extism_runtime_kernel::*;
use owi::*;
main!({
let n = alloc(1024);
let x = u64();
assume(x > 0);
let m = alloc(x);
// 1. Length should equal `x` while active
assert(length(m) == x);
// 2. Length should equal `0` after free
free(m); // Free the block
assert(length(m) == 0);
free(n);
});

24
kernel/examples/error.rs Normal file
View File

@@ -0,0 +1,24 @@
#![no_main]
#![no_std]
use extism_runtime_kernel::*;
use owi::*;
main!({
let x = u64();
let n = u64();
let m = u64();
assume(x > 0);
assume(n > 0);
assume(m > 0);
alloc(n);
alloc(m);
let n = alloc(x);
assert(error_get() == 0);
error_set(n);
assert(error_get() == n);
alloc(m);
error_set(0);
assert(error_get() == 0);
});

View File

@@ -0,0 +1,17 @@
#![no_main]
#![no_std]
use extism_runtime_kernel::*;
use owi::*;
main!({
let x = u64();
assume(x > 0);
let m = alloc(x);
assert(length(m) == x);
for i in 0..x {
store_u8(m + i, i as u8);
assert(load_u8(m + i) == i as u8);
}
free(m);
});

39
kernel/examples/reuse.rs Normal file
View File

@@ -0,0 +1,39 @@
#![no_main]
#![no_std]
use extism_runtime_kernel::*;
use owi::*;
main!({
let x = u64();
assume(x > 0);
let y = u64();
assume(y > 0);
let mut tmp = 0;
for _ in 0..y {
let m = alloc(x);
if tmp == 0 {
tmp = m;
} else {
// Check that the existing block is being re-used
assert(m == tmp);
}
assert(length(m) == x);
free(m); // Free the block
}
let y = u64();
assume(y == x + 1);
let n = alloc(y);
assert(n > tmp);
let z = u64();
assume(z <= x);
assume(x - z < 32);
assume(z > 0);
let p = alloc(z);
assert(p == tmp);
});

View File

@@ -1,5 +1,5 @@
#![no_main]
#![no_std]
#![no_main]
pub use extism_runtime_kernel::*;

View File

@@ -175,7 +175,7 @@ impl MemoryRoot {
core::ptr::write_bytes(
self.blocks.as_mut_ptr() as *mut u8,
MemoryStatus::Unused as u8,
self_position as usize,
self_position as usize - core::mem::size_of::<MemoryRoot>(),
);
// Clear extism runtime metadata
@@ -638,4 +638,15 @@ mod test {
assert_eq!(last, 0);
}
#[wasm_bindgen_test]
fn test_sym() {
unsafe {
reset();
let a = alloc(47);
let b = alloc(20);
assert_eq!(length(a), 47);
assert_eq!(length(b), 20);
}
}
}

23
kernel/verify.sh Executable file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -e
OUT_DIR=./target/wasm32-unknown-unknown/release/examples/
get_proof() {
cp "$OUT_DIR/$1.wasm" "./proofs/$1.wasm"
}
cargo build --examples --release --target wasm32-unknown-unknown --no-default-features
mkdir -p proofs
get_proof alloc_length
get_proof load_store
get_proof reuse
get_proof error
for proof in $(ls proofs/*.wasm); do
echo "Checking $proof"
owi conc "$proof" $@
echo
echo "---"
done

View File

@@ -251,19 +251,6 @@ int32_t extism_plugin_call_with_host_context(ExtismPlugin *plugin,
ExtismSize data_len,
void *host_context);
/**
* Call a function with host context.
*
* `func_name`: is the function to call
* `data`: is the input data
* `data_len`: is the length of `data`
* `host_context`: a pointer to context data that will be available in host functions
*/
int32_t extism_plugin_call_wasi_command(ExtismPlugin *plugin,
const char *func_name,
const uint8_t *data,
ExtismSize data_len);
/**
* Get the error associated with a `Plugin`
*/

View File

@@ -327,18 +327,13 @@ impl CurrentPlugin {
}
}
// Enable WASI output, typically used for debugging purposes
if std::env::var("EXTISM_ENABLE_WASI_OUTPUT").is_ok() {
// Enable WASI output, typically used for debugging purposes
ctx.set_stderr(Box::new(wasi_common::sync::stdio::stderr()));
ctx.set_stdout(Box::new(wasi_common::sync::stdio::stdout()));
}
Some(Wasi {
ctx,
stderr: None,
stdout: None,
})
Some(Wasi { ctx })
} else {
None
};

View File

@@ -4,9 +4,6 @@ use crate::*;
pub struct Wasi {
/// wasi
pub ctx: wasi_common::WasiCtx,
pub stdout: Option<std::sync::Arc<std::sync::RwLock<Vec<u8>>>>,
pub stderr: Option<std::sync::Arc<std::sync::RwLock<Vec<u8>>>>,
}
/// InternalExt provides a unified way of acessing `memory`, `store` and `internal` values

View File

@@ -4,8 +4,6 @@ use std::{
path::PathBuf,
};
use wasi_common::pipe::{ReadPipe, WritePipe};
use crate::*;
pub const EXTISM_ENV_MODULE: &str = "extism:host/env";
@@ -485,7 +483,6 @@ impl Plugin {
input: *const u8,
mut len: usize,
host_context: Option<Rooted<ExternRef>>,
wasi_input: bool,
) -> Result<(), Error> {
self.output = Output::default();
self.clear_error()?;
@@ -509,30 +506,6 @@ impl Plugin {
self.reset()?;
let handle = self.current_plugin_mut().memory_new(bytes)?;
if wasi_input {
if let Some(wasi) = &mut self.current_plugin_mut().wasi {
wasi.ctx.set_stdin(Box::new(ReadPipe::from(bytes)));
if let Some(stdout) = &mut wasi.stdout {
stdout.write().unwrap().clear();
} else {
let stdout = std::sync::Arc::new(std::sync::RwLock::new(vec![0; 1024]));
let x = WritePipe::from_shared(stdout.clone());
wasi.ctx.set_stdout(Box::new(x));
wasi.stdout = Some(stdout);
}
if let Some(stderr) = &mut wasi.stderr {
stderr.write().unwrap().clear();
} else {
let stderr = std::sync::Arc::new(std::sync::RwLock::new(vec![0; 1024]));
let x = WritePipe::from_shared(stderr.clone());
wasi.ctx.set_stderr(Box::new(x));
wasi.stderr = Some(stderr);
}
}
}
if let Some(f) = self
.linker
.get(&mut self.store, EXTISM_ENV_MODULE, "input_set")
@@ -729,7 +702,6 @@ impl Plugin {
name: impl AsRef<str>,
input: impl AsRef<[u8]>,
host_context: Option<Rooted<ExternRef>>,
wasi_input: bool,
) -> Result<i32, (Error, i32)> {
let name = name.as_ref();
let input = input.as_ref();
@@ -743,7 +715,7 @@ impl Plugin {
self.instantiate(lock).map_err(|e| (e, -1))?;
self.set_input(input.as_ptr(), input.len(), host_context, wasi_input)
self.set_input(input.as_ptr(), input.len(), host_context)
.map_err(|x| (x, -1))?;
let func = match self.get_func(lock, name) {
@@ -925,7 +897,7 @@ impl Plugin {
let lock = self.instance.clone();
let mut lock = lock.lock().unwrap();
let data = input.to_bytes()?;
self.raw_call(&mut lock, name, data, None, false)
self.raw_call(&mut lock, name, data, None)
.map_err(|e| e.0)
.and_then(move |rc| {
if rc != 0 {
@@ -951,53 +923,11 @@ impl Plugin {
let mut lock = lock.lock().unwrap();
let data = input.to_bytes()?;
let ctx = ExternRef::new(&mut self.store, host_context)?;
self.raw_call(&mut lock, name, data, Some(ctx), false)
self.raw_call(&mut lock, name, data, Some(ctx))
.map_err(|e| e.0)
.and_then(move |_| self.output())
}
/// Call a WASI command module, this will use Extism input as stdin and WASI stdout as the Extism
/// output
pub fn call_wasi_command<'a, T: ToBytes<'a>, U: FromBytesOwned>(
&mut self,
input: T,
) -> Result<U, (Error, Vec<u8>)> {
let lock = self.instance.clone();
let mut lock = lock.lock().unwrap();
let data = input.to_bytes().map_err(|x| (x, vec![]))?;
self.raw_call(&mut lock, "_start", data, None, true)
.map_err(|e| {
if let Some(wasi) = &self.current_plugin_mut().wasi {
if let Some(stderr) = &wasi.stderr {
return (e.0, stderr.read().unwrap().clone());
}
}
(e.0, vec![])
})
.and_then(move |rc| {
if rc != 0 {
let e = Error::msg(format!("Returned non-zero exit code: {rc}"));
if let Some(wasi) = &self.current_plugin_mut().wasi {
if let Some(stderr) = &wasi.stderr {
return Err((e, stderr.read().unwrap().clone()));
}
}
Err((e, vec![]))
} else {
if let Some(wasi) = &self.current_plugin_mut().wasi {
if let Some(stdout) = &wasi.stdout {
return U::from_bytes_owned(&*stdout.write().unwrap())
.map_err(|e| (e, vec![]));
} else {
self.output().map_err(|e| (e, vec![]))
}
} else {
self.output().map_err(|e| (e, vec![]))
}
}
})
}
/// Similar to `Plugin::call`, but returns the Extism error code along with the
/// `Error`. It is assumed if `Ok(_)` is returned that the error code was `0`.
///
@@ -1012,7 +942,7 @@ impl Plugin {
let lock = self.instance.clone();
let mut lock = lock.lock().unwrap();
let data = input.to_bytes().map_err(|e| (e, -1))?;
self.raw_call(&mut lock, name, data, None, false)
self.raw_call(&mut lock, name, data, None)
.and_then(move |_| self.output().map_err(|e| (e, -1)))
}

View File

@@ -522,47 +522,7 @@ pub unsafe extern "C" fn extism_plugin_call_with_host_context(
Err(e) => return plugin.return_error(&mut lock, e, -1),
Ok(x) => x,
};
let res = plugin.raw_call(&mut lock, name, input, Some(r), false);
match res {
Err((e, rc)) => plugin.return_error(&mut lock, e, rc),
Ok(x) => x,
}
}
/// Call a function with host context.
///
/// `func_name`: is the function to call
/// `data`: is the input data
/// `data_len`: is the length of `data`
/// `host_context`: a pointer to context data that will be available in host functions
#[no_mangle]
pub unsafe extern "C" fn extism_plugin_call_wasi_command(
plugin: *mut Plugin,
func_name: *const c_char,
data: *const u8,
data_len: Size,
) -> i32 {
if plugin.is_null() {
return -1;
}
let plugin = &mut *plugin;
let lock = plugin.instance.clone();
let mut lock = lock.lock().unwrap();
// Get function name
let name = std::ffi::CStr::from_ptr(func_name);
let name = match name.to_str() {
Ok(name) => name,
Err(e) => return plugin.return_error(&mut lock, e, -1),
};
trace!(
plugin = plugin.id.to_string(),
"calling function {} using extism_plugin_call",
name
);
let input = std::slice::from_raw_parts(data, data_len as usize);
let res = plugin.raw_call(&mut lock, name, input, None, true);
let res = plugin.raw_call(&mut lock, name, input, Some(r));
match res {
Err((e, rc)) => plugin.return_error(&mut lock, e, rc),
Ok(x) => x,