From 4bf0b9b73952967e8d1ef2ad4426d68cb9220ee4 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 23 Jun 2025 11:54:34 +0200 Subject: [PATCH 1/4] doc: update Memory trait documentation --- docs/src/concepts/memory-trait.md | 7 +++++ src/lib.rs | 43 +++++++++++++++++-------------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/docs/src/concepts/memory-trait.md b/docs/src/concepts/memory-trait.md index 78f494f1..04555085 100644 --- a/docs/src/concepts/memory-trait.md +++ b/docs/src/concepts/memory-trait.md @@ -23,6 +23,13 @@ The `Memory` trait intentionally models a [WebAssembly memory instance](https:// This design choice ensures consistency with the interface of memories available to canisters. It also provides future compatibility with potential multi-memory support in canisters. +## Safety contract + +⚠️ `read` and `write` **assume the caller will not access memory outside the current size**. + +If the range `[offset … offset + len)` exceeds available memory, the call panics (in native tests) or traps (in a Wasm canister). +Callers must store and check data lengths themselves or use higher-level containers such as `StableVec`. + ## Available Memory Implementations The library provides several implementations of the `Memory` trait, each designed for specific use cases: diff --git a/src/lib.rs b/src/lib.rs index e096b11e..e39d92f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,44 +45,49 @@ const WASM_PAGE_SIZE: u64 = 65536; /// The maximum number of stable memory pages a canister can address. pub const MAX_PAGES: u64 = u64::MAX / WASM_PAGE_SIZE; +/// Abstraction over a WebAssembly-style linear memory (e.g., stable memory). +/// +/// Implementations are expected to mirror WebAssembly semantics: +/// out-of-bounds accesses will cause a panic (in native) or trap (in Wasm). pub trait Memory { - /// Returns the current size of the stable memory in WebAssembly - /// pages. (One WebAssembly page is 64Ki bytes.) + /// Returns the current size of the memory in WebAssembly pages. + /// + /// One WebAssembly page is 64 KiB. fn size(&self) -> u64; - /// Tries to grow the memory by `pages` many pages containing - /// zeroes. If successful, returns the previous size of the - /// memory (in pages). Otherwise, returns -1. + /// Grows the memory by `pages` pages filled with zeroes. + /// + /// Returns the previous size in pages on success, or -1 if the + /// memory could not be grown. fn grow(&self, pages: u64) -> i64; - /// Copies the data referred to by `offset` out of the stable memory - /// and replaces the corresponding bytes in `dst`. + /// Copies `dst.len()` bytes from memory starting at `offset` into `dst`. + /// + /// Panics or traps if the read would go out of bounds. fn read(&self, offset: u64, dst: &mut [u8]); - /// Copies `count` number of bytes of the data starting from `offset` out of the stable memory - /// into the buffer starting at `dst`. + /// Unsafe variant of `read` for advanced use. /// - /// This method is an alternative to `read` which does not require initializing a buffer and may - /// therefore be faster. + /// Copies `count` bytes from memory starting at `offset` into the + /// raw pointer `dst`. Initializes the destination before reading. /// /// # Safety /// - /// Callers must guarantee that - /// * it is valid to write `count` number of bytes starting from `dst`, - /// * `dst..dst + count` does not overlap with `self`. + /// Caller must ensure: + /// - `dst` points to valid writable memory of at least `count` bytes. + /// - The memory range `dst..dst+count` does not overlap with `self`. /// - /// Implementations must guarantee that before the method returns, `count` number of bytes - /// starting from `dst` will be initialized. + /// Panics or traps if the read would go out of bounds. #[inline] unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) { - // Initialize the buffer to make the slice valid. std::ptr::write_bytes(dst, 0, count); let slice = std::slice::from_raw_parts_mut(dst, count); self.read(offset, slice) } - /// Copies the data referred to by `src` and replaces the - /// corresponding segment starting at offset in the stable memory. + /// Copies `src.len()` bytes from `src` into memory starting at `offset`. + /// + /// Panics or traps if the write would go out of bounds. fn write(&self, offset: u64, src: &[u8]); } From 73d3248d502b58dac176073c9b965b848841b1ac Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 23 Jun 2025 11:59:15 +0200 Subject: [PATCH 2/4] . --- docs/src/concepts/memory-trait.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/src/concepts/memory-trait.md b/docs/src/concepts/memory-trait.md index 04555085..c1350ed8 100644 --- a/docs/src/concepts/memory-trait.md +++ b/docs/src/concepts/memory-trait.md @@ -9,12 +9,15 @@ pub trait Memory { fn size(&self) -> u64; /// Equivalent to WebAssembly memory.grow. + /// Returns the previous size, or -1 if the grow fails. fn grow(&self, pages: u64) -> i64; /// Copies bytes from this memory to the heap (in Wasm, memory 0). + /// Panics or traps if out of bounds. fn read(&self, offset: u64, dst: &mut [u8]); /// Writes bytes from the heap (in Wasm, memory 0) to this memory. + /// Panics or traps if out of bounds. fn write(&self, offset: u64, src: &[u8]); } ``` From 4c9ba4cef99140782ca9a73aabfb0d165bc2d9d7 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 23 Jun 2025 12:02:24 +0200 Subject: [PATCH 3/4] . --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index e39d92f3..4326bea4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,6 +80,7 @@ pub trait Memory { /// Panics or traps if the read would go out of bounds. #[inline] unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) { + // Initialize the buffer to make the slice valid. std::ptr::write_bytes(dst, 0, count); let slice = std::slice::from_raw_parts_mut(dst, count); self.read(offset, slice) From a536c7a1b61b4dcbb6da94ae13acbdae8b2ccecc Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 23 Jun 2025 13:22:08 +0200 Subject: [PATCH 4/4] panics --- docs/src/concepts/memory-trait.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/concepts/memory-trait.md b/docs/src/concepts/memory-trait.md index c1350ed8..588e30b1 100644 --- a/docs/src/concepts/memory-trait.md +++ b/docs/src/concepts/memory-trait.md @@ -26,7 +26,7 @@ The `Memory` trait intentionally models a [WebAssembly memory instance](https:// This design choice ensures consistency with the interface of memories available to canisters. It also provides future compatibility with potential multi-memory support in canisters. -## Safety contract +## Panics ⚠️ `read` and `write` **assume the caller will not access memory outside the current size**.