Skip to content

Commit

Permalink
Support for ResourceLimiter API (#737)
Browse files Browse the repository at this point in the history
* Sketch of support for ResourceLimiter API

* Address review comments.

* Handle the 3 separate ResourceLimiter response types more correctly.

* Fix typo in existing error message.

* Add StoreLimits, StoreLimitsBuilder and docs from wasmtime.

* Fix unused-variable warnings.

* Conditionalize ResourceLimiter on cfg(feature="std")

* Add ResourceLimiter test and fix bugs it uncovered.

* Avoid redundant limit and size storage in previous wasmtime-like design.

* Thread an ever-so-smaller &mut ResourceLimiterRef into the Executor

* Support no_std differently (import alloc::boxed::Box)

* Avoid storing &ResourceLimiterRef in Executor (maybe breaks SROA?)

* Fix no_std a little harder.

* Restore TableError::GrowOutOfBounds fields

* Remove now-redundant pub(crate)

* Remove unnecessary cfg guard on alloc::boxed::Box

* Address clippy warnings

* Fix doc reference.

* Some cleanups as requested in review.

* Fix wasmi_core doc-link to wasmi, deps do not go that way.

* Fix s/State/Store/ doc typo

* Add docs on Store::limiter

* Fix new nightly clippy nit
  • Loading branch information
graydon committed Jul 4, 2023
1 parent 799995d commit 4860ecc
Show file tree
Hide file tree
Showing 16 changed files with 861 additions and 79 deletions.
8 changes: 8 additions & 0 deletions crates/core/src/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,13 @@ pub enum TrapCode {
/// internal bytecode so that fuel is consumed for each executed instruction.
/// This is useful to deterministically halt or yield a WebAssembly execution.
OutOfFuel,

/// This trap is raised when a growth operation was attempted and an
/// installed `wasmi::ResourceLimiter` returned `Err(...)` from the
/// associated `table_growing` or `memory_growing` method, indicating a
/// desire on the part of the embedder to trap the interpreter rather than
/// merely fail the growth operation.
GrowthOperationLimited,
}

impl TrapCode {
Expand All @@ -305,6 +312,7 @@ impl TrapCode {
Self::StackOverflow => "call stack exhausted",
Self::BadSignature => "indirect call type mismatch",
Self::OutOfFuel => "all fuel consumed by WebAssembly",
Self::GrowthOperationLimited => "growth operation limited",
}
}
}
Expand Down
37 changes: 24 additions & 13 deletions crates/wasmi/src/engine/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use crate::{
ValueStack,
},
func::FuncEntity,
store::ResourceLimiterRef,
table::TableEntity,
FuelConsumptionMode,
Func,
Expand Down Expand Up @@ -92,15 +93,17 @@ pub enum ReturnOutcome {
///
/// If the Wasm execution traps.
#[inline(never)]
pub fn execute_wasm<'engine>(
ctx: &mut StoreInner,
pub fn execute_wasm<'ctx, 'engine>(
ctx: &'ctx mut StoreInner,
cache: &'engine mut InstanceCache,
value_stack: &'engine mut ValueStack,
call_stack: &'engine mut CallStack,
code_map: &'engine CodeMap,
const_pool: ConstPoolView<'engine>,
resource_limiter: &'ctx mut ResourceLimiterRef<'ctx>,
) -> Result<WasmOutcome, TrapCode> {
Executor::new(ctx, cache, value_stack, call_stack, code_map, const_pool).execute()
Executor::new(ctx, cache, value_stack, call_stack, code_map, const_pool)
.execute(resource_limiter)
}

/// The function signature of Wasm load operations.
Expand Down Expand Up @@ -213,7 +216,10 @@ impl<'ctx, 'engine> Executor<'ctx, 'engine> {

/// Executes the function frame until it returns or traps.
#[inline(always)]
fn execute(mut self) -> Result<WasmOutcome, TrapCode> {
fn execute(
mut self,
resource_limiter: &'ctx mut ResourceLimiterRef<'ctx>,
) -> Result<WasmOutcome, TrapCode> {
use Instruction as Instr;
loop {
match *self.ip.get() {
Expand Down Expand Up @@ -280,13 +286,13 @@ impl<'ctx, 'engine> Executor<'ctx, 'engine> {
Instr::I64Store16(offset) => self.visit_i64_store_16(offset)?,
Instr::I64Store32(offset) => self.visit_i64_store_32(offset)?,
Instr::MemorySize => self.visit_memory_size(),
Instr::MemoryGrow => self.visit_memory_grow()?,
Instr::MemoryGrow => self.visit_memory_grow(&mut *resource_limiter)?,
Instr::MemoryFill => self.visit_memory_fill()?,
Instr::MemoryCopy => self.visit_memory_copy()?,
Instr::MemoryInit(segment) => self.visit_memory_init(segment)?,
Instr::DataDrop(segment) => self.visit_data_drop(segment),
Instr::TableSize(table) => self.visit_table_size(table),
Instr::TableGrow(table) => self.visit_table_grow(table)?,
Instr::TableGrow(table) => self.visit_table_grow(table, &mut *resource_limiter)?,
Instr::TableFill(table) => self.visit_table_fill(table)?,
Instr::TableGet(table) => self.visit_table_get(table)?,
Instr::TableSet(table) => self.visit_table_set(table)?,
Expand Down Expand Up @@ -1077,7 +1083,10 @@ impl<'ctx, 'engine> Executor<'ctx, 'engine> {
}

#[inline(always)]
fn visit_memory_grow(&mut self) -> Result<(), TrapCode> {
fn visit_memory_grow(
&mut self,
resource_limiter: &mut ResourceLimiterRef<'ctx>,
) -> Result<(), TrapCode> {
let delta: u32 = self.sp.pop_as();
let delta = match Pages::new(delta) {
Some(pages) => pages,
Expand All @@ -1097,9 +1106,8 @@ impl<'ctx, 'engine> Executor<'ctx, 'engine> {
let new_pages = this
.ctx
.resolve_memory_mut(memory)
.grow(delta)
.map(u32::from)
.map_err(|_| EntityGrowError::InvalidGrow)?;
.grow(delta, resource_limiter)
.map(u32::from)?;
// The `memory.grow` operation might have invalidated the cached
// linear memory so we need to reset it in order for the cache to
// reload in case it is used again.
Expand Down Expand Up @@ -1210,7 +1218,11 @@ impl<'ctx, 'engine> Executor<'ctx, 'engine> {
}

#[inline(always)]
fn visit_table_grow(&mut self, table_index: TableIdx) -> Result<(), TrapCode> {
fn visit_table_grow(
&mut self,
table_index: TableIdx,
resource_limiter: &mut ResourceLimiterRef<'ctx>,
) -> Result<(), TrapCode> {
let (init, delta) = self.sp.pop2();
let delta: u32 = delta.into();
let result = self.consume_fuel_with(
Expand All @@ -1219,8 +1231,7 @@ impl<'ctx, 'engine> Executor<'ctx, 'engine> {
let table = this.cache.get_table(this.ctx, table_index);
this.ctx
.resolve_table_mut(&table)
.grow_untyped(delta, init)
.map_err(|_| EntityGrowError::InvalidGrow)
.grow_untyped(delta, init, resource_limiter)
},
);
let result = match result {
Expand Down
4 changes: 3 additions & 1 deletion crates/wasmi/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -793,18 +793,20 @@ impl<'engine> EngineExecutor<'engine> {
code.into()
}

let store_inner = &mut ctx.store.inner;
let (store_inner, mut resource_limiter) = ctx.store.store_inner_and_resource_limiter_ref();
let value_stack = &mut self.stack.values;
let call_stack = &mut self.stack.frames;
let code_map = &self.res.code_map;
let const_pool = self.res.const_pool.view();

execute_wasm(
store_inner,
cache,
value_stack,
call_stack,
code_map,
const_pool,
&mut resource_limiter,
)
.map_err(make_trap)
}
Expand Down
4 changes: 1 addition & 3 deletions crates/wasmi/src/func/typed_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,7 @@ impl<Results> Default for CallResultsTuple<Results> {
impl<Results> Copy for CallResultsTuple<Results> {}
impl<Results> Clone for CallResultsTuple<Results> {
fn clone(&self) -> Self {
Self {
_marker: PhantomData,
}
*self
}
}

Expand Down
2 changes: 2 additions & 0 deletions crates/wasmi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ mod externref;
mod func;
mod global;
mod instance;
mod limits;
mod linker;
mod memory;
mod module;
Expand Down Expand Up @@ -145,6 +146,7 @@ pub use self::{
},
global::{Global, GlobalType, Mutability},
instance::{Export, ExportsIter, Extern, ExternType, Instance},
limits::{ResourceLimiter, StoreLimits, StoreLimitsBuilder},
linker::Linker,
memory::{Memory, MemoryType},
module::{
Expand Down
Loading

0 comments on commit 4860ecc

Please sign in to comment.