Function holochain_wasmer_host::guest::write_bytes

source ·
pub fn write_bytes(
    store_mut: &mut StoreMut<'_>,
    memory: &Memory,
    guest_ptr: GuestPtr,
    slice: &[u8]
) -> Result<(), RuntimeError>
Expand description

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 *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 leaking a Vec) region of the guest’s memory that 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.

The guest and the host negotiate the length of the bytes separately.

@see read_bytes()