Skip to content

wasip2 pread consumes the descriptor.read result before checking it — reads uninitialized memory and frees a garbage pointer on the error path #861

Description

@calvinrp

In the wasm32-wasip2 build, pread's glue uses the result of
filesystem_method_descriptor_read before checking whether the call
succeeded (libc-bottom-half/cloudlibc/src/libc/unistd/pread.c, the
__wasip2__ branch):

bool ok = filesystem_method_descriptor_read(file_handle, nbyte, offset,
                                            &contents, &error_code);
bytes_read = contents.f0.len;                  // consumed before the check
memcpy(buf, contents.f0.ptr, bytes_read);      // copy from it
wasip2_list_u8_free(&contents.f0);             // free it

if (!ok) { ... }                               // checked too late

When the host returns an error, the generated binding does not write
contents at all — it is uninitialized stack memory. So on any failing
read, this code:

  1. reads an uninitialized length and pointer,
  2. memcpys from that garbage pointer into the caller's buffer, and
  3. passes the garbage pointer to free, corrupting the allocator heap.

Any WASI 0.2 host whose filesystem can return an error from
descriptor.read (I/O errors, busy resources, permission failures)
triggers this. Noticed by reading the glue while chasing crashes that
only occurred when reads failed.

The neighboring functions get this right — readlinkat and pwrite
both check the flag before touching the result — so pread looks like
a simple ordering slip. A one-function fix is prepared (moving the check
first, plus clamping the returned length to the requested size so an
out-of-spec host cannot overflow the caller's buffer); PR to follow.

The wasip1 and wasip3 branches of the same file are unaffected (different
mechanisms; wasip3 reads directly into the caller's buffer).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions