mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 12:17:58 +00:00
Integrate Wasmer into Substrate sandbox environment (#5920)
* Add comments and refactor Sandbox module * Adds some comments * Add wasmtime instance to the sandbox and delegate calls * Adds module imports stub * WIP state holder via *mut * My take at the problem * Brings back invoke and instantiate implementation details * Removes redundant bound * Code cleanup * Fixes invoke closure * Refactors FunctionExecutor to eliminate lifetime * Wraps `FunctionExecutor::sandbox_store` in `RefCell` * Renames `FunctionExecutor::heap` to `allocator` * Wraps `FunctionExecutor::allocator` in `RefCell` * Refactors FunctionExecutor to `Rc<Inner>` pattern * Implements scoped TLS for FunctionExecutor * Fixes wasmi instancing * Fixes sandbox asserts * Makes sandbox compile after wasmtime API change * Uses Vurich/wasmtime for the Lightbeam backend * Uses wasmtime instead of wasmi for sandbox API results * Refactors sandbox to use one of the execution backends at a time * Fixes wasmtime module instantiation * TEMP vurich branch stuff * Adds wasmer impl stub * Adds get global * Fixes warnings * Adds wasmer invoke impl * Implements host function interface for wasmer * Fixes wasmer instantiation result * Adds workaround to remove debug_assert * Fixes import object generation for wasmer * Attempt to propagate wasmer::Store through sandbox::Store * Wraps `sandbox::Store::memories` in `RefCell` * Moves `sandbox::instantiate` to `sandbox::Store` * Eliminate `RefCell<memories>` * Implements `HostState::memory_get/set`, removes accidental `borrow_mut` * Fixes sandbox memory handling for wasmi * Fix memory allocation * Resets Cargo.lock to match master * Fixes compilation * Refactors sandbox to use TLS for dispatch_thunk propagation to wasmer * Pass dispatch thunk to the sandbox as a TLS * Initialize dispatch thunk holder in `SandboxInstance` * Comment out Wasmtime/Lightbeam sandbox backend * Revert wasmtime back to mainstream * Adds SandboxExecutionMethod enum for cli param * Cleanup sandbox code * Allow wasmi to access wasmer memory regions * More cleanup * Remove debug logging, replace asserts with runtime errors * Revert "Adds SandboxExecutionMethod enum for cli param" This reverts commit dcb2b1d3b54145ab51ad2e3fef0d980ba215b596. * Fixes warnings * Fixes indentation and line width * Fix return types condition * Puts everything related under the `wasmer-sandbox` feature flag * Fixes warnings * Address grumbles * Split instantiate per backend * More splits * Refacmemory allocation * Nitpicks * Attempt to wrap wasmer memory in protoco enforcing type * Revert renaming * WIP wasm buffer proxy API * Reimplement util::wasmer::MemoryRef to use buffers instead of memory slices * Adds WasmiMemoryWrapper and MemoryTransfer trait * Refactor naming * Perform all memory transfers using MemoryTransfer * Adds allocating `read` * Adds comments * Removes unused imports * Removes now unused function * Pulls Cargo.lock from origin/master * Fix rustdoc * Removes unused `TransferError` * Update Cargo.lock * Removes unused import * cargo fmt * Fix feature dependency graph * Feature should flow from the top level crate * We should not assume a specific workspace structure * sc-executor-wasmi does not use the feature * sc-executor-wasmtime should not know about the feature * Fix doc typo * Enable wasmer-sandbox by default (for now) It will be removed before merge. It is so that the benchbot uses the wasmer sandbox. * cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs * Revert "cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs" This reverts commit d713590ba45387c4204b2ad97c8bd6f6ebabda4e. * cargo fmt * Add ci-check to prevent wasmer sandbox build breaking * Run tests with wasmer-sandbox enabled * Revert "Run tests with wasmer-sandbox enabled" This reverts commit cff63156a162f9ffdab23e7cb94a30f44e320f8a. Co-authored-by: Sergei Shulepov <s.pepyakin@gmail.com> Co-authored-by: Andrew Jones <ascjones@gmail.com> Co-authored-by: Alexander Theißen <alex.theissen@me.com> Co-authored-by: Parity Benchmarking Bot <admin@parity.io>
This commit is contained in:
@@ -19,13 +19,14 @@
|
||||
//! This module defines `HostState` and `HostContext` structs which provide logic and state
|
||||
//! required for execution of host.
|
||||
|
||||
use crate::{instance_wrapper::InstanceWrapper, util};
|
||||
use crate::instance_wrapper::InstanceWrapper;
|
||||
use codec::{Decode, Encode};
|
||||
use log::trace;
|
||||
use sc_allocator::FreeingBumpHeapAllocator;
|
||||
use sc_executor_common::{
|
||||
error::Result,
|
||||
sandbox::{self, SandboxCapabilities, SupervisorFuncIndex},
|
||||
sandbox::{self, SandboxCapabilities, SandboxCapabilitiesHolder, SupervisorFuncIndex},
|
||||
util::MemoryTransfer,
|
||||
};
|
||||
use sp_core::sandbox as sandbox_primitives;
|
||||
use sp_wasm_interface::{FunctionContext, MemoryId, Pointer, Sandbox, WordSize};
|
||||
@@ -42,7 +43,12 @@ pub struct SupervisorFuncRef(Func);
|
||||
/// The state required to construct a HostContext context. The context only lasts for one host
|
||||
/// call, whereas the state is maintained for the duration of a Wasm runtime call, which may make
|
||||
/// many different host calls that must share state.
|
||||
#[derive(Clone)]
|
||||
pub struct HostState {
|
||||
inner: Rc<Inner>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
// We need some interior mutability here since the host state is shared between all host
|
||||
// function handlers and the wasmtime backend's `impl WasmRuntime`.
|
||||
//
|
||||
@@ -61,31 +67,18 @@ impl HostState {
|
||||
/// Constructs a new `HostState`.
|
||||
pub fn new(allocator: FreeingBumpHeapAllocator, instance: Rc<InstanceWrapper>) -> Self {
|
||||
HostState {
|
||||
sandbox_store: RefCell::new(sandbox::Store::new()),
|
||||
allocator: RefCell::new(allocator),
|
||||
instance,
|
||||
inner: Rc::new(Inner {
|
||||
sandbox_store: RefCell::new(sandbox::Store::new(
|
||||
sandbox::SandboxBackend::TryWasmer,
|
||||
)),
|
||||
allocator: RefCell::new(allocator),
|
||||
instance,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Materialize `HostContext` that can be used to invoke a substrate host `dyn Function`.
|
||||
pub fn materialize<'a>(&'a self) -> HostContext<'a> {
|
||||
HostContext(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// A `HostContext` implements `FunctionContext` for making host calls from a Wasmtime
|
||||
/// runtime. The `HostContext` exists only for the lifetime of the call and borrows state from
|
||||
/// a longer-living `HostState`.
|
||||
pub struct HostContext<'a>(&'a HostState);
|
||||
|
||||
impl<'a> std::ops::Deref for HostContext<'a> {
|
||||
type Target = HostState;
|
||||
fn deref(&self) -> &HostState {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SandboxCapabilities for HostContext<'a> {
|
||||
impl SandboxCapabilities for HostState {
|
||||
type SupervisorFuncRef = SupervisorFuncRef;
|
||||
|
||||
fn invoke(
|
||||
@@ -125,28 +118,30 @@ impl<'a> SandboxCapabilities for HostContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sp_wasm_interface::FunctionContext for HostContext<'a> {
|
||||
impl sp_wasm_interface::FunctionContext for HostState {
|
||||
fn read_memory_into(
|
||||
&self,
|
||||
address: Pointer<u8>,
|
||||
dest: &mut [u8],
|
||||
) -> sp_wasm_interface::Result<()> {
|
||||
self.instance.read_memory_into(address, dest).map_err(|e| e.to_string())
|
||||
self.inner.instance.read_memory_into(address, dest).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> sp_wasm_interface::Result<()> {
|
||||
self.instance.write_memory_from(address, data).map_err(|e| e.to_string())
|
||||
self.inner.instance.write_memory_from(address, data).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
fn allocate_memory(&mut self, size: WordSize) -> sp_wasm_interface::Result<Pointer<u8>> {
|
||||
self.instance
|
||||
.allocate(&mut *self.allocator.borrow_mut(), size)
|
||||
self.inner
|
||||
.instance
|
||||
.allocate(&mut *self.inner.allocator.borrow_mut(), size)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> sp_wasm_interface::Result<()> {
|
||||
self.instance
|
||||
.deallocate(&mut *self.allocator.borrow_mut(), ptr)
|
||||
self.inner
|
||||
.instance
|
||||
.deallocate(&mut *self.inner.allocator.borrow_mut(), ptr)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
@@ -155,7 +150,7 @@ impl<'a> sp_wasm_interface::FunctionContext for HostContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sandbox for HostContext<'a> {
|
||||
impl Sandbox for HostState {
|
||||
fn memory_get(
|
||||
&mut self,
|
||||
memory_id: MemoryId,
|
||||
@@ -164,27 +159,20 @@ impl<'a> Sandbox for HostContext<'a> {
|
||||
buf_len: WordSize,
|
||||
) -> sp_wasm_interface::Result<u32> {
|
||||
let sandboxed_memory =
|
||||
self.sandbox_store.borrow().memory(memory_id).map_err(|e| e.to_string())?;
|
||||
sandboxed_memory.with_direct_access(|sandboxed_memory| {
|
||||
let len = buf_len as usize;
|
||||
let src_range = match util::checked_range(offset as usize, len, sandboxed_memory.len())
|
||||
{
|
||||
Some(range) => range,
|
||||
None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS),
|
||||
};
|
||||
let supervisor_mem_size = self.instance.memory_size() as usize;
|
||||
let dst_range = match util::checked_range(buf_ptr.into(), len, supervisor_mem_size) {
|
||||
Some(range) => range,
|
||||
None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS),
|
||||
};
|
||||
self.instance
|
||||
.write_memory_from(
|
||||
Pointer::new(dst_range.start as u32),
|
||||
&sandboxed_memory[src_range],
|
||||
)
|
||||
.expect("ranges are checked above; write can't fail; qed");
|
||||
Ok(sandbox_primitives::ERR_OK)
|
||||
})
|
||||
self.inner.sandbox_store.borrow().memory(memory_id).map_err(|e| e.to_string())?;
|
||||
|
||||
let len = buf_len as usize;
|
||||
|
||||
let buffer = match sandboxed_memory.read(Pointer::new(offset as u32), len) {
|
||||
Err(_) => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS),
|
||||
Ok(buffer) => buffer,
|
||||
};
|
||||
|
||||
if let Err(_) = self.inner.instance.write_memory_from(buf_ptr, &buffer) {
|
||||
return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS)
|
||||
}
|
||||
|
||||
Ok(sandbox_primitives::ERR_OK)
|
||||
}
|
||||
|
||||
fn memory_set(
|
||||
@@ -195,38 +183,33 @@ impl<'a> Sandbox for HostContext<'a> {
|
||||
val_len: WordSize,
|
||||
) -> sp_wasm_interface::Result<u32> {
|
||||
let sandboxed_memory =
|
||||
self.sandbox_store.borrow().memory(memory_id).map_err(|e| e.to_string())?;
|
||||
sandboxed_memory.with_direct_access_mut(|sandboxed_memory| {
|
||||
let len = val_len as usize;
|
||||
let supervisor_mem_size = self.instance.memory_size() as usize;
|
||||
let src_range = match util::checked_range(val_ptr.into(), len, supervisor_mem_size) {
|
||||
Some(range) => range,
|
||||
None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS),
|
||||
};
|
||||
let dst_range = match util::checked_range(offset as usize, len, sandboxed_memory.len())
|
||||
{
|
||||
Some(range) => range,
|
||||
None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS),
|
||||
};
|
||||
self.instance
|
||||
.read_memory_into(
|
||||
Pointer::new(src_range.start as u32),
|
||||
&mut sandboxed_memory[dst_range],
|
||||
)
|
||||
.expect("ranges are checked above; read can't fail; qed");
|
||||
Ok(sandbox_primitives::ERR_OK)
|
||||
})
|
||||
self.inner.sandbox_store.borrow().memory(memory_id).map_err(|e| e.to_string())?;
|
||||
|
||||
let len = val_len as usize;
|
||||
|
||||
let buffer = match self.inner.instance.read_memory(val_ptr, len) {
|
||||
Err(_) => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS),
|
||||
Ok(buffer) => buffer,
|
||||
};
|
||||
|
||||
if let Err(_) = sandboxed_memory.write_from(Pointer::new(offset as u32), &buffer) {
|
||||
return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS)
|
||||
}
|
||||
|
||||
Ok(sandbox_primitives::ERR_OK)
|
||||
}
|
||||
|
||||
fn memory_teardown(&mut self, memory_id: MemoryId) -> sp_wasm_interface::Result<()> {
|
||||
self.sandbox_store
|
||||
self.inner
|
||||
.sandbox_store
|
||||
.borrow_mut()
|
||||
.memory_teardown(memory_id)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
fn memory_new(&mut self, initial: u32, maximum: u32) -> sp_wasm_interface::Result<u32> {
|
||||
self.sandbox_store
|
||||
self.inner
|
||||
.sandbox_store
|
||||
.borrow_mut()
|
||||
.new_memory(initial, maximum)
|
||||
.map_err(|e| e.to_string())
|
||||
@@ -250,9 +233,14 @@ impl<'a> Sandbox for HostContext<'a> {
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let instance =
|
||||
self.sandbox_store.borrow().instance(instance_id).map_err(|e| e.to_string())?;
|
||||
let result = instance.invoke(export_name, &args, self, state);
|
||||
let instance = self
|
||||
.inner
|
||||
.sandbox_store
|
||||
.borrow()
|
||||
.instance(instance_id)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let result = instance.invoke::<_, CapsHolder, ThunkHolder>(export_name, &args, state);
|
||||
|
||||
match result {
|
||||
Ok(None) => Ok(sandbox_primitives::ERR_OK),
|
||||
@@ -262,7 +250,7 @@ impl<'a> Sandbox for HostContext<'a> {
|
||||
if val.len() > return_val_len as usize {
|
||||
Err("Return value buffer is too small")?;
|
||||
}
|
||||
<HostContext as FunctionContext>::write_memory(self, return_val, val)
|
||||
<HostState as FunctionContext>::write_memory(self, return_val, val)
|
||||
.map_err(|_| "can't write return value")?;
|
||||
Ok(sandbox_primitives::ERR_OK)
|
||||
})
|
||||
@@ -272,7 +260,8 @@ impl<'a> Sandbox for HostContext<'a> {
|
||||
}
|
||||
|
||||
fn instance_teardown(&mut self, instance_id: u32) -> sp_wasm_interface::Result<()> {
|
||||
self.sandbox_store
|
||||
self.inner
|
||||
.sandbox_store
|
||||
.borrow_mut()
|
||||
.instance_teardown(instance_id)
|
||||
.map_err(|e| e.to_string())
|
||||
@@ -288,6 +277,7 @@ impl<'a> Sandbox for HostContext<'a> {
|
||||
// Extract a dispatch thunk from the instance's table by the specified index.
|
||||
let dispatch_thunk = {
|
||||
let table_item = self
|
||||
.inner
|
||||
.instance
|
||||
.table()
|
||||
.as_ref()
|
||||
@@ -303,20 +293,26 @@ impl<'a> Sandbox for HostContext<'a> {
|
||||
SupervisorFuncRef(func_ref)
|
||||
};
|
||||
|
||||
let guest_env =
|
||||
match sandbox::GuestEnvironment::decode(&*self.sandbox_store.borrow(), raw_env_def) {
|
||||
Ok(guest_env) => guest_env,
|
||||
Err(_) => return Ok(sandbox_primitives::ERR_MODULE as u32),
|
||||
};
|
||||
let guest_env = match sandbox::GuestEnvironment::decode(
|
||||
&*self.inner.sandbox_store.borrow(),
|
||||
raw_env_def,
|
||||
) {
|
||||
Ok(guest_env) => guest_env,
|
||||
Err(_) => return Ok(sandbox_primitives::ERR_MODULE as u32),
|
||||
};
|
||||
|
||||
let instance_idx_or_err_code =
|
||||
match sandbox::instantiate(self, dispatch_thunk, wasm, guest_env, state)
|
||||
.map(|i| i.register(&mut *self.sandbox_store.borrow_mut()))
|
||||
{
|
||||
Ok(instance_idx) => instance_idx,
|
||||
Err(sandbox::InstantiationError::StartTrapped) => sandbox_primitives::ERR_EXECUTION,
|
||||
Err(_) => sandbox_primitives::ERR_MODULE,
|
||||
};
|
||||
let store = &mut *self.inner.sandbox_store.borrow_mut();
|
||||
let result = DISPATCH_THUNK.set(&dispatch_thunk, || {
|
||||
store
|
||||
.instantiate::<_, CapsHolder, ThunkHolder>(wasm, guest_env, state)
|
||||
.map(|i| i.register(store))
|
||||
});
|
||||
|
||||
let instance_idx_or_err_code = match result {
|
||||
Ok(instance_idx) => instance_idx,
|
||||
Err(sandbox::InstantiationError::StartTrapped) => sandbox_primitives::ERR_EXECUTION,
|
||||
Err(_) => sandbox_primitives::ERR_MODULE,
|
||||
};
|
||||
|
||||
Ok(instance_idx_or_err_code as u32)
|
||||
}
|
||||
@@ -326,10 +322,50 @@ impl<'a> Sandbox for HostContext<'a> {
|
||||
instance_idx: u32,
|
||||
name: &str,
|
||||
) -> sp_wasm_interface::Result<Option<sp_wasm_interface::Value>> {
|
||||
self.sandbox_store
|
||||
self.inner
|
||||
.sandbox_store
|
||||
.borrow()
|
||||
.instance(instance_idx)
|
||||
.map(|i| i.get_global_val(name))
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Wasmtime specific implementation of `SandboxCapabilitiesHolder` that provides
|
||||
/// sandbox with a scoped thread local access to a function executor.
|
||||
/// This is a way to calm down the borrow checker since host function closures
|
||||
/// require exclusive access to it.
|
||||
struct CapsHolder;
|
||||
|
||||
impl SandboxCapabilitiesHolder for CapsHolder {
|
||||
type SupervisorFuncRef = SupervisorFuncRef;
|
||||
type SC = HostState;
|
||||
|
||||
fn with_sandbox_capabilities<R, F: FnOnce(&mut Self::SC) -> R>(f: F) -> R {
|
||||
crate::state_holder::with_context(|ctx| f(&mut ctx.expect("wasmtime executor is not set")))
|
||||
}
|
||||
}
|
||||
|
||||
/// Wasmtime specific implementation of `DispatchThunkHolder` that provides
|
||||
/// sandbox with a scoped thread local access to a dispatch thunk.
|
||||
/// This is a way to calm down the borrow checker since host function closures
|
||||
/// require exclusive access to it.
|
||||
struct ThunkHolder;
|
||||
|
||||
scoped_tls::scoped_thread_local!(static DISPATCH_THUNK: SupervisorFuncRef);
|
||||
|
||||
impl sandbox::DispatchThunkHolder for ThunkHolder {
|
||||
type DispatchThunk = SupervisorFuncRef;
|
||||
|
||||
fn with_dispatch_thunk<R, F: FnOnce(&mut Self::DispatchThunk) -> R>(f: F) -> R {
|
||||
assert!(DISPATCH_THUNK.is_set(), "dispatch thunk is not set");
|
||||
DISPATCH_THUNK.with(|thunk| f(&mut thunk.clone()))
|
||||
}
|
||||
|
||||
fn initialize_thunk<R, F>(s: &Self::DispatchThunk, f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
DISPATCH_THUNK.set(s, f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +255,9 @@ impl MissingHostFuncHandler {
|
||||
fn wasmtime_func_sig(func: &dyn Function) -> wasmtime::FuncType {
|
||||
let signature = func.signature();
|
||||
let params = signature.args.iter().cloned().map(into_wasmtime_val_type);
|
||||
|
||||
let results = signature.return_value.iter().cloned().map(into_wasmtime_val_type);
|
||||
|
||||
wasmtime::FuncType::new(params, results)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,15 @@
|
||||
//! Defines data and logic needed for interaction with an WebAssembly instance of a substrate
|
||||
//! runtime module.
|
||||
|
||||
use crate::{imports::Imports, util};
|
||||
use crate::{
|
||||
imports::Imports,
|
||||
util::{from_wasmtime_val, into_wasmtime_val},
|
||||
};
|
||||
|
||||
use sc_executor_common::{
|
||||
error::{Error, Result},
|
||||
runtime_blob,
|
||||
util::checked_range,
|
||||
wasm_runtime::InvokeMethod,
|
||||
};
|
||||
use sp_wasm_interface::{Pointer, Value, WordSize};
|
||||
@@ -96,12 +100,16 @@ impl EntryPoint {
|
||||
/// routines.
|
||||
pub struct InstanceWrapper {
|
||||
instance: Instance,
|
||||
|
||||
// The memory instance of the `instance`.
|
||||
//
|
||||
// It is important to make sure that we don't make any copies of this to make it easier to
|
||||
// proof See `memory_as_slice` and `memory_as_slice_mut`.
|
||||
memory: Memory,
|
||||
|
||||
/// Indirect functions table of the module
|
||||
table: Option<Table>,
|
||||
|
||||
// Make this struct explicitly !Send & !Sync.
|
||||
_not_send_nor_sync: marker::PhantomData<*const ()>,
|
||||
}
|
||||
@@ -147,7 +155,7 @@ impl InstanceWrapper {
|
||||
None => {
|
||||
let memory = get_linear_memory(&instance)?;
|
||||
if !memory.grow(heap_pages).is_ok() {
|
||||
return Err("failed top increase the linear memory size".into())
|
||||
return Err("failed to increase the linear memory size".into())
|
||||
}
|
||||
memory
|
||||
},
|
||||
@@ -223,11 +231,6 @@ impl InstanceWrapper {
|
||||
self.table.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the byte size of the linear memory instance attached to this instance.
|
||||
pub fn memory_size(&self) -> u32 {
|
||||
self.memory.data_size() as u32
|
||||
}
|
||||
|
||||
/// Reads `__heap_base: i32` global variable and returns it.
|
||||
///
|
||||
/// If it doesn't exist, not a global or of not i32 type returns an error.
|
||||
@@ -291,32 +294,45 @@ fn get_table(instance: &Instance) -> Option<Table> {
|
||||
|
||||
/// Functions related to memory.
|
||||
impl InstanceWrapper {
|
||||
/// Read data from a slice of memory into a destination buffer.
|
||||
/// Read data from a slice of memory into a newly allocated buffer.
|
||||
///
|
||||
/// Returns an error if the read would go out of the memory bounds.
|
||||
pub fn read_memory_into(&self, address: Pointer<u8>, dest: &mut [u8]) -> Result<()> {
|
||||
pub fn read_memory(&self, source_addr: Pointer<u8>, size: usize) -> Result<Vec<u8>> {
|
||||
let range = checked_range(source_addr.into(), size, self.memory.data_size())
|
||||
.ok_or_else(|| Error::Other("memory read is out of bounds".into()))?;
|
||||
|
||||
let mut buffer = vec![0; range.len()];
|
||||
self.read_memory_into(source_addr, &mut buffer)?;
|
||||
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
/// Read data from the instance memory into a slice.
|
||||
///
|
||||
/// Returns an error if the read would go out of the memory bounds.
|
||||
pub fn read_memory_into(&self, source_addr: Pointer<u8>, dest: &mut [u8]) -> Result<()> {
|
||||
unsafe {
|
||||
// This should be safe since we don't grow up memory while caching this reference and
|
||||
// we give up the reference before returning from this function.
|
||||
let memory = self.memory_as_slice();
|
||||
|
||||
let range = util::checked_range(address.into(), dest.len(), memory.len())
|
||||
let range = checked_range(source_addr.into(), dest.len(), memory.len())
|
||||
.ok_or_else(|| Error::Other("memory read is out of bounds".into()))?;
|
||||
dest.copy_from_slice(&memory[range]);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Write data to a slice of memory.
|
||||
/// Write data to the instance memory from a slice.
|
||||
///
|
||||
/// Returns an error if the write would go out of the memory bounds.
|
||||
pub fn write_memory_from(&self, address: Pointer<u8>, data: &[u8]) -> Result<()> {
|
||||
pub fn write_memory_from(&self, dest_addr: Pointer<u8>, data: &[u8]) -> Result<()> {
|
||||
unsafe {
|
||||
// This should be safe since we don't grow up memory while caching this reference and
|
||||
// we give up the reference before returning from this function.
|
||||
let memory = self.memory_as_slice_mut();
|
||||
|
||||
let range = util::checked_range(address.into(), data.len(), memory.len())
|
||||
let range = checked_range(dest_addr.into(), data.len(), memory.len())
|
||||
.ok_or_else(|| Error::Other("memory write is out of bounds".into()))?;
|
||||
memory[range].copy_from_slice(data);
|
||||
Ok(())
|
||||
@@ -442,11 +458,11 @@ impl runtime_blob::InstanceGlobals for InstanceWrapper {
|
||||
}
|
||||
|
||||
fn get_global_value(&self, global: &Self::Global) -> Value {
|
||||
util::from_wasmtime_val(global.get())
|
||||
from_wasmtime_val(global.get())
|
||||
}
|
||||
|
||||
fn set_global_value(&self, global: &Self::Global, value: Value) {
|
||||
global.set(util::into_wasmtime_val(value)).expect(
|
||||
global.set(into_wasmtime_val(value)).expect(
|
||||
"the value is guaranteed to be of the same value; the global is guaranteed to be mutable; qed",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ pub struct WasmtimeInstance {
|
||||
}
|
||||
|
||||
// This is safe because `WasmtimeInstance` does not leak reference to `self.imports`
|
||||
// and all imports don't reference any anything, other than host functions and memory
|
||||
// and all imports don't reference anything, other than host functions and memory
|
||||
unsafe impl Send for WasmtimeInstance {}
|
||||
|
||||
impl WasmInstance for WasmtimeInstance {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::host::{HostContext, HostState};
|
||||
use crate::host::HostState;
|
||||
|
||||
scoped_tls::scoped_thread_local!(static HOST_STATE: HostState);
|
||||
|
||||
@@ -36,10 +36,10 @@ where
|
||||
/// context will be `None`.
|
||||
pub fn with_context<R, F>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(Option<HostContext>) -> R,
|
||||
F: FnOnce(Option<HostState>) -> R,
|
||||
{
|
||||
if !HOST_STATE.is_set() {
|
||||
return f(None)
|
||||
}
|
||||
HOST_STATE.with(|state| f(Some(state.materialize())))
|
||||
HOST_STATE.with(|state| f(Some(state.clone())))
|
||||
}
|
||||
|
||||
@@ -16,21 +16,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::ops::Range;
|
||||
|
||||
use sp_wasm_interface::Value;
|
||||
|
||||
/// Construct a range from an offset to a data length after the offset.
|
||||
/// Returns None if the end of the range would exceed some maximum offset.
|
||||
pub fn checked_range(offset: usize, len: usize, max: usize) -> Option<Range<usize>> {
|
||||
let end = offset.checked_add(len)?;
|
||||
if end <= max {
|
||||
Some(offset..end)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a [`wasmtime::Val`] into a substrate runtime interface [`Value`].
|
||||
///
|
||||
/// Panics if the given value doesn't have a corresponding variant in `Value`.
|
||||
|
||||
Reference in New Issue
Block a user