mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 11:07:56 +00:00
Separate wasmi and wasmer sandbox implementations into their own modules (#10563)
* Moves wasmi specific `ImportResolver` and `MemoryTransfer` impls to submodule * Splits context store environmental, moves impl `Externals` to wasmi backend * Adds wasmer sandbox backend stub module * Move sandbox impl code to backend specific modules * Moves wasmi stuff * Fixes value conversion * Makes it all compile * Remove `with_context_store` * Moves `WasmerBackend` to the impl * Reformat the source * Moves wasmer MemoryWrapper * Reformats the source * Fixes mutability * Moves backend impls to a submodule * Fix visibility * Reformat the source * Feature gate wasmer backend module * Moves wasmi memory allocation to backend module * Rename WasmerBackend to Backend * Refactor dispatch result decoding, get rid of Wasmi types in common sandbox code * Reformat the source * Remove redundant prefixes in backend functions * Remove wasmer-sandbox from default features * Post-review changes * Add conversion soundness proof * Remove redundant prefix * Removes now redundant clone_inner * Add `Error::SandboxBackend`, refactor invoke result * Fix comments * Rename `Error::SandboxBackend` to `Sandbox` * Simplifies logic in `wasmer_backend::invoke` * Fixes memory management
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
|
||||
//! Utilities used by all backends
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::error::Result;
|
||||
use sp_wasm_interface::Pointer;
|
||||
use std::ops::Range;
|
||||
|
||||
@@ -50,192 +50,3 @@ pub trait MemoryTransfer {
|
||||
/// Returns an error if the write would go out of the memory bounds.
|
||||
fn write_from(&self, dest_addr: Pointer<u8>, source: &[u8]) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Safe wrapper over wasmi memory reference
|
||||
pub mod wasmi {
|
||||
use super::*;
|
||||
|
||||
/// Wasmi provides direct access to its memory using slices.
|
||||
///
|
||||
/// This wrapper limits the scope where the slice can be taken to
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MemoryWrapper(::wasmi::MemoryRef);
|
||||
|
||||
impl MemoryWrapper {
|
||||
/// Take ownership of the memory region and return a wrapper object
|
||||
pub fn new(memory: ::wasmi::MemoryRef) -> Self {
|
||||
Self(memory)
|
||||
}
|
||||
|
||||
/// Clone the underlying memory object
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The sole purpose of `MemoryRef` is to protect the memory from uncontrolled
|
||||
/// access. By returning the memory object "as is" we bypass all of the checks.
|
||||
///
|
||||
/// Intended to use only during module initialization.
|
||||
pub unsafe fn clone_inner(&self) -> ::wasmi::MemoryRef {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl super::MemoryTransfer for MemoryWrapper {
|
||||
fn read(&self, source_addr: Pointer<u8>, size: usize) -> Result<Vec<u8>> {
|
||||
self.0.with_direct_access(|source| {
|
||||
let range = checked_range(source_addr.into(), size, source.len())
|
||||
.ok_or_else(|| Error::Other("memory read is out of bounds".into()))?;
|
||||
|
||||
Ok(Vec::from(&source[range]))
|
||||
})
|
||||
}
|
||||
|
||||
fn read_into(&self, source_addr: Pointer<u8>, destination: &mut [u8]) -> Result<()> {
|
||||
self.0.with_direct_access(|source| {
|
||||
let range = checked_range(source_addr.into(), destination.len(), source.len())
|
||||
.ok_or_else(|| Error::Other("memory read is out of bounds".into()))?;
|
||||
|
||||
destination.copy_from_slice(&source[range]);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn write_from(&self, dest_addr: Pointer<u8>, source: &[u8]) -> Result<()> {
|
||||
self.0.with_direct_access_mut(|destination| {
|
||||
let range = checked_range(dest_addr.into(), source.len(), destination.len())
|
||||
.ok_or_else(|| Error::Other("memory write is out of bounds".into()))?;
|
||||
|
||||
destination[range].copy_from_slice(source);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Routines specific to Wasmer runtime. Since sandbox can be invoked from both
|
||||
/// wasmi and wasmtime runtime executors, we need to have a way to deal with sanbox
|
||||
/// backends right from the start.
|
||||
#[cfg(feature = "wasmer-sandbox")]
|
||||
pub mod wasmer {
|
||||
use super::checked_range;
|
||||
use crate::error::{Error, Result};
|
||||
use sp_wasm_interface::Pointer;
|
||||
use std::{cell::RefCell, convert::TryInto, rc::Rc};
|
||||
|
||||
/// In order to enforce memory access protocol to the backend memory
|
||||
/// we wrap it with `RefCell` and encapsulate all memory operations.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MemoryWrapper {
|
||||
buffer: Rc<RefCell<wasmer::Memory>>,
|
||||
}
|
||||
|
||||
impl MemoryWrapper {
|
||||
/// Take ownership of the memory region and return a wrapper object
|
||||
pub fn new(memory: wasmer::Memory) -> Self {
|
||||
Self { buffer: Rc::new(RefCell::new(memory)) }
|
||||
}
|
||||
|
||||
/// Returns linear memory of the wasm instance as a slice.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Wasmer doesn't provide comprehensive documentation about the exact behavior of the data
|
||||
/// pointer. If a dynamic style heap is used the base pointer of the heap can change. Since
|
||||
/// growing, we cannot guarantee the lifetime of the returned slice reference.
|
||||
unsafe fn memory_as_slice(memory: &wasmer::Memory) -> &[u8] {
|
||||
let ptr = memory.data_ptr() as *const _;
|
||||
let len: usize =
|
||||
memory.data_size().try_into().expect("data size should fit into usize");
|
||||
|
||||
if len == 0 {
|
||||
&[]
|
||||
} else {
|
||||
core::slice::from_raw_parts(ptr, len)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns linear memory of the wasm instance as a slice.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// See `[memory_as_slice]`. In addition to those requirements, since a mutable reference is
|
||||
/// returned it must be ensured that only one mutable and no shared references to memory
|
||||
/// exists at the same time.
|
||||
unsafe fn memory_as_slice_mut(memory: &wasmer::Memory) -> &mut [u8] {
|
||||
let ptr = memory.data_ptr();
|
||||
let len: usize =
|
||||
memory.data_size().try_into().expect("data size should fit into usize");
|
||||
|
||||
if len == 0 {
|
||||
&mut []
|
||||
} else {
|
||||
core::slice::from_raw_parts_mut(ptr, len)
|
||||
}
|
||||
}
|
||||
|
||||
/// Clone the underlying memory object
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The sole purpose of `MemoryRef` is to protect the memory from uncontrolled
|
||||
/// access. By returning the memory object "as is" we bypass all of the checks.
|
||||
///
|
||||
/// Intended to use only during module initialization.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if `MemoryRef` is currently in use.
|
||||
pub unsafe fn clone_inner(&mut self) -> wasmer::Memory {
|
||||
// We take exclusive lock to ensure that we're the only one here
|
||||
self.buffer.borrow_mut().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl super::MemoryTransfer for MemoryWrapper {
|
||||
fn read(&self, source_addr: Pointer<u8>, size: usize) -> Result<Vec<u8>> {
|
||||
let memory = self.buffer.borrow();
|
||||
|
||||
let data_size = memory.data_size().try_into().expect("data size does not fit");
|
||||
|
||||
let range = checked_range(source_addr.into(), size, data_size)
|
||||
.ok_or_else(|| Error::Other("memory read is out of bounds".into()))?;
|
||||
|
||||
let mut buffer = vec![0; range.len()];
|
||||
self.read_into(source_addr, &mut buffer)?;
|
||||
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
fn read_into(&self, source_addr: Pointer<u8>, destination: &mut [u8]) -> Result<()> {
|
||||
unsafe {
|
||||
let memory = self.buffer.borrow();
|
||||
|
||||
// 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 source = Self::memory_as_slice(&memory);
|
||||
|
||||
let range = checked_range(source_addr.into(), destination.len(), source.len())
|
||||
.ok_or_else(|| Error::Other("memory read is out of bounds".into()))?;
|
||||
|
||||
destination.copy_from_slice(&source[range]);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_from(&self, dest_addr: Pointer<u8>, source: &[u8]) -> Result<()> {
|
||||
unsafe {
|
||||
let memory = self.buffer.borrow_mut();
|
||||
|
||||
// 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 destination = Self::memory_as_slice_mut(&memory);
|
||||
|
||||
let range = checked_range(dest_addr.into(), source.len(), destination.len())
|
||||
.ok_or_else(|| Error::Other("memory write is out of bounds".into()))?;
|
||||
|
||||
destination[range].copy_from_slice(source);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user