Skip to content

Commit c985664

Browse files
committed
rust: support memory
Introduce unsafe API to acquire memory slices and a safe API to set/get memory.
1 parent e50875f commit c985664

1 file changed

Lines changed: 309 additions & 0 deletions

File tree

bindings/rust/src/lib.rs

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,75 @@ impl TypedExecutionResult {
289289
}
290290

291291
impl Instance {
292+
/// Ensure the range is valid according to the currently available memory size.
293+
fn checked_memory_range(
294+
&self,
295+
offset: u32,
296+
size: usize,
297+
) -> Result<core::ops::Range<usize>, ()> {
298+
// This should be safe given usize::BITS >= u32::BITS, see https://doc.rust-lang.org/std/primitive.usize.html.
299+
let offset = offset as usize;
300+
let memory_size = self.memory_size();
301+
// Empty slices are allowed, but ensure both starting and ending offsets are valid.
302+
if offset >= memory_size || (offset + size) > memory_size {
303+
return Err(());
304+
}
305+
debug_assert!(memory_size != 0);
306+
debug_assert!(
307+
unsafe { sys::fizzy_get_instance_memory_data(self.0.as_ptr()) } != std::ptr::null_mut()
308+
);
309+
Ok(offset..offset + size)
310+
}
311+
312+
/// Obtain a read-only slice of underlying memory.
313+
///
314+
/// # Safety
315+
/// These slices turn invalid if the memory is resized (i.e. via the WebAssembly `memory.grow` instruction)
316+
pub unsafe fn checked_memory_slice(&self, offset: u32, size: usize) -> Result<&[u8], ()> {
317+
let range = self.checked_memory_range(offset, size)?;
318+
let memory = std::slice::from_raw_parts(
319+
sys::fizzy_get_instance_memory_data(self.0.as_ptr()),
320+
sys::fizzy_get_instance_memory_size(self.0.as_ptr()),
321+
);
322+
Ok(&memory[range])
323+
}
324+
325+
/// Obtain a mutable slice of underlying memory.
326+
///
327+
/// # Safety
328+
/// These slices turn invalid if the memory is resized (i.e. via the WebAssembly `memory.grow` instruction)
329+
pub unsafe fn checked_memory_slice_mut(
330+
&mut self,
331+
offset: u32,
332+
size: usize,
333+
) -> Result<&mut [u8], ()> {
334+
let range = self.checked_memory_range(offset, size)?;
335+
let memory = std::slice::from_raw_parts_mut(
336+
sys::fizzy_get_instance_memory_data(self.0.as_ptr()),
337+
sys::fizzy_get_instance_memory_size(self.0.as_ptr()),
338+
);
339+
Ok(&mut memory[range])
340+
}
341+
342+
/// Returns the current memory size, in bytes.
343+
pub fn memory_size(&self) -> usize {
344+
unsafe { sys::fizzy_get_instance_memory_size(self.0.as_ptr()) }
345+
}
346+
347+
/// Copies memory from `offset` to `target`, for the length of `target.len()`.
348+
pub fn memory_get(&self, offset: u32, target: &mut [u8]) -> Result<(), ()> {
349+
let slice = unsafe { self.checked_memory_slice(offset, target.len())? };
350+
target.copy_from_slice(slice);
351+
Ok(())
352+
}
353+
354+
/// Copies memory from `source` to `offset`, for the length of `source.len()`.
355+
pub fn memory_set(&mut self, offset: u32, source: &[u8]) -> Result<(), ()> {
356+
let slice = unsafe { self.checked_memory_slice_mut(offset, source.len())? };
357+
slice.copy_from_slice(source);
358+
Ok(())
359+
}
360+
292361
/// Get a read-only pointer to the module.
293362
unsafe fn get_module(&self) -> *const sys::FizzyModule {
294363
sys::fizzy_get_instance_module(self.0.as_ptr())
@@ -755,4 +824,244 @@ mod tests {
755824
let result = instance.execute("bar", &[TypedValue::F32(1.0), TypedValue::F64(2.0)], 0);
756825
assert!(result.is_err());
757826
}
827+
828+
#[test]
829+
fn no_memory() {
830+
/* wat2wasm
831+
(module)
832+
*/
833+
let input = hex::decode("0061736d01000000").unwrap();
834+
835+
let module = parse(&input);
836+
assert!(module.is_ok());
837+
let instance = module.unwrap().instantiate();
838+
assert!(instance.is_ok());
839+
let mut instance = instance.unwrap();
840+
841+
assert_eq!(instance.memory_size(), 0);
842+
843+
// If there is no memory, do not allow any slice.
844+
unsafe {
845+
assert!(instance.checked_memory_slice(0, 0).is_err());
846+
assert!(instance.checked_memory_slice_mut(0, 0).is_err());
847+
assert!(instance.checked_memory_slice(0, 65536).is_err());
848+
assert!(instance.checked_memory_slice_mut(0, 65536).is_err());
849+
assert!(instance.checked_memory_slice(65535, 1).is_err());
850+
assert!(instance.checked_memory_slice_mut(65535, 1).is_err());
851+
assert!(instance.checked_memory_slice(65535, 2).is_err());
852+
assert!(instance.checked_memory_slice_mut(65535, 2).is_err());
853+
assert!(instance.checked_memory_slice(65536, 0).is_err());
854+
assert!(instance.checked_memory_slice_mut(65536, 0).is_err());
855+
}
856+
857+
// Set memory via safe helper.
858+
assert!(instance.memory_set(0, &[]).is_err());
859+
// Get memory via safe helper.
860+
let mut dst: Vec<u8> = Vec::new();
861+
dst.resize(65536, 0);
862+
// Reading 65536 bytes.
863+
assert!(instance.memory_get(0, &mut dst).is_err());
864+
}
865+
866+
#[test]
867+
fn empty_memory() {
868+
/* wat2wasm
869+
(module
870+
;; Memory is allowed, but no memory is allocated at start.
871+
(memory 0)
872+
)
873+
*/
874+
let input = hex::decode("0061736d010000000503010000").unwrap();
875+
876+
let module = parse(&input);
877+
assert!(module.is_ok());
878+
let instance = module.unwrap().instantiate();
879+
assert!(instance.is_ok());
880+
let mut instance = instance.unwrap();
881+
882+
assert_eq!(instance.memory_size(), 0);
883+
884+
// If there is no memory, do not allow any slice.
885+
unsafe {
886+
assert!(instance.checked_memory_slice(0, 0).is_err());
887+
assert!(instance.checked_memory_slice_mut(0, 0).is_err());
888+
assert!(instance.checked_memory_slice(0, 65536).is_err());
889+
assert!(instance.checked_memory_slice_mut(0, 65536).is_err());
890+
assert!(instance.checked_memory_slice(65535, 1).is_err());
891+
assert!(instance.checked_memory_slice_mut(65535, 1).is_err());
892+
assert!(instance.checked_memory_slice(65535, 2).is_err());
893+
assert!(instance.checked_memory_slice_mut(65535, 2).is_err());
894+
assert!(instance.checked_memory_slice(65536, 0).is_err());
895+
assert!(instance.checked_memory_slice_mut(65536, 0).is_err());
896+
}
897+
898+
// Set memory via safe helper.
899+
assert!(instance.memory_set(0, &[]).is_err());
900+
// Get memory via safe helper.
901+
let mut dst: Vec<u8> = Vec::new();
902+
dst.resize(65536, 0);
903+
// Reading 65536 bytes.
904+
assert!(instance.memory_get(0, &mut dst).is_err());
905+
}
906+
907+
#[test]
908+
fn memory() {
909+
/* wat2wasm
910+
(module
911+
(func (export "grow") (param i32) (result i32) (memory.grow (local.get 0)))
912+
(func (export "peek") (param i32) (result i32) (i32.load (local.get 0)))
913+
(func (export "poke") (param i32) (param i32) (i32.store (local.get 0) (local.get 1)))
914+
(memory (export "mem") 1 2)
915+
)
916+
*/
917+
let input = hex::decode("0061736d01000000010b0260017f017f60027f7f00030403000001050401010102071c040467726f770000047065656b000104706f6b650002036d656d02000a1a030600200040000b070020002802000b0900200020013602000b").unwrap();
918+
let module = parse(&input);
919+
assert!(module.is_ok());
920+
let instance = module.unwrap().instantiate();
921+
assert!(instance.is_ok());
922+
let mut instance = instance.unwrap();
923+
924+
assert_eq!(instance.memory_size(), 65536);
925+
unsafe {
926+
// Allow empty slices.
927+
assert!(instance.checked_memory_slice(0, 0).is_ok());
928+
assert!(instance.checked_memory_slice_mut(0, 0).is_ok());
929+
// Entire memory.
930+
assert!(instance.checked_memory_slice(0, 65536).is_ok());
931+
assert!(instance.checked_memory_slice_mut(0, 65536).is_ok());
932+
// Allow empty slices.
933+
assert!(instance.checked_memory_slice(65535, 0).is_ok());
934+
assert!(instance.checked_memory_slice_mut(65535, 0).is_ok());
935+
// Single byte.
936+
assert!(instance.checked_memory_slice(65535, 1).is_ok());
937+
assert!(instance.checked_memory_slice_mut(65535, 1).is_ok());
938+
// Reading over.
939+
assert!(instance.checked_memory_slice(65535, 2).is_err());
940+
assert!(instance.checked_memory_slice_mut(65535, 2).is_err());
941+
// Offset overflow.
942+
assert!(instance.checked_memory_slice(65536, 0).is_err());
943+
assert!(instance.checked_memory_slice_mut(65536, 0).is_err());
944+
}
945+
946+
// Grow with a single page.
947+
let result = instance
948+
.execute("grow", &[TypedValue::U32(1)], 0)
949+
.expect("successful execution");
950+
assert!(!result.trapped());
951+
assert_eq!(
952+
result
953+
.value()
954+
.expect("expected value")
955+
.as_u32()
956+
.expect("expected u32 result"),
957+
1
958+
);
959+
// Expect new total memory size.
960+
assert_eq!(instance.memory_size(), 65536 * 2);
961+
962+
// Set memory via slices.
963+
unsafe {
964+
let mem = instance
965+
.checked_memory_slice_mut(0, 65536)
966+
.expect("valid mutable slice");
967+
assert_eq!(mem[0], 0);
968+
assert_eq!(mem[1], 0);
969+
assert_eq!(mem[2], 0);
970+
assert_eq!(mem[3], 0);
971+
mem[0] = 42;
972+
}
973+
unsafe {
974+
// Check that const slice matches up.
975+
let mem = instance.checked_memory_slice(0, 5).expect("valid slice");
976+
assert_eq!(mem[0], 42);
977+
assert_eq!(mem[1], 0);
978+
assert_eq!(mem[2], 0);
979+
assert_eq!(mem[3], 0);
980+
}
981+
let result = instance
982+
.execute("peek", &[TypedValue::U32(0)], 0)
983+
.expect("successful execution");
984+
assert!(!result.trapped());
985+
assert_eq!(
986+
result
987+
.value()
988+
.expect("expected value")
989+
.as_u32()
990+
.expect("expected u32 result"),
991+
42
992+
);
993+
994+
// Remember, by now we have grown memory to two pages.
995+
996+
// Set memory via safe helper.
997+
assert!(instance.memory_set(0, &[]).is_ok());
998+
assert!(instance.memory_set(65536 + 65535, &[]).is_ok());
999+
assert!(instance.memory_set(65536 + 65536, &[]).is_err());
1000+
assert!(instance.memory_set(65536 + 65537, &[]).is_err());
1001+
assert!(instance.memory_set(0, &[0x11, 0x22, 0x33, 0x44]).is_ok());
1002+
assert!(instance
1003+
.memory_set(65536 + 65532, &[0x11, 0x22, 0x33, 0x44])
1004+
.is_ok());
1005+
assert!(instance
1006+
.memory_set(65536 + 65533, &[0x11, 0x22, 0x33, 0x44])
1007+
.is_err());
1008+
assert!(instance
1009+
.memory_set(65536 + 65534, &[0x11, 0x22, 0x33, 0x44])
1010+
.is_err());
1011+
assert!(instance
1012+
.memory_set(65536 + 65535, &[0x11, 0x22, 0x33, 0x44])
1013+
.is_err());
1014+
assert!(instance
1015+
.memory_set(65536 + 65536, &[0x11, 0x22, 0x33, 0x44])
1016+
.is_err());
1017+
assert!(instance
1018+
.memory_set(65536 + 65537, &[0x11, 0x22, 0x33, 0x44])
1019+
.is_err());
1020+
1021+
let result = instance
1022+
.execute("peek", &[TypedValue::U32(0)], 0)
1023+
.expect("successful execution");
1024+
assert!(!result.trapped());
1025+
assert_eq!(
1026+
result
1027+
.value()
1028+
.expect("expected value")
1029+
.as_u32()
1030+
.expect("expected u32 result"),
1031+
0x44332211
1032+
);
1033+
1034+
// Change memory via wasm.
1035+
let result = instance
1036+
.execute(
1037+
"poke",
1038+
&[TypedValue::U32(0), TypedValue::U32(0x88776655)],
1039+
0,
1040+
)
1041+
.expect("successful execution");
1042+
assert!(!result.trapped());
1043+
1044+
// Read memory via safe helper.
1045+
let mut dst: Vec<u8> = Vec::new();
1046+
dst.resize(65536, 0);
1047+
// Reading 65536 bytes.
1048+
assert!(instance.memory_get(0, &mut dst).is_ok());
1049+
// Only checking the first 4.
1050+
assert_eq!(dst[0..4], [0x55, 0x66, 0x77, 0x88]);
1051+
1052+
// Read into empty slice.
1053+
assert!(instance.memory_get(0, &mut dst[0..0]).is_ok());
1054+
assert!(instance.memory_get(65536 + 65535, &mut dst[0..0]).is_ok());
1055+
assert!(instance.memory_get(65536 + 65536, &mut dst[0..0]).is_err());
1056+
assert!(instance.memory_get(65536 + 65537, &mut dst[0..0]).is_err());
1057+
1058+
// Read into short slice.
1059+
assert!(instance.memory_get(0, &mut dst[0..4]).is_ok());
1060+
assert!(instance.memory_get(65536 + 65532, &mut dst[0..4]).is_ok());
1061+
assert!(instance.memory_get(65536 + 65533, &mut dst[0..4]).is_err());
1062+
assert!(instance.memory_get(65536 + 65534, &mut dst[0..4]).is_err());
1063+
assert!(instance.memory_get(65536 + 65535, &mut dst[0..4]).is_err());
1064+
assert!(instance.memory_get(65536 + 65536, &mut dst[0..4]).is_err());
1065+
assert!(instance.memory_get(65536 + 65537, &mut dst[0..4]).is_err());
1066+
}
7581067
}

0 commit comments

Comments
 (0)