Function aingle_wasmer_host::guest::write_bytes[][src]

pub fn write_bytes(
    ctx: &mut Ctx,
    guest_ptr: GuestPtr,
    slice: &[u8]
) -> Result<(), WasmError>

write a slice of bytes to the guest in a safe-ish way

a naive approach would look like this:

let view: MemoryView<u8> = ctx.memory(0).view();
unsafe {
      std::ptr::copy_nonoverlapping(
        slice.as_ptr(),
        view.as_ptr().add(guest_ptr as usize) as *mut u8,
        slice.len(),
    );
}

the guest memory is part of the host memory, so we get the host's pointer to the start of the guest's memory with view.as_ptr() then we add the guest's pointer to where it wants to see the written bytes then copy the slice directly across.

the problem with this approach is that the guest_ptr typically needs to be provided by the allocator in the guest wasm in order to be safe for the guest's consumption, but a malicious guest could provide bogus guest_ptr values that point outside the bounds of the guest memory. the naive host would then corrupt its own memory by copying bytes... wherever, basically.

a better approach is to use wasmer's WasmPtr abstraction, which checks against the memory bounds of the guest based on the input type and can be dereferenced to a [Cell] slice that we can write to more safely.

@see https://docs.rs/wasmer-runtime-core/0.17.0/src/wasmer_runtime_core/memory/ptr.rs.html#120

this is still not completely safe in the face of shared memory and threads, etc.

the guest needs to provide a pointer to a pre-allocated (e.g. by forgetting a Vec) region of the guest's memory that it is safe for the host to write to.

it is the host's responsibility to tell the guest the length of the allocation that is needed and the guest's responsibility to correctly reserve an allocation to be written into.

write_bytes() takes a slice of bytes and writes it to the position at the guest pointer

as the byte slice cannot be co-ordinated by the compiler (because the host and guest have different compilers and allocators) we prefix the allocation with a WasmSize length value.

for example, if we wanted to write the slice &[1, 2, 3] then we'd take the length of the slice, 3 as a WasmSize, which is u32, i.e. a 3_u32 and convert it to an array of u8 bytes as [ 3_u8, 0_u8, 0_u8, 0_u8 ] and concatenate it to our original [ 1_u8, 2_u8, 3_u8 ]. this gives the full array of bytes to write as:

[ 3_u8, 0_u8, 0_u8, 0_u8, 1_u8, 2_u8, 3_u8 ]

this allows us to read back the byte slice given only a GuestPtr because the read operation can do the inverse in a single step by reading the length inline

it also requires the host and the guest to both adopt this convention and read/write the additional 4 byte prefix in order to read/write the real payload correctly

@see read_bytes()