summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/tinywasm/src/interpreter/executor.rs101
-rw-r--r--crates/tinywasm/src/store/memory/instance.rs165
-rw-r--r--crates/tinywasm/src/store/memory/lazy.rs4
-rw-r--r--crates/tinywasm/src/store/memory/mod.rs273
-rw-r--r--crates/tinywasm/src/store/memory/paged.rs148
-rw-r--r--crates/tinywasm/src/store/memory/vec.rs73
6 files changed, 257 insertions, 507 deletions
diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs
index e4bb6c5..8183b55 100644
--- a/crates/tinywasm/src/interpreter/executor.rs
+++ b/crates/tinywasm/src/interpreter/executor.rs
@@ -9,7 +9,7 @@ use alloc::rc::Rc;
use alloc::vec::Vec;
use alloc::sync::Arc;
-use interpreter::stack::CallFrame;
+use interpreter::stack::{CallFrame, ValueStack};
use tinywasm_types::*;
use super::ExecState;
@@ -31,6 +31,19 @@ pub(crate) struct Executor<'store, const BUDGETED: bool> {
}
impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
+ #[inline(always)]
+ fn pop_memory_addr<const N: usize>(
+ stack: &mut ValueStack,
+ mem: &MemoryInstance,
+ offset: u64,
+ ) -> Result<usize, Trap> {
+ if mem.is_64bit() {
+ mem.effective_addr_64::<N>(<i64>::stack_pop(stack) as u64, offset)
+ } else {
+ mem.effective_addr_32::<N>(<i32>::stack_pop(stack) as u32, offset)
+ }
+ }
+
pub(crate) fn new(store: &'store mut Store, cf: CallFrame, call_stack_base: u32) -> Self {
let wasm_func = store.state.get_wasm_func(cf.func_addr);
let module = store.get_module_instance_internal(wasm_func.owner);
@@ -240,18 +253,10 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
}};
}
- let Some(next) = self.func.instructions.get(self.cf.instr_ptr) else {
- unreachable!(
- "Instruction pointer out of bounds: {} ({} instructions)",
- self.cf.instr_ptr,
- self.func.instructions.len()
- )
- };
-
use tinywasm_types::Instruction::*;
#[rustfmt::skip]
- match next {
- Unreachable => return Err(Trap::Unreachable),
+ match &self.func.instructions[self.cf.instr_ptr] {
+ Unreachable => { cold_path(); return Err(Trap::Unreachable) },
Drop32 => { _ = Value32::stack_pop(&mut self.store.value_stack)},
Drop64 => { _ = Value64::stack_pop(&mut self.store.value_stack)},
Drop128 => { _ = Value128::stack_pop(&mut self.store.value_stack)},
@@ -755,10 +760,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
I64x2ExtendHighI32x4S => stack_op!(unary Value128, |a| a.i64x2_extend_high_i32x4_s()),
I64x2ExtendHighI32x4U => stack_op!(unary Value128, |a| a.i64x2_extend_high_i32x4_u()),
I8x16Popcnt => stack_op!(unary Value128, |v| v.i8x16_popcnt()),
- I8x16Shuffle(idx) => {
- let mask = self.func.data.v128_const(*idx);
- stack_op!(binary Value128, |a, b| Value128::i8x16_shuffle(a, b, Value128(mask)))
- },
+ I8x16Shuffle(idx) => stack_op!(binary Value128, |a, b| Value128::i8x16_shuffle(a, b, Value128(self.func.data.v128_const(*idx)))),
I16x8Q15MulrSatS => stack_op!(binary Value128, |a, b| a.i16x8_q15mulr_sat_s(b)),
I32x4DotI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_dot_i16x8_s(b)),
I8x16RelaxedLaneselect => stack_op!(ternary Value128, |a, b, c| Value128::i8x16_relaxed_laneselect(a, b, c)),
@@ -822,7 +824,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
I32x4RelaxedTruncF64x2UZero => stack_op!(unary Value128, |v| v.i32x4_relaxed_trunc_f64x2_u_zero()),
};
- self.cf.incr_instr_ptr();
+ self.cf.instr_ptr += 1;
Ok(None)
}
@@ -985,7 +987,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
};
self.store.value_stack.extend_from_wasmvalues(&res)?;
- self.cf.incr_instr_ptr();
+ self.cf.instr_ptr += 1;
Ok(())
}
@@ -1140,10 +1142,11 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
addr_local: u8,
value_local: u8,
) -> Result<(), Trap> {
- let addr = u64::from(u32::local_get(&self.store.value_stack, &self.cf, u16::from(addr_local)));
- let value = T::local_get(&self.store.value_stack, &self.cf, u16::from(value_local)).to_mem_bytes();
+ let base = u32::local_get(&self.store.value_stack, &self.cf, u16::from(addr_local));
+ let value = T::local_get(&self.store.value_stack, &self.cf, u16::from(value_local));
let mem = self.store.state.get_mem_mut(self.module.resolve_mem_addr(memarg.mem_addr()));
- mem.store(addr, memarg.offset(), value)?;
+ let addr = mem.effective_addr_32::<N>(base, memarg.offset())?;
+ value.store_at(&mut *mem.inner, addr)?;
Ok(())
}
@@ -1161,7 +1164,8 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
let addr = i32::stack_pop(&mut self.store.value_stack);
let fma = acc + lhs * rhs;
let mem = self.store.state.get_mem_mut(self.module.resolve_mem_addr(m.mem_addr()));
- mem.store(addr as u32 as u64, m.offset(), fma.to_mem_bytes())?;
+ let addr = mem.effective_addr_32::<N>(addr as u32, m.offset())?;
+ fma.store_at(&mut *mem.inner, addr)?;
Ok(())
}
@@ -1183,9 +1187,15 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
addr_local: u8,
) -> Result<T, Trap> {
let mem = self.store.state.get_mem(self.module.resolve_mem_addr(memarg.mem_addr()));
- let addr = u64::from(u32::local_get(&self.store.value_stack, &self.cf, u16::from(addr_local)));
- let bytes = mem.load(addr, memarg.offset())?;
- Ok(T::from_mem_bytes(bytes))
+ let base = u32::local_get(&self.store.value_stack, &self.cf, u16::from(addr_local));
+ let addr = mem.effective_addr_32::<N>(base, memarg.offset())?;
+ match T::load_at(&*mem.inner, addr) {
+ Ok(res) => Ok(res),
+ Err(err) => {
+ cold_path();
+ Err(err)
+ }
+ }
}
fn exec_load_local_tee<T: InternalValue + MemValue<N>, const N: usize>(
@@ -1375,20 +1385,11 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
lane: u8,
) -> Result<(), Trap> {
let mem = self.store.state.get_mem(self.module.resolve_mem_addr(mem_addr));
- let base = match mem.is_64bit() {
- true => <i64>::stack_pop(&mut self.store.value_stack) as u64,
- false => <i32>::stack_pop(&mut self.store.value_stack) as u32 as u64,
- };
- let val = match mem.load::<LOAD_SIZE>(base, offset) {
- Ok(val) => val,
- Err(e) => {
- cold_path();
- return Err(e);
- }
- };
+ let addr = Self::pop_memory_addr::<LOAD_SIZE>(&mut self.store.value_stack, mem, offset)?;
+ let val = LOAD::load_at(&*mem.inner, addr)?;
let offset = lane as usize * LOAD_SIZE;
let mut imm = <Value128>::stack_pop(&mut self.store.value_stack).to_mem_bytes();
- imm[offset..offset + LOAD_SIZE].copy_from_slice(&val);
+ imm[offset..offset + LOAD_SIZE].copy_from_slice(&val.to_mem_bytes());
self.store.value_stack.push(Value128(imm))?;
Ok(())
}
@@ -1401,12 +1402,9 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
cast: impl Fn(LOAD) -> TARGET,
) -> Result<(), Trap> {
let mem = self.store.state.get_mem(self.module.resolve_mem_addr(mem_addr));
- let base = match mem.is_64bit() {
- true => <i64>::stack_pop(&mut self.store.value_stack) as u64,
- false => <i32>::stack_pop(&mut self.store.value_stack) as u32 as u64,
- };
+ let addr = Self::pop_memory_addr::<LOAD_SIZE>(&mut self.store.value_stack, mem, offset)?;
- match LOAD::load(&*mem.inner, base, offset) {
+ match LOAD::load_at(&*mem.inner, addr) {
Ok(val) => {
self.store.value_stack.push(cast(val))?;
Ok(())
@@ -1426,15 +1424,13 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
) -> Result<(), Trap> {
let bytes = <Value128>::stack_pop(&mut self.store.value_stack).to_mem_bytes();
let lane_offset = lane as usize * N;
- let mut val = [0u8; N];
- val.copy_from_slice(&bytes[lane_offset..lane_offset + N]);
+ let mut val_bytes = [0u8; N];
+ val_bytes.copy_from_slice(&bytes[lane_offset..lane_offset + N]);
+ let val = U::from_mem_bytes(val_bytes);
let mem_addr = self.module.resolve_mem_addr(mem_addr);
let mem = self.store.state.get_mem_mut(mem_addr);
- let addr = match mem.is_64bit() {
- true => <i64>::stack_pop(&mut self.store.value_stack) as u64,
- false => <i32>::stack_pop(&mut self.store.value_stack) as u32 as u64,
- };
- match mem.store(addr, offset, val) {
+ let addr = Self::pop_memory_addr::<N>(&mut self.store.value_stack, mem, offset)?;
+ match val.store_at(&mut *mem.inner, addr) {
Ok(()) => Ok(()),
Err(e) => {
cold_path();
@@ -1450,15 +1446,12 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
cast: impl Fn(T) -> U,
) -> Result<(), Trap> {
let val = <T>::stack_pop(&mut self.store.value_stack);
- let val = cast(val).to_mem_bytes();
+ let val = cast(val);
let mem_addr = self.module.resolve_mem_addr(mem_addr);
let mem = self.store.state.get_mem_mut(mem_addr);
- let addr = match mem.is_64bit() {
- true => <i64>::stack_pop(&mut self.store.value_stack) as u64,
- false => <i32>::stack_pop(&mut self.store.value_stack) as u32 as u64,
- };
- match mem.store(addr, offset, val) {
+ let addr = Self::pop_memory_addr::<N>(&mut self.store.value_stack, mem, offset)?;
+ match val.store_at(&mut *mem.inner, addr) {
Ok(()) => Ok(()),
Err(e) => {
cold_path();
diff --git a/crates/tinywasm/src/store/memory/instance.rs b/crates/tinywasm/src/store/memory/instance.rs
index 9a3d462..be0f3b4 100644
--- a/crates/tinywasm/src/store/memory/instance.rs
+++ b/crates/tinywasm/src/store/memory/instance.rs
@@ -9,16 +9,53 @@ use core::hint::cold_path;
/// A WebAssembly Memory Instance
///
/// See <https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances>
-#[cfg_attr(feature = "debug", derive(Debug))]
pub(crate) struct MemoryInstance {
pub(crate) kind: MemoryType,
pub(crate) inner: MemoryStorage,
pub(crate) page_count: usize,
}
+#[cfg(feature = "debug")]
+impl core::fmt::Debug for MemoryInstance {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ f.debug_struct("MemoryInstance").field("kind", &self.kind).field("page_count", &self.page_count).finish()
+ }
+}
+
impl MemoryInstance {
const COPY_CHUNK_SIZE: usize = 4 * 1024;
+ #[inline(always)]
+ pub(crate) fn effective_addr_32<const N: usize>(&self, base: u32, offset: u64) -> Result<usize, Trap> {
+ #[cfg(target_pointer_width = "64")]
+ {
+ debug_assert!(u32::try_from(offset).is_ok(), "validated memory32 offsets fit in u32");
+ Ok(base as usize + offset as usize)
+ }
+
+ #[cfg(not(target_pointer_width = "64"))]
+ {
+ match usize::try_from(u64::from(base) + offset) {
+ Ok(addr) => Ok(addr),
+ Err(_) => {
+ cold_path();
+ Err(memory_oob(base as usize, N, self.inner.len()))
+ }
+ }
+ }
+ }
+
+ #[inline(always)]
+ pub(crate) fn effective_addr_64<const N: usize>(&self, base: u64, offset: u64) -> Result<usize, Trap> {
+ match base.checked_add(offset).and_then(|addr| usize::try_from(addr).ok()) {
+ Some(addr) => Ok(addr),
+ None => {
+ cold_path();
+ Err(memory_oob(base as usize, N, self.inner.len()))
+ }
+ }
+ }
+
pub(crate) fn new(kind: MemoryType, backend: &MemoryBackend) -> Result<Self> {
assert!(kind.page_count_initial() <= kind.page_count_max());
@@ -62,132 +99,6 @@ impl MemoryInstance {
matches!(self.kind.arch(), MemoryArch::I64)
}
- #[inline(always)]
- pub(crate) fn load<const SIZE: usize>(&self, base: u64, offset: u64) -> Result<[u8; SIZE], Trap> {
- // the compiler doesn't optimize .as_slice().try_into() for some reason, so we have to manually copy the bytes into an array
- // heavy usage of cold_path() seems to help a lot from looking at profile data
- match SIZE {
- 1 => {
- let res = match self.inner.read_8(base, offset) {
- Ok(bytes) => bytes,
- Err(e) => {
- cold_path();
- return Err(e);
- }
- };
- let mut bytes = [0; SIZE];
- bytes[0] = res;
- Ok(bytes)
- }
- 2 => {
- let res = match self.inner.read_16(base, offset) {
- Ok(bytes) => bytes,
- Err(e) => {
- cold_path();
- return Err(e);
- }
- };
- let mut bytes = [0; SIZE];
- bytes[0] = res[0];
- bytes[1] = res[1];
- Ok(bytes)
- }
- 4 => {
- let mut bytes = [0; SIZE];
- let res = match self.inner.read_32(base, offset) {
- Ok(bytes) => bytes,
- Err(e) => {
- cold_path();
- return Err(e);
- }
- };
- bytes[0] = res[0];
- bytes[1] = res[1];
- bytes[2] = res[2];
- bytes[3] = res[3];
- Ok(bytes)
- }
- 8 => {
- let mut bytes = [0; SIZE];
- let res = match self.inner.read_64(base, offset) {
- Ok(bytes) => bytes,
- Err(e) => {
- cold_path();
- return Err(e);
- }
- };
- bytes[0] = res[0];
- bytes[1] = res[1];
- bytes[2] = res[2];
- bytes[3] = res[3];
- bytes[4] = res[4];
- bytes[5] = res[5];
- bytes[6] = res[6];
- bytes[7] = res[7];
- Ok(bytes)
- }
- 16 => {
- let mut bytes = [0; SIZE];
- let res = match self.inner.read_128(base, offset) {
- Ok(bytes) => bytes,
- Err(e) => {
- cold_path();
- return Err(e);
- }
- };
- bytes[0] = res[0];
- bytes[1] = res[1];
- bytes[2] = res[2];
- bytes[3] = res[3];
- bytes[4] = res[4];
- bytes[5] = res[5];
- bytes[6] = res[6];
- bytes[7] = res[7];
- bytes[8] = res[8];
- bytes[9] = res[9];
- bytes[10] = res[10];
- bytes[11] = res[11];
- bytes[12] = res[12];
- bytes[13] = res[13];
- bytes[14] = res[14];
- bytes[15] = res[15];
- Ok(bytes)
- }
- _ => unreachable!("unsupported fixed-size read width {SIZE}"),
- }
- }
-
- #[inline(always)]
- pub(crate) fn store<const SIZE: usize>(&mut self, base: u64, offset: u64, bytes: [u8; SIZE]) -> Result<(), Trap> {
- // the compiler doesn't optimize .as_slice().try_into() for some reason, so we have to manually copy the bytes into an array
- // heavy usage of cold_path() seems to help a lot from looking at profile data
- let res = match SIZE {
- 1 => self.inner.write_8(base, offset, bytes[0]),
- 2 => self.inner.write_16(base, offset, [bytes[0], bytes[1]]),
- 4 => self.inner.write_32(base, offset, [bytes[0], bytes[1], bytes[2], bytes[3]]),
- 8 => self.inner.write_64(
- base,
- offset,
- [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]],
- ),
- 16 => self.inner.write_128(
- base,
- offset,
- [
- bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9],
- bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
- ],
- ),
- _ => unreachable!("unsupported fixed-size write width {SIZE}"),
- };
-
- if let Err(e) = res {
- cold_path();
- return Err(e);
- }
- Ok(())
- }
-
pub(crate) fn copy_from_memory(
&mut self,
dst: usize,
diff --git a/crates/tinywasm/src/store/memory/lazy.rs b/crates/tinywasm/src/store/memory/lazy.rs
index 2bcce09..ef6dace 100644
--- a/crates/tinywasm/src/store/memory/lazy.rs
+++ b/crates/tinywasm/src/store/memory/lazy.rs
@@ -33,7 +33,7 @@ impl LazyLinearMemory {
fn materialize(&mut self) -> &mut dyn LinearMemory {
if self.inner.is_none() {
self.inner =
- Some(self.backend.create(self.ty, self.initial_len).expect("lazy memory materialization failed").0);
+ Some(self.backend.create(self.ty, self.initial_len).expect("lazy memory materialization failed"));
}
self.inner.as_deref_mut().expect("lazy memory should be materialized")
}
@@ -48,7 +48,7 @@ impl LazyLinearMemory {
}
Err(err) => panic!("lazy memory materialization failed: {err}"),
};
- self.inner = Some(storage.0);
+ self.inner = Some(storage);
}
Ok(self.inner.as_deref_mut().expect("lazy memory should be materialized"))
}
diff --git a/crates/tinywasm/src/store/memory/mod.rs b/crates/tinywasm/src/store/memory/mod.rs
index 84ed1e5..57783d2 100644
--- a/crates/tinywasm/src/store/memory/mod.rs
+++ b/crates/tinywasm/src/store/memory/mod.rs
@@ -2,7 +2,6 @@ use alloc::{boxed::Box, format, sync::Arc};
use alloc::{vec, vec::Vec};
use core::cmp::min;
use core::hint::cold_path;
-use core::ops::{Deref, DerefMut};
use tinywasm_types::MemoryType;
@@ -53,8 +52,13 @@ pub trait LinearMemory {
/// Writes all bytes in `src` starting at `addr`, or returns `None` if any byte could not be written.
fn write_all(&mut self, addr: usize, src: &[u8]) -> Option<()> {
- let end = addr.checked_add(src.len())?;
+ let Some(end) = addr.checked_add(src.len()) else {
+ cold_path();
+ return None;
+ };
+
if end > self.len() {
+ cold_path();
return None;
}
@@ -62,6 +66,7 @@ pub trait LinearMemory {
while offset < src.len() {
let written = self.write(addr + offset, &src[offset..]);
if written == 0 {
+ cold_path();
return None;
}
offset += written;
@@ -127,8 +132,13 @@ pub trait LinearMemory {
/// Reads exactly `dst.len()` bytes starting at `addr`.
fn read_exact(&self, addr: usize, dst: &mut [u8]) -> Option<()> {
- let end = addr.checked_add(dst.len())?;
+ let Some(end) = addr.checked_add(dst.len()) else {
+ cold_path();
+ return None;
+ };
+
if end > self.len() {
+ cold_path();
return None;
}
@@ -136,6 +146,7 @@ pub trait LinearMemory {
while offset < dst.len() {
let read = self.read(addr + offset, &mut dst[offset..]);
if read == 0 {
+ cold_path();
return None;
}
offset += read;
@@ -156,20 +167,18 @@ pub trait LinearMemory {
Some(data)
}
- /// Reads exactly 1 byte at the effective address `base + offset`.
- fn read_8(&self, base: u64, offset: u64) -> core::result::Result<u8, crate::Trap> {
- let addr = checked_effective_addr::<1>(self.len(), base, offset)?;
+ /// Reads exactly 1 byte at `addr`.
+ fn read_8(&self, addr: usize) -> core::result::Result<[u8; 1], crate::Trap> {
let mut bytes = [0; 1];
self.read_exact(addr, &mut bytes).ok_or_else(|| {
cold_path();
memory_oob(addr, 1, self.len())
})?;
- Ok(bytes[0])
+ Ok(bytes)
}
- /// Reads exactly 2 bytes at the effective address `base + offset`.
- fn read_16(&self, base: u64, offset: u64) -> core::result::Result<[u8; 2], crate::Trap> {
- let addr = checked_effective_addr::<2>(self.len(), base, offset)?;
+ /// Reads exactly 2 bytes at `addr`.
+ fn read_16(&self, addr: usize) -> core::result::Result<[u8; 2], crate::Trap> {
let mut bytes = [0; 2];
self.read_exact(addr, &mut bytes).ok_or_else(|| {
cold_path();
@@ -178,9 +187,8 @@ pub trait LinearMemory {
Ok(bytes)
}
- /// Reads exactly 4 bytes at the effective address `base + offset`.
- fn read_32(&self, base: u64, offset: u64) -> core::result::Result<[u8; 4], crate::Trap> {
- let addr = checked_effective_addr::<4>(self.len(), base, offset)?;
+ /// Reads exactly 4 bytes at `addr`.
+ fn read_32(&self, addr: usize) -> core::result::Result<[u8; 4], crate::Trap> {
let mut bytes = [0; 4];
self.read_exact(addr, &mut bytes).ok_or_else(|| {
cold_path();
@@ -189,9 +197,8 @@ pub trait LinearMemory {
Ok(bytes)
}
- /// Reads exactly 8 bytes at the effective address `base + offset`.
- fn read_64(&self, base: u64, offset: u64) -> core::result::Result<[u8; 8], crate::Trap> {
- let addr = checked_effective_addr::<8>(self.len(), base, offset)?;
+ /// Reads exactly 8 bytes at `addr`.
+ fn read_64(&self, addr: usize) -> core::result::Result<[u8; 8], crate::Trap> {
let mut bytes = [0; 8];
self.read_exact(addr, &mut bytes).ok_or_else(|| {
cold_path();
@@ -200,9 +207,8 @@ pub trait LinearMemory {
Ok(bytes)
}
- /// Reads exactly 16 bytes at the effective address `base + offset`.
- fn read_128(&self, base: u64, offset: u64) -> core::result::Result<[u8; 16], crate::Trap> {
- let addr = checked_effective_addr::<16>(self.len(), base, offset)?;
+ /// Reads exactly 16 bytes at `addr`.
+ fn read_128(&self, addr: usize) -> core::result::Result<[u8; 16], crate::Trap> {
let mut bytes = [0; 16];
self.read_exact(addr, &mut bytes).ok_or_else(|| {
cold_path();
@@ -211,44 +217,41 @@ pub trait LinearMemory {
Ok(bytes)
}
- /// Writes exactly 1 byte at the effective address `base + offset`.
- fn write_8(&mut self, base: u64, offset: u64, byte: u8) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<1>(self.len(), base, offset)?;
- self.write(addr, &[byte]);
- Ok(())
+ /// Writes exactly 1 byte at `addr`.
+ fn write_8(&mut self, addr: usize, bytes: &[u8]) -> core::result::Result<(), crate::Trap> {
+ self.write_all(addr, bytes).ok_or_else(|| {
+ cold_path();
+ memory_oob(addr, 1, self.len())
+ })
}
- /// Writes exactly 2 bytes at the effective address `base + offset`.
- fn write_16(&mut self, base: u64, offset: u64, bytes: [u8; 2]) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<2>(self.len(), base, offset)?;
- self.write_all(addr, &bytes).ok_or_else(|| {
+ /// Writes exactly 2 bytes at `addr`.
+ fn write_16(&mut self, addr: usize, bytes: &[u8]) -> core::result::Result<(), crate::Trap> {
+ self.write_all(addr, bytes).ok_or_else(|| {
cold_path();
memory_oob(addr, 2, self.len())
})
}
- /// Writes exactly 4 bytes at the effective address `base + offset`.
- fn write_32(&mut self, base: u64, offset: u64, bytes: [u8; 4]) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<4>(self.len(), base, offset)?;
- self.write_all(addr, &bytes).ok_or_else(|| {
+ /// Writes exactly 4 bytes at `addr`.
+ fn write_32(&mut self, addr: usize, bytes: &[u8]) -> core::result::Result<(), crate::Trap> {
+ self.write_all(addr, bytes).ok_or_else(|| {
cold_path();
memory_oob(addr, 4, self.len())
})
}
- /// Writes exactly 8 bytes at the effective address `base + offset`.
- fn write_64(&mut self, base: u64, offset: u64, bytes: [u8; 8]) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<8>(self.len(), base, offset)?;
- self.write_all(addr, &bytes).ok_or_else(|| {
+ /// Writes exactly 8 bytes at `addr`.
+ fn write_64(&mut self, addr: usize, bytes: &[u8]) -> core::result::Result<(), crate::Trap> {
+ self.write_all(addr, bytes).ok_or_else(|| {
cold_path();
memory_oob(addr, 8, self.len())
})
}
- /// Writes exactly 16 bytes at the effective address `base + offset`.
- fn write_128(&mut self, base: u64, offset: u64, bytes: [u8; 16]) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<16>(self.len(), base, offset)?;
- self.write_all(addr, &bytes).ok_or_else(|| {
+ /// Writes exactly 16 bytes at `addr`.
+ fn write_128(&mut self, addr: usize, bytes: &[u8]) -> core::result::Result<(), crate::Trap> {
+ self.write_all(addr, bytes).ok_or_else(|| {
cold_path();
memory_oob(addr, 16, self.len())
})
@@ -259,12 +262,10 @@ type MemoryFactory = dyn Fn(MemoryType) -> Result<Box<dyn LinearMemory>> + Send
/// Configures how runtime memory instances are created.
#[derive(Clone, Default)]
-pub struct MemoryBackend {
- kind: MemoryBackendKind,
-}
+pub struct MemoryBackend(MemoryBackendInner);
#[derive(Clone, Default)]
-enum MemoryBackendKind {
+enum MemoryBackendInner {
#[default]
Vec,
Paged {
@@ -279,7 +280,7 @@ impl MemoryBackend {
/// This is usually the fastest option for reads and writes, but large grows can be expensive
/// because they may reallocate and copy the entire buffer.
pub const fn vec() -> Self {
- Self { kind: MemoryBackendKind::Vec }
+ Self(MemoryBackendInner::Vec)
}
/// Uses sparse chunked storage for each memory instance.
@@ -290,7 +291,7 @@ impl MemoryBackend {
/// little more work and may be slightly slower.
pub fn paged(chunk_size: usize) -> Self {
assert!(chunk_size != 0, "chunk_size must be greater than zero");
- Self { kind: MemoryBackendKind::Paged { chunk_size } }
+ Self(MemoryBackendInner::Paged { chunk_size })
}
/// Uses a custom factory to create memory instances.
@@ -299,23 +300,21 @@ impl MemoryBackend {
F: Fn(MemoryType) -> Result<M> + Send + Sync + 'static,
M: LinearMemory + 'static,
{
- Self {
- kind: MemoryBackendKind::Custom(Arc::new(move |ty| {
- let memory = factory(ty)?;
- Ok(Box::new(memory) as Box<dyn LinearMemory>)
- })),
- }
+ Self(MemoryBackendInner::Custom(Arc::new(move |ty| {
+ let memory = factory(ty)?;
+ Ok(Box::new(memory) as Box<dyn LinearMemory>)
+ })))
}
pub(crate) fn create(&self, ty: MemoryType, initial_len: usize) -> Result<MemoryStorage> {
- let storage = match &self.kind {
- MemoryBackendKind::Vec => {
+ let storage = match &self.0 {
+ MemoryBackendInner::Vec => {
Box::new(VecMemory::try_new(initial_len).map_err(Error::Trap)?) as Box<dyn LinearMemory>
}
- MemoryBackendKind::Paged { chunk_size } => {
+ MemoryBackendInner::Paged { chunk_size } => {
Box::new(PagedMemory::try_new(initial_len, *chunk_size).map_err(Error::Trap)?) as Box<dyn LinearMemory>
}
- MemoryBackendKind::Custom(factory) => factory(ty)?,
+ MemoryBackendInner::Custom(factory) => factory(ty)?,
};
if storage.len() < initial_len {
@@ -325,152 +324,89 @@ impl MemoryBackend {
)));
}
- Ok(MemoryStorage(storage))
+ Ok(storage)
}
pub(crate) fn create_lazy(&self, ty: MemoryType, initial_len: usize) -> Result<MemoryStorage> {
- Ok(MemoryStorage(Box::new(LazyLinearMemory::new_with_initial_len(ty, initial_len, self.clone()))))
+ Ok(Box::new(LazyLinearMemory::new_with_initial_len(ty, initial_len, self.clone())))
}
}
#[cfg(feature = "debug")]
impl core::fmt::Debug for MemoryBackend {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- match &self.kind {
- MemoryBackendKind::Vec => f.debug_tuple("MemoryBackend::Vec").finish(),
- MemoryBackendKind::Paged { chunk_size } => {
+ match &self.0 {
+ MemoryBackendInner::Vec => f.debug_tuple("MemoryBackend::Vec").finish(),
+ MemoryBackendInner::Paged { chunk_size } => {
f.debug_struct("MemoryBackend::Paged").field("chunk_size", chunk_size).finish()
}
- MemoryBackendKind::Custom(_) => f.debug_tuple("MemoryBackend::Custom").finish(),
+ MemoryBackendInner::Custom(_) => f.debug_tuple("MemoryBackend::Custom").finish(),
}
}
}
-pub(crate) struct MemoryStorage(Box<dyn LinearMemory>);
-
-impl Deref for MemoryStorage {
- type Target = dyn LinearMemory;
-
- #[inline(always)]
- fn deref(&self) -> &Self::Target {
- &*self.0
- }
-}
-
-impl DerefMut for MemoryStorage {
- #[inline(always)]
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut *self.0
- }
-}
-
-#[cfg(feature = "debug")]
-impl core::fmt::Debug for MemoryStorage {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- f.debug_tuple("MemoryStorage").field(&format!("{} bytes", self.len())).finish()
- }
-}
+pub(crate) type MemoryStorage = Box<dyn LinearMemory>;
/// A trait for types that can be converted to and from static byte arrays
pub(crate) trait MemValue<const N: usize>: Copy + Default {
/// Store a value in memory
fn to_mem_bytes(self) -> [u8; N];
- /// Load a value from memory
fn from_mem_bytes(bytes: [u8; N]) -> Self;
- fn load(mem: &dyn LinearMemory, base: u64, offset: u64) -> core::result::Result<Self, crate::Trap>;
+ fn load_at(mem: &dyn LinearMemory, addr: usize) -> core::result::Result<Self, crate::Trap>;
+
+ fn store_at(self, mem: &mut dyn LinearMemory, addr: usize) -> core::result::Result<(), crate::Trap>;
}
macro_rules! impl_mem_traits {
- ($($ty:ty, $size:expr, $read:ident),* $(,)?) => {
+ ($($ty:ty, $size:expr, $read:ident, $write:ident),* $(,)?) => {
$(
impl MemValue<$size> for $ty {
#[inline(always)]
+ fn to_mem_bytes(self) -> [u8; $size] {
+ self.to_le_bytes()
+ }
+
+ #[inline(always)]
fn from_mem_bytes(bytes: [u8; $size]) -> Self {
- <$ty>::from_le_bytes(bytes)
+ Self::from_le_bytes(bytes)
}
#[inline(always)]
- fn to_mem_bytes(self) -> [u8; $size] {
- self.to_le_bytes()
+ fn load_at(mem: &dyn LinearMemory, addr: usize) -> core::result::Result<Self, crate::Trap> {
+ Ok(Self::from_mem_bytes(match mem.$read(addr) {
+ Ok(bytes) => bytes,
+ Err(trap) => {
+ cold_path();
+ return Err(trap);
+ }
+ }))
}
#[inline(always)]
- fn load(
- mem: &dyn LinearMemory,
- base: u64,
- offset: u64,
- ) -> core::result::Result<Self, crate::Trap> {
- Ok(Self::from_mem_bytes(mem.$read(base, offset)?))
+ fn store_at(
+ self,
+ mem: &mut dyn LinearMemory,
+ addr: usize,
+ ) -> core::result::Result<(), crate::Trap> {
+ mem.$write(addr, &self.to_mem_bytes())
}
}
)*
};
}
-impl MemValue<1> for u8 {
- #[inline(always)]
- fn from_mem_bytes(bytes: [u8; 1]) -> Self {
- bytes[0]
- }
-
- #[inline(always)]
- fn to_mem_bytes(self) -> [u8; 1] {
- [self]
- }
-
- #[inline(always)]
- fn load(mem: &dyn LinearMemory, base: u64, offset: u64) -> core::result::Result<Self, crate::Trap> {
- mem.read_8(base, offset)
- }
-}
-
-impl MemValue<1> for i8 {
- #[inline(always)]
- fn from_mem_bytes(bytes: [u8; 1]) -> Self {
- i8::from_le_bytes(bytes)
- }
-
- #[inline(always)]
- fn to_mem_bytes(self) -> [u8; 1] {
- self.to_le_bytes()
- }
-
- #[inline(always)]
- fn load(mem: &dyn LinearMemory, base: u64, offset: u64) -> core::result::Result<Self, crate::Trap> {
- Ok(mem.read_8(base, offset)? as i8)
- }
-}
-
impl_mem_traits!(
- u16, 2, read_16, i16, 2, read_16, u32, 4, read_32, i32, 4, read_32, f32, 4, read_32, u64, 8, read_64, i64, 8,
- read_64, f64, 8, read_64, Value128, 16, read_128
+ u8, 1, read_8, write_8, i8, 1, read_8, write_8, u16, 2, read_16, write_16, i16, 2, read_16, write_16, u32, 4,
+ read_32, write_32, i32, 4, read_32, write_32, f32, 4, read_32, write_32, u64, 8, read_64, write_64, i64, 8,
+ read_64, write_64, f64, 8, read_64, write_64, Value128, 16, read_128, write_128
);
fn memory_oob(offset: usize, len: usize, max: usize) -> crate::Trap {
crate::Trap::MemoryOutOfBounds { offset, len, max }
}
-fn checked_effective_addr<const LEN: usize>(
- max: usize,
- base: u64,
- offset: u64,
-) -> core::result::Result<usize, crate::Trap> {
- let Some(max_addr) = max.checked_sub(LEN).map(|max_addr| max_addr as u64) else {
- cold_path();
- return Err(memory_oob(usize::try_from(base).unwrap_or(usize::MAX), LEN, max));
- };
-
- let addr = base.wrapping_add(offset);
- if addr < base || addr > max_addr {
- cold_path();
- return Err(memory_oob(usize::try_from(addr).unwrap_or(usize::MAX), LEN, max));
- }
-
- Ok(addr as usize)
-}
-
#[cfg(test)]
mod tests {
use super::*;
@@ -485,6 +421,23 @@ mod tests {
}
#[test]
+ fn effective_memory_addresses_handle_host_and_wasm_overflow() {
+ let memory = create_test_memory(MemoryType::new(MemoryArch::I32, 1, Some(1), None), MemoryBackend::vec());
+ assert_eq!(memory.effective_addr_32::<1>(1, 2), Ok(3));
+ assert_eq!(memory.effective_addr_64::<1>(1, 2), Ok(3));
+ assert!(memory.effective_addr_64::<1>(u64::MAX, 1).is_err());
+
+ #[cfg(target_pointer_width = "64")]
+ assert_eq!(
+ memory.effective_addr_32::<1>(u32::MAX, u64::from(u32::MAX)),
+ Ok(usize::try_from(u64::from(u32::MAX) * 2).unwrap())
+ );
+
+ #[cfg(target_pointer_width = "32")]
+ assert!(memory.effective_addr_32::<1>(u32::MAX, 1).is_err());
+ }
+
+ #[test]
fn memory_copy_from_slice_and_read_vec_work() {
for backend in test_backends() {
let kind = MemoryType::new(MemoryArch::I32, 1, Some(2), None);
@@ -519,6 +472,20 @@ mod tests {
}
#[test]
+ fn fixed_width_access_out_of_bounds_traps() {
+ for backend in test_backends() {
+ let kind = MemoryType::new(MemoryArch::I32, 1, Some(2), None);
+ let mut memory = create_test_memory(kind, backend);
+ let len = memory.inner.len();
+
+ assert!(memory.inner.read_8(len).is_err());
+ assert!(memory.inner.read_32(len - 3).is_err());
+ assert!(memory.inner.write_8(len, &[0]).is_err());
+ assert!(memory.inner.write_32(len - 3, &[0; 4]).is_err());
+ }
+ }
+
+ #[test]
fn memory_fill_works() {
for backend in test_backends() {
let kind = MemoryType::new(MemoryArch::I32, 1, Some(2), None);
diff --git a/crates/tinywasm/src/store/memory/paged.rs b/crates/tinywasm/src/store/memory/paged.rs
index fc6b778..6ae65af 100644
--- a/crates/tinywasm/src/store/memory/paged.rs
+++ b/crates/tinywasm/src/store/memory/paged.rs
@@ -3,7 +3,7 @@ use alloc::vec::Vec;
use core::cmp::min;
use core::hint::cold_path;
-use super::{LinearMemory, checked_effective_addr};
+use super::{LinearMemory, memory_oob};
/// A sparse chunked linear memory.
///
@@ -275,151 +275,25 @@ impl LinearMemory for PagedMemory {
}
#[inline(always)]
- fn read_8(&self, base: u64, offset: u64) -> core::result::Result<u8, crate::Trap> {
- let addr = checked_effective_addr::<1>(self.len, base, offset)?;
- let chunk_idx = addr >> self.chunk_shift;
- let chunk_offset = addr & self.chunk_mask;
- Ok(self.chunk_slice(chunk_idx).map_or(0, |chunk| chunk[chunk_offset]))
- }
-
- #[inline(always)]
- fn read_16(&self, base: u64, offset: u64) -> core::result::Result<[u8; 2], crate::Trap> {
- let addr = checked_effective_addr::<2>(self.len, base, offset)?;
- let chunk_idx = addr >> self.chunk_shift;
- let chunk_offset = addr & self.chunk_mask;
- if chunk_offset + 2 <= self.chunk_size {
- return Ok(match self.chunk_slice(chunk_idx) {
- Some(chunk) => chunk[chunk_offset..chunk_offset + 2].try_into().unwrap_or_else(|_| unreachable!()),
- None => [0; 2],
- });
- }
-
- let mut bytes = [0; 2];
- self.read_exact(addr, &mut bytes).unwrap();
- Ok(bytes)
- }
-
- #[inline(always)]
- fn read_32(&self, base: u64, offset: u64) -> core::result::Result<[u8; 4], crate::Trap> {
- let addr = checked_effective_addr::<4>(self.len, base, offset)?;
- let chunk_idx = addr >> self.chunk_shift;
- let chunk_offset = addr & self.chunk_mask;
- if chunk_offset + 4 <= self.chunk_size {
- return Ok(match self.chunk_slice(chunk_idx) {
- Some(chunk) => chunk[chunk_offset..chunk_offset + 4].try_into().unwrap_or_else(|_| unreachable!()),
- None => [0; 4],
- });
- }
-
- let mut bytes = [0; 4];
- self.read_exact(addr, &mut bytes).unwrap();
- Ok(bytes)
- }
-
- #[inline(always)]
- fn read_64(&self, base: u64, offset: u64) -> core::result::Result<[u8; 8], crate::Trap> {
- let addr = checked_effective_addr::<8>(self.len, base, offset)?;
- let chunk_idx = addr >> self.chunk_shift;
- let chunk_offset = addr & self.chunk_mask;
- if chunk_offset + 8 <= self.chunk_size {
- return Ok(match self.chunk_slice(chunk_idx) {
- Some(chunk) => chunk[chunk_offset..chunk_offset + 8].try_into().unwrap_or_else(|_| unreachable!()),
- None => [0; 8],
- });
- }
-
- let mut bytes = [0; 8];
- self.read_exact(addr, &mut bytes).unwrap();
- Ok(bytes)
- }
-
- #[inline(always)]
- fn read_128(&self, base: u64, offset: u64) -> core::result::Result<[u8; 16], crate::Trap> {
- let addr = checked_effective_addr::<16>(self.len, base, offset)?;
- let chunk_idx = addr >> self.chunk_shift;
- let chunk_offset = addr & self.chunk_mask;
- if chunk_offset + 16 <= self.chunk_size {
- return Ok(match self.chunk_slice(chunk_idx) {
- Some(chunk) => chunk[chunk_offset..chunk_offset + 16].try_into().unwrap_or_else(|_| unreachable!()),
- None => [0; 16],
- });
- }
-
- let mut bytes = [0; 16];
- self.read_exact(addr, &mut bytes).unwrap();
- Ok(bytes)
- }
-
- #[inline(always)]
- fn write_8(&mut self, base: u64, offset: u64, byte: u8) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<1>(self.len, base, offset)?;
- let chunk_idx = addr >> self.chunk_shift;
- let chunk_offset = addr & self.chunk_mask;
- self.chunk_mut(chunk_idx)?[chunk_offset] = byte;
- Ok(())
- }
-
- #[inline(always)]
- fn write_16(&mut self, base: u64, offset: u64, bytes: [u8; 2]) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<2>(self.len, base, offset)?;
- let chunk_idx = addr >> self.chunk_shift;
- let chunk_offset = addr & self.chunk_mask;
- if chunk_offset + 2 <= self.chunk_size {
- self.chunk_mut(chunk_idx)?[chunk_offset..chunk_offset + 2].copy_from_slice(&bytes);
- } else {
- if self.write_all(addr, &bytes).is_none() {
- cold_path();
- return Err(crate::Trap::OutOfMemory);
- }
+ fn read_8(&self, addr: usize) -> core::result::Result<[u8; 1], crate::Trap> {
+ if addr >= self.len {
+ cold_path();
+ return Err(memory_oob(addr, 1, self.len));
}
- Ok(())
- }
-
- #[inline(always)]
- fn write_32(&mut self, base: u64, offset: u64, bytes: [u8; 4]) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<4>(self.len, base, offset)?;
let chunk_idx = addr >> self.chunk_shift;
let chunk_offset = addr & self.chunk_mask;
- if chunk_offset + 4 <= self.chunk_size {
- self.chunk_mut(chunk_idx)?[chunk_offset..chunk_offset + 4].copy_from_slice(&bytes);
- } else {
- if self.write_all(addr, &bytes).is_none() {
- cold_path();
- return Err(crate::Trap::OutOfMemory);
- }
- }
- Ok(())
+ Ok([self.chunk_slice(chunk_idx).map_or(0, |chunk| chunk[chunk_offset])])
}
#[inline(always)]
- fn write_64(&mut self, base: u64, offset: u64, bytes: [u8; 8]) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<8>(self.len, base, offset)?;
- let chunk_idx = addr >> self.chunk_shift;
- let chunk_offset = addr & self.chunk_mask;
- if chunk_offset + 8 <= self.chunk_size {
- self.chunk_mut(chunk_idx)?[chunk_offset..chunk_offset + 8].copy_from_slice(&bytes);
- } else {
- if self.write_all(addr, &bytes).is_none() {
- cold_path();
- return Err(crate::Trap::OutOfMemory);
- }
+ fn write_8(&mut self, addr: usize, bytes: &[u8]) -> core::result::Result<(), crate::Trap> {
+ if addr >= self.len {
+ cold_path();
+ return Err(memory_oob(addr, 1, self.len));
}
- Ok(())
- }
-
- #[inline(always)]
- fn write_128(&mut self, base: u64, offset: u64, bytes: [u8; 16]) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<16>(self.len, base, offset)?;
let chunk_idx = addr >> self.chunk_shift;
let chunk_offset = addr & self.chunk_mask;
- if chunk_offset + 16 <= self.chunk_size {
- self.chunk_mut(chunk_idx)?[chunk_offset..chunk_offset + 16].copy_from_slice(&bytes);
- } else {
- if self.write_all(addr, &bytes).is_none() {
- cold_path();
- return Err(crate::Trap::OutOfMemory);
- }
- }
+ self.chunk_mut(chunk_idx)?[chunk_offset] = bytes[0];
Ok(())
}
}
diff --git a/crates/tinywasm/src/store/memory/vec.rs b/crates/tinywasm/src/store/memory/vec.rs
index 5f6288c..2737159 100644
--- a/crates/tinywasm/src/store/memory/vec.rs
+++ b/crates/tinywasm/src/store/memory/vec.rs
@@ -1,7 +1,7 @@
use alloc::vec::Vec;
use core::hint::cold_path;
-use super::{LinearMemory, checked_effective_addr};
+use super::{LinearMemory, memory_oob};
/// A contiguous `Vec<u8>`-backed linear memory.
///
@@ -33,10 +33,18 @@ impl VecMemory {
}
#[inline(always)]
- fn read_fixed<const N: usize>(&self, addr: usize) -> [u8; N] {
- self.data[addr..addr + N]
- .try_into()
- .unwrap_or_else(|_| unreachable!("fixed-width memory read has incorrect length"))
+ fn read_fixed<const N: usize>(&self, addr: usize) -> Result<[u8; N], crate::Trap> {
+ self.check_fixed_addr::<N>(addr)?;
+ Ok(self.data[addr..addr + N].try_into().unwrap_or_else(|_| unreachable!("slice length should be {N}")))
+ }
+
+ #[inline(always)]
+ fn check_fixed_addr<const N: usize>(&self, addr: usize) -> Result<(), crate::Trap> {
+ if N > self.data.len() || addr > self.data.len() - N {
+ cold_path();
+ return Err(memory_oob(addr, N, self.data.len()));
+ }
+ Ok(())
}
}
@@ -120,66 +128,63 @@ impl LinearMemory for VecMemory {
}
#[inline(always)]
- fn read_8(&self, base: u64, offset: u64) -> core::result::Result<u8, crate::Trap> {
- Ok(self.data[checked_effective_addr::<1>(self.data.len(), base, offset)?])
+ fn read_8(&self, addr: usize) -> core::result::Result<[u8; 1], crate::Trap> {
+ self.check_fixed_addr::<1>(addr)?;
+ Ok([self.data[addr]])
}
#[inline(always)]
- fn read_16(&self, base: u64, offset: u64) -> core::result::Result<[u8; 2], crate::Trap> {
- let addr = checked_effective_addr::<2>(self.data.len(), base, offset)?;
- Ok(self.read_fixed::<2>(addr))
+ fn read_16(&self, addr: usize) -> core::result::Result<[u8; 2], crate::Trap> {
+ self.read_fixed::<2>(addr)
}
#[inline(always)]
- fn read_32(&self, base: u64, offset: u64) -> core::result::Result<[u8; 4], crate::Trap> {
- let addr = checked_effective_addr::<4>(self.data.len(), base, offset)?;
- Ok(self.read_fixed::<4>(addr))
+ fn read_32(&self, addr: usize) -> core::result::Result<[u8; 4], crate::Trap> {
+ self.read_fixed::<4>(addr)
}
#[inline(always)]
- fn read_64(&self, base: u64, offset: u64) -> core::result::Result<[u8; 8], crate::Trap> {
- let addr = checked_effective_addr::<8>(self.data.len(), base, offset)?;
- Ok(self.read_fixed::<8>(addr))
+ fn read_64(&self, addr: usize) -> core::result::Result<[u8; 8], crate::Trap> {
+ self.read_fixed::<8>(addr)
}
#[inline(always)]
- fn read_128(&self, base: u64, offset: u64) -> core::result::Result<[u8; 16], crate::Trap> {
- let addr = checked_effective_addr::<16>(self.data.len(), base, offset)?;
- Ok(self.read_fixed::<16>(addr))
+ fn read_128(&self, addr: usize) -> core::result::Result<[u8; 16], crate::Trap> {
+ self.read_fixed::<16>(addr)
}
#[inline(always)]
- fn write_8(&mut self, base: u64, offset: u64, byte: u8) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<1>(self.data.len(), base, offset)?;
- self.data[addr] = byte;
+ fn write_8(&mut self, addr: usize, bytes: &[u8]) -> core::result::Result<(), crate::Trap> {
+ self.check_fixed_addr::<1>(addr)?;
+ self.data[addr] = bytes[0];
Ok(())
}
#[inline(always)]
- fn write_16(&mut self, base: u64, offset: u64, bytes: [u8; 2]) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<2>(self.data.len(), base, offset)?;
- self.data[addr..addr + 2].copy_from_slice(&bytes);
+ fn write_16(&mut self, addr: usize, bytes: &[u8]) -> core::result::Result<(), crate::Trap> {
+ self.check_fixed_addr::<2>(addr)?;
+ self.data[addr..addr + 2].copy_from_slice(bytes);
Ok(())
}
#[inline(always)]
- fn write_32(&mut self, base: u64, offset: u64, bytes: [u8; 4]) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<4>(self.data.len(), base, offset)?;
- self.data[addr..addr + 4].copy_from_slice(&bytes);
+ fn write_32(&mut self, addr: usize, bytes: &[u8]) -> core::result::Result<(), crate::Trap> {
+ self.check_fixed_addr::<4>(addr)?;
+ self.data[addr..addr + 4].copy_from_slice(bytes);
Ok(())
}
#[inline(always)]
- fn write_64(&mut self, base: u64, offset: u64, bytes: [u8; 8]) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<8>(self.data.len(), base, offset)?;
- self.data[addr..addr + 8].copy_from_slice(&bytes);
+ fn write_64(&mut self, addr: usize, bytes: &[u8]) -> core::result::Result<(), crate::Trap> {
+ self.check_fixed_addr::<8>(addr)?;
+ self.data[addr..addr + 8].copy_from_slice(bytes);
Ok(())
}
#[inline(always)]
- fn write_128(&mut self, base: u64, offset: u64, bytes: [u8; 16]) -> core::result::Result<(), crate::Trap> {
- let addr = checked_effective_addr::<16>(self.data.len(), base, offset)?;
- self.data[addr..addr + 16].copy_from_slice(&bytes);
+ fn write_128(&mut self, addr: usize, bytes: &[u8]) -> core::result::Result<(), crate::Trap> {
+ self.check_fixed_addr::<16>(addr)?;
+ self.data[addr..addr + 16].copy_from_slice(bytes);
Ok(())
}
}