diff options
| author | Henry <mail@henrygressmann.de> | 2026-07-23 09:47:59 +0200 |
|---|---|---|
| committer | Henry <mail@henrygressmann.de> | 2026-07-23 09:47:59 +0200 |
| commit | 1dc86cd5e68f754a03bd38268d7d4a693bbdea47 (patch) | |
| tree | 33f82426b5e8cd216fef0e8f04f32b67ad24af70 /crates | |
| parent | 8bc916ef0afdede9edce6bb9045dc555e4d3b218 (diff) | |
chore: simplify LinearMemory trait
Signed-off-by: Henry <mail@henrygressmann.de>
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/tinywasm/src/interpreter/executor.rs | 101 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/memory/instance.rs | 165 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/memory/lazy.rs | 4 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/memory/mod.rs | 273 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/memory/paged.rs | 148 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/memory/vec.rs | 73 |
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(()) } } |
