nodejs / uvwasi

WASI syscall API built atop libuv
MIT License
222 stars 48 forks source link

`fd_pwrite` behavior diverges from host & other wasm runtimes #256

Closed yagehu closed 3 months ago

yagehu commented 3 months ago

Attempting to fd_pwrite to a file with a very large offset fails on Linux host and other Wasm runtimes (Wasmtime, Wasmer, WasmEdge, WAMR), but it succeeds with Node (using uvwasi) but writes 0 bytes.

Repro host:

#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>

int main() {
    int fd = open("tmp/a", O_CREAT | O_RDWR);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    ssize_t nwritten = pwrite(fd, "a", 1, UINT64_MAX);
    if (nwritten < 0) {
        perror("pwrite");
        return 1;
    }

    return 0;
}

Repro other runtimes:

(module
  (type (;0;) (func))
  (type (;1;) (func (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32)))
  (type (;2;) (func (param i32)))
  (type (;3;) (func (param i32 i32 i32 i64 i32) (result i32)))
  (import "wasi_snapshot_preview1" "path_open" (func $path_open (type 1)))
  (import "wasi_snapshot_preview1" "proc_exit" (func $proc_exit (type 2)))
  (import "wasi_snapshot_preview1" "fd_pwrite" (func $fd_pwrite (type 3)))
  (func $_start (type 0)
    (local $err i32)

    (local.set $err
      (call $path_open
        (i32.const 3)
        (i32.const 0)
        (i32.const 1024)
        (i32.const 1)
        (i32.const 0)
        (i64.const 66)
        (i64.const 0)
        (i32.const 0)
        (i32.const 512)
      )
    )
    (local.get $err)
    (if
      (then (call $proc_exit (i32.const 1)))
    )

    (local.set $err
      (call $fd_pwrite
        (i32.load (i32.const 512))
        (i32.const 256)
        (i32.const 1)
        (i64.const 18446744073709551615)
        (i32.const 0)
      )
    )
    (local.get $err)
    (if
      (then (call $proc_exit (local.get $err)))
    )
  )
  (memory 2)
  (export "memory" (memory 0))
  (export "_start" (func $_start))
  (data (i32.const 1024) "a\00")
  (data (i32.const 260) "\01")
)

Running the above WAT snippet causes other runtimes to report einval (28). But Node using uvwasi will report success (0).

Note that compiling the C snippet with wasi-sdk will cause all runtimes to report an error because the top half of wasi-libc checks the offset.

I believe https://github.com/nodejs/uvwasi/pull/254 should bring uvwasi in line with other runtimes.

guybedford commented 3 months ago

Thank you, landed in https://github.com/nodejs/uvwasi/pull/254.