mirror of
https://github.com/extism/extism.git
synced 2026-01-11 23:08:06 -05:00
Compare commits
14 Commits
custom-htt
...
stable
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4c61e8c44 | ||
|
|
452bb2f498 | ||
|
|
c043decd95 | ||
|
|
ec70b0e7b8 | ||
|
|
1452b9fb42 | ||
|
|
9f69a68df4 | ||
|
|
dbf9f34dc6 | ||
|
|
c8a9478da1 | ||
|
|
e89ddd5a2a | ||
|
|
93392e0884 | ||
|
|
4ebd0eb372 | ||
|
|
8feee0c693 | ||
|
|
773ab32a45 | ||
|
|
6a041d0c39 |
7
.github/workflows/release-elixir.yaml
vendored
7
.github/workflows/release-elixir.yaml
vendored
@@ -10,13 +10,6 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install extism shared library
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p /home/runner/.local/bin/
|
||||
export PATH="/home/runner/.local/bin/:$PATH"
|
||||
curl https://raw.githubusercontent.com/extism/cli/main/install.sh | sh
|
||||
extism --sudo --prefix /usr/local install
|
||||
- name: Setup Elixir Host SDK
|
||||
uses: erlef/setup-beam@v1
|
||||
with:
|
||||
|
||||
2
.github/workflows/release-rust.yaml
vendored
2
.github/workflows/release-rust.yaml
vendored
@@ -20,6 +20,7 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
|
||||
- name: Release Rust Manifest Crate
|
||||
if: always()
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_API_TOKEN }}
|
||||
run: |
|
||||
@@ -29,6 +30,7 @@ jobs:
|
||||
sleep 5
|
||||
|
||||
- name: Release Rust Host SDK
|
||||
if: always()
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_API_TOKEN }}
|
||||
run: |
|
||||
|
||||
@@ -4,7 +4,7 @@ defmodule Extism.MixProject do
|
||||
def project do
|
||||
[
|
||||
app: :extism,
|
||||
version: "0.5.0",
|
||||
version: "0.5.1",
|
||||
elixir: "~> 1.12",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps(),
|
||||
|
||||
@@ -13,6 +13,6 @@ crate-type = ["cdylib"]
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
rustler = "0.28.0"
|
||||
extism = "0.5.0"
|
||||
rustler = "0.29.1"
|
||||
extism = "0.5.2"
|
||||
log = "0.4"
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
//! ## Input/Output
|
||||
//!
|
||||
//! Input and output are just allocated blocks of memory that are marked as either input or output using
|
||||
//! the `extism_input_set` or `extism_output_set` functions. The global variables `INPUT_OFFSET` contains
|
||||
//! the offset in memory to the input data and `INPUT_LENGTH` contains the size of the input data. `OUTPUT_OFFSET`
|
||||
//! and `OUTPUT_LENGTH` are used for the output data.
|
||||
//! the `extism_input_set` or `extism_output_set` functions. The MemoryRoot field `input_offset` contains
|
||||
//! the offset in memory to the input data and `input_length` contains the size of the input data. `output_offset`
|
||||
//! and `output_length` are used for the output data.
|
||||
//!
|
||||
//! ## Error handling
|
||||
//!
|
||||
//! The `ERROR` global is used to track the current error message. If it is set to `0` then there is no error.
|
||||
//! The `error` field is used to track the current error message. If it is set to `0` then there is no error.
|
||||
//! The length of the error message can be retreived using `extism_length`.
|
||||
//!
|
||||
//! ## Memory offsets
|
||||
@@ -45,31 +45,6 @@ pub type Length = u64;
|
||||
/// WebAssembly page size
|
||||
const PAGE_SIZE: usize = 65536;
|
||||
|
||||
/// Determines the amount of bytes that can be wasted by re-using a block. If more than this number is wasted by re-using
|
||||
/// a block then it will be split into two smaller blocks.
|
||||
const BLOCK_SPLIT_SIZE: usize = 128;
|
||||
|
||||
/// Offset to the input data
|
||||
static mut INPUT_OFFSET: Pointer = 0;
|
||||
|
||||
/// Length of the input data
|
||||
static mut INPUT_LENGTH: Length = 0;
|
||||
|
||||
/// Offset to the output data
|
||||
static mut OUTPUT_OFFSET: Pointer = 0;
|
||||
|
||||
/// Offset to the input data
|
||||
static mut OUTPUT_LENGTH: Length = 0;
|
||||
|
||||
/// Current error message
|
||||
static mut ERROR: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
/// Determines if the kernel has been initialized already
|
||||
static mut INITIALIZED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// A pointer to the first page that will be managed by Extism, this is set during initialization
|
||||
static mut START_PAGE: usize = 0;
|
||||
|
||||
/// Provides information about the usage status of a `MemoryBlock`
|
||||
#[repr(u8)]
|
||||
#[derive(PartialEq)]
|
||||
@@ -87,19 +62,34 @@ pub enum MemoryStatus {
|
||||
///
|
||||
/// The overall layout of the Extism-manged memory is organized like this:
|
||||
|
||||
/// |------|-------|---------|-------|--------------|
|
||||
/// | Root | Block | Data | Block | Data | ...
|
||||
/// |------|-------|---------|-------|--------------|
|
||||
/// |------|-------+---------|-------+--------------|
|
||||
/// | Root | Block + Data | Block + Data | ...
|
||||
/// |------|-------+---------|-------+--------------|
|
||||
///
|
||||
/// Where `Root` and `Block` are fixed to the size of the `MemoryRoot` and `MemoryBlock` structs. But
|
||||
/// the size of `Data` is dependent on the allocation size.
|
||||
///
|
||||
/// This means that the offset of a `Block` is the size of `Root` plus the size of all existing `Blocks`
|
||||
/// including their data.
|
||||
#[repr(C)]
|
||||
pub struct MemoryRoot {
|
||||
/// Position of the bump allocator, relative to `START_PAGE`
|
||||
/// Set to true after initialization
|
||||
pub initialized: AtomicBool,
|
||||
/// Position of the bump allocator, relative to `blocks` field
|
||||
pub position: AtomicU64,
|
||||
/// The total size of all data allocated using this allocator
|
||||
pub length: AtomicU64,
|
||||
/// A pointer to where the blocks begin
|
||||
/// Offset of error block
|
||||
pub error: AtomicU64,
|
||||
/// Input position in memory
|
||||
pub input_offset: Pointer,
|
||||
/// Input length
|
||||
pub input_length: Length,
|
||||
/// Output position in memory
|
||||
pub output_offset: Pointer,
|
||||
/// Output length
|
||||
pub output_length: Length,
|
||||
/// A pointer to the start of the first block
|
||||
pub blocks: [MemoryBlock; 0],
|
||||
}
|
||||
|
||||
@@ -119,37 +109,50 @@ pub struct MemoryBlock {
|
||||
|
||||
/// Returns the number of pages needed for the given number of bytes
|
||||
pub fn num_pages(nbytes: u64) -> usize {
|
||||
let nbytes = nbytes as f64;
|
||||
let page = PAGE_SIZE as f64;
|
||||
((nbytes / page) + 0.5) as usize
|
||||
let npages = nbytes / PAGE_SIZE as u64;
|
||||
let remainder = nbytes % PAGE_SIZE as u64;
|
||||
if remainder != 0 {
|
||||
(npages + 1) as usize
|
||||
} else {
|
||||
npages as usize
|
||||
}
|
||||
}
|
||||
|
||||
// Get the `MemoryRoot` at the correct offset in memory
|
||||
// Get the `MemoryRoot`, this is always stored at offset 1 in memory
|
||||
#[inline]
|
||||
unsafe fn memory_root() -> &'static mut MemoryRoot {
|
||||
&mut *((START_PAGE * PAGE_SIZE) as *mut MemoryRoot)
|
||||
&mut *(1 as *mut MemoryRoot)
|
||||
}
|
||||
|
||||
impl MemoryRoot {
|
||||
/// Initialize or load the `MemoryRoot` from the correct position in memory
|
||||
pub unsafe fn new() -> &'static mut MemoryRoot {
|
||||
let root = memory_root();
|
||||
|
||||
// If this fails then `INITIALIZED` is already `true` and we can just return the
|
||||
// already initialized `MemoryRoot`
|
||||
if INITIALIZED
|
||||
if root
|
||||
.initialized
|
||||
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
|
||||
.is_err()
|
||||
{
|
||||
return memory_root();
|
||||
return root;
|
||||
}
|
||||
|
||||
// Ensure that at least one page is allocated to store the `MemoryRoot` data
|
||||
START_PAGE = core::arch::wasm32::memory_grow(0, 1);
|
||||
if START_PAGE == usize::MAX {
|
||||
panic!("Out of memory");
|
||||
if core::arch::wasm32::memory_size(0) == 0 {
|
||||
if core::arch::wasm32::memory_grow(0, 1) == usize::MAX {
|
||||
core::arch::wasm32::unreachable()
|
||||
}
|
||||
}
|
||||
|
||||
root.input_offset = 0;
|
||||
root.input_length = 0;
|
||||
root.output_offset = 0;
|
||||
root.output_length = 0;
|
||||
root.error.store(0, Ordering::Release);
|
||||
|
||||
// Initialize the `MemoryRoot` length, position and data
|
||||
let root = memory_root();
|
||||
root.length.store(
|
||||
PAGE_SIZE as u64 - core::mem::size_of::<MemoryRoot>() as u64,
|
||||
Ordering::Release,
|
||||
@@ -174,6 +177,27 @@ impl MemoryRoot {
|
||||
self.length.load(Ordering::Acquire) as usize,
|
||||
);
|
||||
self.position.store(0, Ordering::Release);
|
||||
self.error.store(0, Ordering::Release);
|
||||
self.input_offset = 0;
|
||||
self.input_length = 0;
|
||||
self.output_offset = 0;
|
||||
self.output_length = 0;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[allow(unused)]
|
||||
fn pointer_in_bounds(&self, p: Pointer) -> bool {
|
||||
let start_ptr = self.blocks.as_ptr() as Pointer;
|
||||
p >= start_ptr && p < start_ptr + self.length.load(Ordering::Acquire) as Pointer
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[allow(unused)]
|
||||
fn pointer_in_bounds_fast(p: Pointer) -> bool {
|
||||
// Similar to `pointer_in_bounds` but less accurate on the upper bound. This uses the total memory size,
|
||||
// instead of checking `MemoryRoot::length`
|
||||
let end = core::arch::wasm32::memory_size(0) << 16;
|
||||
p >= core::mem::size_of::<Self>() as Pointer && p <= end as Pointer
|
||||
}
|
||||
|
||||
// Find a block that is free to use, this can be a new block or an existing freed block. The `self_position` argument
|
||||
@@ -190,6 +214,7 @@ impl MemoryRoot {
|
||||
while (block as u64) < self.blocks.as_ptr() as u64 + self_position {
|
||||
let b = &mut *block;
|
||||
|
||||
// Get the block status, this lets us know if we are able to re-use it
|
||||
let status = b.status.load(Ordering::Acquire);
|
||||
|
||||
// An unused block is safe to use
|
||||
@@ -200,7 +225,7 @@ impl MemoryRoot {
|
||||
// Re-use freed blocks when they're large enough
|
||||
if status == MemoryStatus::Free as u8 && b.size >= length as usize {
|
||||
// Split block if there is too much excess
|
||||
if b.size - length as usize >= BLOCK_SPLIT_SIZE {
|
||||
if b.size - length as usize >= 128 {
|
||||
b.size -= length as usize;
|
||||
b.used = 0;
|
||||
|
||||
@@ -242,13 +267,13 @@ impl MemoryRoot {
|
||||
let curr = self.blocks.as_ptr() as u64 + self_position;
|
||||
|
||||
// Get the number of bytes available
|
||||
let mem_left = self_length - self_position;
|
||||
let mem_left = self_length - self_position - core::mem::size_of::<MemoryRoot>() as u64;
|
||||
|
||||
// When the allocation is larger than the number of bytes available
|
||||
// we will need to try to grow the memory
|
||||
if length >= mem_left {
|
||||
// Calculate the number of pages needed to cover the remaining bytes
|
||||
let npages = num_pages(length);
|
||||
let npages = num_pages(length - mem_left);
|
||||
let x = core::arch::wasm32::memory_grow(0, npages);
|
||||
if x == usize::MAX {
|
||||
return None;
|
||||
@@ -276,8 +301,7 @@ impl MemoryRoot {
|
||||
|
||||
/// Finds the block at an offset in memory
|
||||
pub unsafe fn find_block(&mut self, offs: Pointer) -> Option<&mut MemoryBlock> {
|
||||
if offs >= self.blocks.as_ptr() as Pointer + self.length.load(Ordering::Acquire) as Pointer
|
||||
{
|
||||
if !Self::pointer_in_bounds_fast(offs) {
|
||||
return None;
|
||||
}
|
||||
let ptr = offs - core::mem::size_of::<MemoryBlock>() as u64;
|
||||
@@ -293,9 +317,7 @@ impl MemoryBlock {
|
||||
/// is calculated based on metadata provided by the current block
|
||||
#[inline]
|
||||
pub unsafe fn next_ptr(&mut self) -> *mut MemoryBlock {
|
||||
self.data
|
||||
.as_mut_ptr()
|
||||
.add(self.size + core::mem::size_of::<MemoryBlock>()) as *mut MemoryBlock
|
||||
self.data.as_mut_ptr().add(self.size) as *mut MemoryBlock
|
||||
}
|
||||
|
||||
/// Mark a block as free
|
||||
@@ -305,11 +327,14 @@ impl MemoryBlock {
|
||||
}
|
||||
}
|
||||
|
||||
// Extism functions - these functions should be
|
||||
// Extism functions
|
||||
|
||||
/// Allocate a block of memory and return the offset
|
||||
#[no_mangle]
|
||||
pub unsafe fn extism_alloc(n: Length) -> Pointer {
|
||||
if n == 0 {
|
||||
return 0;
|
||||
}
|
||||
let region = MemoryRoot::new();
|
||||
let block = region.alloc(n);
|
||||
match block {
|
||||
@@ -321,9 +346,19 @@ pub unsafe fn extism_alloc(n: Length) -> Pointer {
|
||||
/// Free allocated memory
|
||||
#[no_mangle]
|
||||
pub unsafe fn extism_free(p: Pointer) {
|
||||
let block = MemoryRoot::new().find_block(p);
|
||||
if p == 0 {
|
||||
return;
|
||||
}
|
||||
let root = MemoryRoot::new();
|
||||
let block = root.find_block(p);
|
||||
if let Some(block) = block {
|
||||
block.free();
|
||||
|
||||
// If the input pointer is freed for some reason, make sure the input length to 0
|
||||
// since the original data is gone
|
||||
if p == root.input_offset {
|
||||
root.input_length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,100 +378,144 @@ pub unsafe fn extism_length(p: Pointer) -> Length {
|
||||
/// Load a byte from Extism-managed memory
|
||||
#[no_mangle]
|
||||
pub unsafe fn extism_load_u8(p: Pointer) -> u8 {
|
||||
#[cfg(feature = "bounds-checking")]
|
||||
if !MemoryRoot::pointer_in_bounds_fast(p) {
|
||||
return 0;
|
||||
}
|
||||
*(p as *mut u8)
|
||||
}
|
||||
|
||||
/// Load a u64 from Extism-managed memory
|
||||
#[no_mangle]
|
||||
pub unsafe fn extism_load_u64(p: Pointer) -> u64 {
|
||||
#[cfg(feature = "bounds-checking")]
|
||||
if !MemoryRoot::pointer_in_bounds_fast(p + core::mem::size_of::<u64>() as u64 - 1) {
|
||||
return 0;
|
||||
}
|
||||
*(p as *mut u64)
|
||||
}
|
||||
|
||||
/// Load a byte from the input data
|
||||
#[no_mangle]
|
||||
pub unsafe fn extism_input_load_u8(p: Pointer) -> u8 {
|
||||
*((INPUT_OFFSET + p) as *mut u8)
|
||||
let root = MemoryRoot::new();
|
||||
#[cfg(feature = "bounds-checking")]
|
||||
if p >= root.input_length {
|
||||
return 0;
|
||||
}
|
||||
*((root.input_offset + p) as *mut u8)
|
||||
}
|
||||
|
||||
/// Load a u64 from the input data
|
||||
#[no_mangle]
|
||||
pub unsafe fn extism_input_load_u64(p: Pointer) -> u64 {
|
||||
*((INPUT_OFFSET + p) as *mut u64)
|
||||
let root = MemoryRoot::new();
|
||||
#[cfg(feature = "bounds-checking")]
|
||||
if p + core::mem::size_of::<u64>() as Pointer > root.input_length {
|
||||
return 0;
|
||||
}
|
||||
*((root.input_offset + p) as *mut u64)
|
||||
}
|
||||
|
||||
/// Write a byte in Extism-managed memory
|
||||
#[no_mangle]
|
||||
pub unsafe fn extism_store_u8(p: Pointer, x: u8) {
|
||||
#[cfg(feature = "bounds-checking")]
|
||||
if !MemoryRoot::pointer_in_bounds_fast(p) {
|
||||
return;
|
||||
}
|
||||
*(p as *mut u8) = x;
|
||||
}
|
||||
|
||||
/// Write a u64 in Extism-managed memory
|
||||
#[no_mangle]
|
||||
pub unsafe fn extism_store_u64(p: Pointer, x: u64) {
|
||||
unsafe {
|
||||
*(p as *mut u64) = x;
|
||||
#[cfg(feature = "bounds-checking")]
|
||||
if !MemoryRoot::pointer_in_bounds_fast(p + core::mem::size_of::<u64>() as u64 - 1) {
|
||||
return;
|
||||
}
|
||||
*(p as *mut u64) = x;
|
||||
}
|
||||
|
||||
/// Set the range of the input data in memory
|
||||
#[no_mangle]
|
||||
pub fn extism_input_set(p: Pointer, len: Length) {
|
||||
unsafe {
|
||||
INPUT_OFFSET = p;
|
||||
INPUT_LENGTH = len;
|
||||
pub unsafe fn extism_input_set(p: Pointer, len: Length) {
|
||||
let root = MemoryRoot::new();
|
||||
#[cfg(feature = "bounds-checking")]
|
||||
{
|
||||
if !root.pointer_in_bounds(p) || !root.pointer_in_bounds(p + len - 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
root.input_offset = p;
|
||||
root.input_length = len;
|
||||
}
|
||||
|
||||
/// Set the range of the output data in memory
|
||||
#[no_mangle]
|
||||
pub fn extism_output_set(p: Pointer, len: Length) {
|
||||
unsafe {
|
||||
OUTPUT_OFFSET = p;
|
||||
OUTPUT_LENGTH = len;
|
||||
pub unsafe fn extism_output_set(p: Pointer, len: Length) {
|
||||
let root = MemoryRoot::new();
|
||||
#[cfg(feature = "bounds-checking")]
|
||||
{
|
||||
if !root.pointer_in_bounds(p) || !root.pointer_in_bounds(p + len - 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
root.output_offset = p;
|
||||
root.output_length = len;
|
||||
}
|
||||
|
||||
/// Get the input length
|
||||
#[no_mangle]
|
||||
pub fn extism_input_length() -> Length {
|
||||
unsafe { INPUT_LENGTH }
|
||||
unsafe { MemoryRoot::new().input_length }
|
||||
}
|
||||
|
||||
/// Get the input offset in Exitsm-managed memory
|
||||
#[no_mangle]
|
||||
pub fn extism_input_offset() -> Length {
|
||||
unsafe { INPUT_OFFSET }
|
||||
unsafe { MemoryRoot::new().input_offset }
|
||||
}
|
||||
|
||||
/// Get the output length
|
||||
#[no_mangle]
|
||||
pub fn extism_output_length() -> Length {
|
||||
unsafe { OUTPUT_LENGTH }
|
||||
pub unsafe fn extism_output_length() -> Length {
|
||||
unsafe { MemoryRoot::new().output_length }
|
||||
}
|
||||
|
||||
/// Get the output offset in Extism-managed memory
|
||||
#[no_mangle]
|
||||
pub fn extism_output_offset() -> Length {
|
||||
unsafe { OUTPUT_OFFSET }
|
||||
pub unsafe fn extism_output_offset() -> Length {
|
||||
MemoryRoot::new().output_offset
|
||||
}
|
||||
|
||||
/// Reset the allocator
|
||||
#[no_mangle]
|
||||
pub unsafe fn extism_reset() {
|
||||
ERROR.store(0, Ordering::SeqCst);
|
||||
MemoryRoot::new().reset()
|
||||
}
|
||||
|
||||
/// Set the error message offset
|
||||
#[no_mangle]
|
||||
pub unsafe fn extism_error_set(ptr: Pointer) {
|
||||
ERROR.store(ptr, Ordering::SeqCst);
|
||||
let root = MemoryRoot::new();
|
||||
|
||||
// Allow ERROR to be set to 0
|
||||
if ptr == 0 {
|
||||
root.error.store(ptr, Ordering::SeqCst);
|
||||
return;
|
||||
}
|
||||
|
||||
if !root.pointer_in_bounds(ptr) {
|
||||
return;
|
||||
}
|
||||
root.error.store(ptr, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// Get the error message offset, if it's `0` then no error has been set
|
||||
#[no_mangle]
|
||||
pub unsafe fn extism_error_get() -> Pointer {
|
||||
ERROR.load(Ordering::SeqCst)
|
||||
MemoryRoot::new().error.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
/// Get the position of the allocator, this can be used as an indication of how many bytes are currently in-use
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "libextism"
|
||||
version = "0.5.0"
|
||||
version = "0.5.4"
|
||||
edition = "2021"
|
||||
authors = ["The Extism Authors", "oss@extism.org"]
|
||||
license = "BSD-3-Clause"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "extism-runtime"
|
||||
version = "0.5.0"
|
||||
version = "0.5.5"
|
||||
edition = "2021"
|
||||
authors = ["The Extism Authors", "oss@extism.org"]
|
||||
license = "BSD-3-Clause"
|
||||
|
||||
@@ -81,7 +81,12 @@ typedef struct {
|
||||
/**
|
||||
* Host function signature
|
||||
*/
|
||||
typedef void (*ExtismFunctionType)(ExtismCurrentPlugin *plugin, const ExtismVal *inputs, ExtismSize n_inputs, ExtismVal *outputs, ExtismSize n_outputs, void *data);
|
||||
typedef void (*ExtismFunctionType)(ExtismCurrentPlugin *plugin,
|
||||
const ExtismVal *inputs,
|
||||
ExtismSize n_inputs,
|
||||
ExtismVal *outputs,
|
||||
ExtismSize n_outputs,
|
||||
void *data);
|
||||
|
||||
typedef int32_t ExtismPlugin;
|
||||
|
||||
|
||||
Binary file not shown.
@@ -104,6 +104,9 @@ pub trait InternalExt {
|
||||
}
|
||||
|
||||
fn memory_alloc(&mut self, n: Size) -> Result<u64, Error> {
|
||||
if n == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
let (linker, mut store) = self.linker_and_store();
|
||||
let output = &mut [Val::I64(0)];
|
||||
linker
|
||||
@@ -113,7 +116,7 @@ pub trait InternalExt {
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(n as i64)], output)?;
|
||||
let offs = output[0].unwrap_i64() as u64;
|
||||
if offs == 0 {
|
||||
if offs == 0 && n > 0 {
|
||||
anyhow::bail!("out of memory")
|
||||
}
|
||||
trace!("memory_alloc: {}, {}", offs, n);
|
||||
|
||||
@@ -200,6 +200,7 @@ pub(crate) fn http_request(
|
||||
Some(res.into_reader())
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Unable to make HTTP request: {:?}", e);
|
||||
if let Some(res) = e.into_response() {
|
||||
data.http_status = res.status();
|
||||
Some(res.into_reader())
|
||||
|
||||
@@ -132,7 +132,8 @@ impl Plugin {
|
||||
Config::new()
|
||||
.epoch_interruption(true)
|
||||
.debug_info(std::env::var("EXTISM_DEBUG").is_ok())
|
||||
.profiler(profiling_strategy()),
|
||||
.profiler(profiling_strategy())
|
||||
.cache_config_load_default()?,
|
||||
)?;
|
||||
let mut imports = imports.into_iter();
|
||||
let (manifest, modules) = Manifest::new(&engine, wasm.as_ref())?;
|
||||
@@ -334,23 +335,25 @@ impl Plugin {
|
||||
internal.linker = linker;
|
||||
}
|
||||
|
||||
let bytes = unsafe { std::slice::from_raw_parts(input, len) };
|
||||
trace!("Input size: {}", bytes.len());
|
||||
if len > 0 {
|
||||
let bytes = unsafe { std::slice::from_raw_parts(input, len) };
|
||||
trace!("Input size: {}", bytes.len());
|
||||
|
||||
if let Some(f) = self.linker.get(&mut self.store, "env", "extism_reset") {
|
||||
f.into_func().unwrap().call(&mut self.store, &[], &mut [])?;
|
||||
} else {
|
||||
error!("Call to extism_reset failed");
|
||||
}
|
||||
if let Some(f) = self.linker.get(&mut self.store, "env", "extism_reset") {
|
||||
f.into_func().unwrap().call(&mut self.store, &[], &mut [])?;
|
||||
} else {
|
||||
error!("Call to extism_reset failed");
|
||||
}
|
||||
|
||||
let offs = self.memory_alloc_bytes(bytes)?;
|
||||
let offs = self.memory_alloc_bytes(bytes)?;
|
||||
|
||||
if let Some(f) = self.linker.get(&mut self.store, "env", "extism_input_set") {
|
||||
f.into_func().unwrap().call(
|
||||
&mut self.store,
|
||||
&[Val::I64(offs as i64), Val::I64(len as i64)],
|
||||
&mut [],
|
||||
)?;
|
||||
if let Some(f) = self.linker.get(&mut self.store, "env", "extism_input_set") {
|
||||
f.into_func().unwrap().call(
|
||||
&mut self.store,
|
||||
&[Val::I64(offs as i64), Val::I64(len as i64)],
|
||||
&mut [],
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "extism"
|
||||
version = "0.5.0"
|
||||
version = "0.5.5"
|
||||
edition = "2021"
|
||||
authors = ["The Extism Authors", "oss@extism.org"]
|
||||
license = "BSD-3-Clause"
|
||||
@@ -10,7 +10,7 @@ description = "Extism Host SDK for Rust"
|
||||
|
||||
[dependencies]
|
||||
extism-manifest = { version = "0.5.0", path = "../manifest" }
|
||||
extism-runtime = { version = "0.5.0", path = "../runtime"}
|
||||
extism-runtime = { version = "0.5.5", path = "../runtime"}
|
||||
serde_json = "1"
|
||||
log = "0.4"
|
||||
anyhow = "1"
|
||||
|
||||
@@ -45,6 +45,7 @@ mod tests {
|
||||
const WASM: &[u8] = include_bytes!("../../wasm/code-functions.wasm");
|
||||
const WASM_LOOP: &[u8] = include_bytes!("../../wasm/loop.wasm");
|
||||
const WASM_GLOBALS: &[u8] = include_bytes!("../../wasm/globals.wasm");
|
||||
const REFLECT_WASM: &[u8] = include_bytes!("../../wasm/reflect.wasm");
|
||||
|
||||
fn hello_world(
|
||||
plugin: &mut CurrentPlugin,
|
||||
@@ -53,8 +54,8 @@ mod tests {
|
||||
_user_data: UserData,
|
||||
) -> Result<(), Error> {
|
||||
let input_offs = inputs[0].unwrap_i64() as u64;
|
||||
let input = plugin.memory_read_str(input_offs).unwrap().to_string();
|
||||
|
||||
let length = plugin.memory_length(input_offs);
|
||||
let input = plugin.memory_read(input_offs, length).to_vec();
|
||||
let output = plugin.memory_alloc_bytes(&input).unwrap();
|
||||
outputs[0] = Val::I64(output as i64);
|
||||
Ok(())
|
||||
@@ -282,7 +283,7 @@ mod tests {
|
||||
});
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
let _output = plugin.call("infinite_loop", "abc123");
|
||||
let _output = plugin.call("infinite_loop", "");
|
||||
let end = std::time::Instant::now();
|
||||
let time = end - start;
|
||||
println!("Cancelled plugin ran for {:?}", time);
|
||||
@@ -319,4 +320,26 @@ mod tests {
|
||||
assert_eq!(count.get("count").unwrap().as_i64().unwrap(), i);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fuzz_reflect_plugin() {
|
||||
// assert!(set_log_file("stdout", Some(log::Level::Trace)));
|
||||
let f = Function::new(
|
||||
"host_reflect",
|
||||
[ValType::I64],
|
||||
[ValType::I64],
|
||||
None,
|
||||
hello_world,
|
||||
);
|
||||
|
||||
let context = Context::new();
|
||||
let mut plugin = Plugin::new(&context, REFLECT_WASM, [f], true).unwrap();
|
||||
|
||||
for i in 1..65540 {
|
||||
let input = "a".repeat(i);
|
||||
let output = plugin.call("reflect", input.clone());
|
||||
let output = std::str::from_utf8(output.unwrap()).unwrap();
|
||||
assert_eq!(output, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
wasm/reflect.wasm
Normal file
BIN
wasm/reflect.wasm
Normal file
Binary file not shown.
Reference in New Issue
Block a user