diff --git a/CHANGELOG.md b/CHANGELOG.md index 123e53b878..8c83a60698 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,8 @@ and this project adheres to - cosmwasm-vm: Upgrade Wasmer to 4.2.5; Bump `MODULE_SERIALIZATION_VERSION` to "v9". ([#1992]) - cosmwasm-std: Rename `GovMsg::vote` to `GovMsg::option` ([#1999]) +- cosmwasm-vm: Read `Region` from Wasm memory as bytes and convert to `Region` + afterwards ([#2005]) [#1874]: https://github.com/CosmWasm/cosmwasm/pull/1874 [#1876]: https://github.com/CosmWasm/cosmwasm/pull/1876 @@ -119,6 +121,7 @@ and this project adheres to [#1977]: https://github.com/CosmWasm/cosmwasm/pull/1977 [#1992]: https://github.com/CosmWasm/cosmwasm/pull/1992 [#1999]: https://github.com/CosmWasm/cosmwasm/pull/1999 +[#2005]: https://github.com/CosmWasm/cosmwasm/pull/2005 ### Removed diff --git a/packages/vm/src/memory.rs b/packages/vm/src/memory.rs index 5143de0bd4..e8e2a45b23 100644 --- a/packages/vm/src/memory.rs +++ b/packages/vm/src/memory.rs @@ -1,4 +1,4 @@ -use std::mem::MaybeUninit; +use std::mem::{size_of, MaybeUninit}; use wasmer::{ValueType, WasmPtr}; @@ -26,6 +26,37 @@ pub struct Region { pub length: u32, } +/// Byte representation of a [Region] struct in Wasm memory. +type RegionBytes = [u8; size_of::()]; + +impl Region { + fn from_wasm_bytes(bytes: RegionBytes) -> Self { + let offset = u32::from_le_bytes(bytes[0..4].try_into().unwrap()); + let capacity = u32::from_le_bytes(bytes[4..8].try_into().unwrap()); + let length = u32::from_le_bytes(bytes[8..12].try_into().unwrap()); + Region { + offset, + capacity, + length, + } + } + + fn into_wasm_bytes(self) -> RegionBytes { + let Region { + offset, + capacity, + length, + } = self; + + let mut bytes = [0u8; 12]; + // wasm is little endian + bytes[0..4].copy_from_slice(&offset.to_le_bytes()); + bytes[4..8].copy_from_slice(&capacity.to_le_bytes()); + bytes[8..12].copy_from_slice(&length.to_le_bytes()); + bytes + } +} + unsafe impl ValueType for Region { fn zero_padding_bytes(&self, _bytes: &mut [MaybeUninit]) { // The size of Region is exactly 3x4=12 bytes with no padding. @@ -34,6 +65,14 @@ unsafe impl ValueType for Region { } } +// Wasm is little endian, and we want to be able to just reinterpret slices of +// wasm memory as a Region struct, so we only support little endian systems. +// If we ever need to support big endian systems, we can use more fine-grained checks +// in the places where we read/write the Region struct +// (and possibly other interactions between Wasm and host). +#[cfg(target_endian = "big")] +compile_error!("big endian systems are not supported"); + /// Expects a (fixed size) Region struct at ptr, which is read. This links to the /// memory region, which is copied in the second step. /// Errors if the length of the region exceeds `max_length`. @@ -91,10 +130,10 @@ pub fn write_region(memory: &wasmer::MemoryView, ptr: u32, data: &[u8]) -> VmRes /// Reads in a Region at offset in Wasm memory and returns a copy of it fn get_region(memory: &wasmer::MemoryView, offset: u32) -> CommunicationResult { - let wptr = WasmPtr::::new(offset); - let region = wptr.deref(memory).read().map_err(|_err| { + let wptr = WasmPtr::::new(offset); + let region = Region::from_wasm_bytes(wptr.deref(memory).read().map_err(|_err| { CommunicationError::deref_err(offset, "Could not dereference this pointer to a Region") - })?; + })?); validate_region(®ion)?; Ok(region) } @@ -122,10 +161,12 @@ fn validate_region(region: &Region) -> RegionValidationResult<()> { /// Overrides a Region at offset in Wasm memory fn set_region(memory: &wasmer::MemoryView, offset: u32, data: Region) -> CommunicationResult<()> { - let wptr = WasmPtr::::new(offset); - wptr.deref(memory).write(data).map_err(|_err| { - CommunicationError::deref_err(offset, "Could not dereference this pointer to a Region") - })?; + let wptr = WasmPtr::::new(offset); + wptr.deref(memory) + .write(data.into_wasm_bytes()) + .map_err(|_err| { + CommunicationError::deref_err(offset, "Could not dereference this pointer to a Region") + })?; Ok(()) }