Skip to content

Commit 4684eb1

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

1 file changed

Lines changed: 321 additions & 0 deletions

File tree

bindings/rust/src/lib.rs

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

0 commit comments

Comments
 (0)