Skip to content

Commit

Permalink
Merge pull request #2005 from CosmWasm/use-bytes-for-reading-wasm
Browse files Browse the repository at this point in the history
Use bytes for reading Region from wasm memory
  • Loading branch information
chipshort authored Jan 24, 2024
2 parents 1b64d21 + b9b8022 commit 226add5
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 8 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
57 changes: 49 additions & 8 deletions packages/vm/src/memory.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::mem::MaybeUninit;
use std::mem::{size_of, MaybeUninit};

use wasmer::{ValueType, WasmPtr};

Expand Down Expand Up @@ -26,6 +26,37 @@ pub struct Region {
pub length: u32,
}

/// Byte representation of a [Region] struct in Wasm memory.
type RegionBytes = [u8; size_of::<Region>()];

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<u8>]) {
// The size of Region is exactly 3x4=12 bytes with no padding.
Expand All @@ -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`.
Expand Down Expand Up @@ -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<Region> {
let wptr = WasmPtr::<Region>::new(offset);
let region = wptr.deref(memory).read().map_err(|_err| {
let wptr = WasmPtr::<RegionBytes>::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(&region)?;
Ok(region)
}
Expand Down Expand Up @@ -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::<Region>::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::<RegionBytes>::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(())
}

Expand Down

0 comments on commit 226add5

Please sign in to comment.