diff --git a/src/node_wasi.cc b/src/node_wasi.cc index 965a619c8d4acd..26cf356d4dfe8e 100644 --- a/src/node_wasi.cc +++ b/src/node_wasi.cc @@ -72,11 +72,20 @@ inline void Debug(WASI* wasi, Args&&... args) { } \ } while (0) +#define CHECK_BOUNDS_OR_RETURN2(mem_size, offset, buf_size) \ + do { \ + if (!uvwasi_serdes_check_bounds((offset), (mem_size), (buf_size))) { \ + return UVWASI_EOVERFLOW; \ + } \ + } while (0) + using v8::Array; using v8::BackingStore; using v8::BigInt; +using v8::CFunction; using v8::Context; using v8::Exception; +using v8::FastApiCallbackOptions; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Integer; @@ -84,6 +93,7 @@ using v8::Isolate; using v8::Local; using v8::MaybeLocal; using v8::Object; +using v8::Signature; using v8::String; using v8::Uint32; using v8::Value; @@ -248,46 +258,149 @@ void WASI::New(const FunctionCallbackInfo& args) { } } +template +void WASI::WasiFunction::SetFunction( + Environment* env, const char* name, Local tmpl) { + auto c_function = CFunction::Make(FastCallback); + Local t = + v8::FunctionTemplate::New(env->isolate(), + SlowCallback, + Local(), + Local(), + sizeof...(Args), + v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasSideEffect, + &c_function); + const v8::NewStringType type = v8::NewStringType::kInternalized; + v8::Local name_string = + v8::String::NewFromUtf8(env->isolate(), name, type).ToLocalChecked(); + tmpl->PrototypeTemplate()->Set(name_string, t); + t->SetClassName(name_string); +} + +template +R WASI::WasiFunction::FastCallback( + Local receiver, Args... args, FastApiCallbackOptions& options) { + WASI* wasi = reinterpret_cast(BaseObject::FromJSObject(receiver)); + if (UNLIKELY(wasi == nullptr)) return UVWASI_EINVAL; + + if (UNLIKELY(options.wasm_memory == nullptr)) return UVWASI_EINVAL; + uint8_t* memory = nullptr; + CHECK(LIKELY(options.wasm_memory->getStorageIfAligned(&memory))); + + return F(*wasi, {(char*)memory, options.wasm_memory->length()}, args...); +} + +namespace { +template +static uvwasi_errno_t CheckType(Local v); + +template +static VT ConvertType(Local V); + +template <> +uvwasi_errno_t CheckType(Local value) { + if (!value->IsUint32()) return UVWASI_EINVAL; + return UVWASI_ESUCCESS; +} + +template <> +uint32_t ConvertType(Local value) { + return value.As()->Value(); +} + +template <> +uvwasi_errno_t CheckType(Local value) { + if (!value->IsBigInt()) return UVWASI_EINVAL; + return UVWASI_ESUCCESS; +} -void WASI::ArgsGet(const FunctionCallbackInfo& args) { +template <> +uint64_t ConvertType(Local value) { + Local js_value = value.As(); + bool lossless; + return js_value->Uint64Value(&lossless); +} + +template <> +uvwasi_errno_t CheckType(Local value) { + if (!value->IsBigInt()) return UVWASI_EINVAL; + return UVWASI_ESUCCESS; +} + +template <> +int64_t ConvertType(Local value) { + Local js_value = value.As(); + bool lossless; + return js_value->Int64Value(&lossless); +} +} // namespace + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunsequenced" +template +void WASI::WasiFunction::SlowCallback( + const FunctionCallbackInfo& args) { WASI* wasi; - uint32_t argv_offset; - uint32_t argv_buf_offset; char* memory; size_t mem_size; - RETURN_IF_BAD_ARG_COUNT(args, 2); - CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, argv_offset); - CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, argv_buf_offset); + RETURN_IF_BAD_ARG_COUNT(args, sizeof...(Args)); + { + int counter = 0; + uvwasi_errno_t results[sizeof...(Args)] = { + (CheckType(args[counter++]))...}; + for (size_t i = 0; i < sizeof...(Args); i += 1) { + if (results[i] != UVWASI_ESUCCESS) { + args.GetReturnValue().Set(results[i]); + return; + } + } + } ASSIGN_INITIALIZED_OR_RETURN_UNWRAP(&wasi, args.This()); - Debug(wasi, "args_get(%d, %d)\n", argv_offset, argv_buf_offset); GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); - CHECK_BOUNDS_OR_RETURN(args, - mem_size, - argv_buf_offset, - wasi->uvw_.argv_buf_size); - CHECK_BOUNDS_OR_RETURN(args, - mem_size, - argv_offset, - wasi->uvw_.argc * UVWASI_SERDES_SIZE_uint32_t); - std::vector argv(wasi->uvw_.argc); - char* argv_buf = &memory[argv_buf_offset]; - uvwasi_errno_t err = uvwasi_args_get(&wasi->uvw_, argv.data(), argv_buf); + + { + int counter = 0; + args.GetReturnValue().Set( + F(*wasi, {memory, mem_size}, ConvertType(args[counter++])...)); + } +} +#pragma clang diagnostic pop + +template +static void SetFunction(R (*)(WASI&, WasmMemory, Args...), + Environment* env, + const char* name, + Local tmpl) { + WASI::WasiFunction::SetFunction(env, name, tmpl); +} + +uint32_t WASI::ArgsGet(WASI& wasi, + WasmMemory memory, + uint32_t argv_offset, + uint32_t argv_buf_offset) { + Debug(&wasi, "args_get(%d, %d)\n", argv_offset, argv_buf_offset); + + CHECK_BOUNDS_OR_RETURN2( + memory.size, argv_buf_offset, wasi.uvw_.argv_buf_size); + CHECK_BOUNDS_OR_RETURN2( + memory.size, argv_offset, wasi.uvw_.argc * UVWASI_SERDES_SIZE_uint32_t); + std::vector argv(wasi.uvw_.argc); + char* argv_buf = &memory.data[argv_buf_offset]; + uvwasi_errno_t err = uvwasi_args_get(&wasi.uvw_, argv.data(), argv_buf); if (err == UVWASI_ESUCCESS) { - for (size_t i = 0; i < wasi->uvw_.argc; i++) { + for (size_t i = 0; i < wasi.uvw_.argc; i++) { uint32_t offset = static_cast(argv_buf_offset + (argv[i] - argv[0])); - uvwasi_serdes_write_uint32_t(memory, - argv_offset + - (i * UVWASI_SERDES_SIZE_uint32_t), - offset); + uvwasi_serdes_write_uint32_t( + memory.data, argv_offset + (i * UVWASI_SERDES_SIZE_uint32_t), offset); } } - args.GetReturnValue().Set(err); + return err; } - void WASI::ArgsSizesGet(const FunctionCallbackInfo& args) { WASI* wasi; uint32_t argc_offset; @@ -454,25 +567,16 @@ void WASI::EnvironSizesGet(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(err); } - -void WASI::FdAdvise(const FunctionCallbackInfo& args) { - WASI* wasi; - uint32_t fd; - uint64_t offset; - uint64_t len; - uint8_t advice; - RETURN_IF_BAD_ARG_COUNT(args, 4); - CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); - UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, offset); - UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, len); - CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, advice); - ASSIGN_INITIALIZED_OR_RETURN_UNWRAP(&wasi, args.This()); - Debug(wasi, "fd_advise(%d, %d, %d, %d)\n", fd, offset, len, advice); - uvwasi_errno_t err = uvwasi_fd_advise(&wasi->uvw_, fd, offset, len, advice); - args.GetReturnValue().Set(err); +uint32_t WASI::FdAdvise(WASI& wasi, + WasmMemory, + uint32_t fd, + uint64_t offset, + uint64_t len, + uint32_t advice) { + Debug(&wasi, "fd_advise(%d, %d, %d, %d)\n", fd, offset, len, advice); + return uvwasi_fd_advise(&wasi.uvw_, fd, offset, len, advice); } - void WASI::FdAllocate(const FunctionCallbackInfo& args) { WASI* wasi; uint32_t fd; @@ -892,40 +996,24 @@ void WASI::FdRenumber(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(err); } - -void WASI::FdSeek(const FunctionCallbackInfo& args) { - WASI* wasi; - uint32_t fd; - int64_t offset; - uint8_t whence; - uint32_t newoffset_ptr; - char* memory; - size_t mem_size; - RETURN_IF_BAD_ARG_COUNT(args, 4); - CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); - UNWRAP_BIGINT_OR_RETURN(args, args[1], Int64, offset); - CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, whence); - CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, newoffset_ptr); - ASSIGN_INITIALIZED_OR_RETURN_UNWRAP(&wasi, args.This()); - Debug(wasi, "fd_seek(%d, %d, %d, %d)\n", fd, offset, whence, newoffset_ptr); - GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); - CHECK_BOUNDS_OR_RETURN(args, - mem_size, - newoffset_ptr, - UVWASI_SERDES_SIZE_filesize_t); +uint32_t WASI::FdSeek(WASI& wasi, + WasmMemory memory, + uint32_t fd, + int64_t offset, + uint32_t whence, + uint32_t newoffset_ptr) { + Debug(&wasi, "fd_seek(%d, %d, %d, %d)\n", fd, offset, whence, newoffset_ptr); + CHECK_BOUNDS_OR_RETURN2( + memory.size, newoffset_ptr, UVWASI_SERDES_SIZE_filesize_t); uvwasi_filesize_t newoffset; - uvwasi_errno_t err = uvwasi_fd_seek(&wasi->uvw_, - fd, - offset, - whence, - &newoffset); + uvwasi_errno_t err = + uvwasi_fd_seek(&wasi.uvw_, fd, offset, whence, &newoffset); if (err == UVWASI_ESUCCESS) - uvwasi_serdes_write_filesize_t(memory, newoffset_ptr, newoffset); + uvwasi_serdes_write_filesize_t(memory.data, newoffset_ptr, newoffset); - args.GetReturnValue().Set(err); + return err; } - void WASI::FdSync(const FunctionCallbackInfo& args) { WASI* wasi; uint32_t fd; @@ -1673,13 +1761,13 @@ static void Initialize(Local target, tmpl->InstanceTemplate()->SetInternalFieldCount(WASI::kInternalFieldCount); tmpl->Inherit(BaseObject::GetConstructorTemplate(env)); - env->SetProtoMethod(tmpl, "args_get", WASI::ArgsGet); + SetFunction(WASI::ArgsGet, env, "args_get", tmpl); env->SetProtoMethod(tmpl, "args_sizes_get", WASI::ArgsSizesGet); env->SetProtoMethod(tmpl, "clock_res_get", WASI::ClockResGet); env->SetProtoMethod(tmpl, "clock_time_get", WASI::ClockTimeGet); env->SetProtoMethod(tmpl, "environ_get", WASI::EnvironGet); env->SetProtoMethod(tmpl, "environ_sizes_get", WASI::EnvironSizesGet); - env->SetProtoMethod(tmpl, "fd_advise", WASI::FdAdvise); + SetFunction(WASI::FdAdvise, env, "fd_advise", tmpl); env->SetProtoMethod(tmpl, "fd_allocate", WASI::FdAllocate); env->SetProtoMethod(tmpl, "fd_close", WASI::FdClose); env->SetProtoMethod(tmpl, "fd_datasync", WASI::FdDatasync); @@ -1696,7 +1784,7 @@ static void Initialize(Local target, env->SetProtoMethod(tmpl, "fd_read", WASI::FdRead); env->SetProtoMethod(tmpl, "fd_readdir", WASI::FdReaddir); env->SetProtoMethod(tmpl, "fd_renumber", WASI::FdRenumber); - env->SetProtoMethod(tmpl, "fd_seek", WASI::FdSeek); + SetFunction(WASI::FdSeek, env, "fd_seek", tmpl); env->SetProtoMethod(tmpl, "fd_sync", WASI::FdSync); env->SetProtoMethod(tmpl, "fd_tell", WASI::FdTell); env->SetProtoMethod(tmpl, "fd_write", WASI::FdWrite); diff --git a/src/node_wasi.h b/src/node_wasi.h index b3814ddc31033a..8294bfca7206e3 100644 --- a/src/node_wasi.h +++ b/src/node_wasi.h @@ -10,6 +10,10 @@ namespace node { namespace wasi { +struct WasmMemory { + char* data; + size_t size; +}; class WASI : public BaseObject, public mem::NgLibMemoryManager { @@ -23,13 +27,14 @@ class WASI : public BaseObject, SET_MEMORY_INFO_NAME(WASI) SET_SELF_SIZE(WASI) - static void ArgsGet(const v8::FunctionCallbackInfo& args); + static uint32_t ArgsGet(WASI&, WasmMemory, uint32_t, uint32_t); static void ArgsSizesGet(const v8::FunctionCallbackInfo& args); static void ClockResGet(const v8::FunctionCallbackInfo& args); static void ClockTimeGet(const v8::FunctionCallbackInfo& args); static void EnvironGet(const v8::FunctionCallbackInfo& args); static void EnvironSizesGet(const v8::FunctionCallbackInfo& args); - static void FdAdvise(const v8::FunctionCallbackInfo& args); + static uint32_t FdAdvise( + WASI&, WasmMemory, uint32_t, uint64_t, uint64_t, uint32_t); static void FdAllocate(const v8::FunctionCallbackInfo& args); static void FdClose(const v8::FunctionCallbackInfo& args); static void FdDatasync(const v8::FunctionCallbackInfo& args); @@ -49,7 +54,8 @@ class WASI : public BaseObject, static void FdRead(const v8::FunctionCallbackInfo& args); static void FdReaddir(const v8::FunctionCallbackInfo& args); static void FdRenumber(const v8::FunctionCallbackInfo& args); - static void FdSeek(const v8::FunctionCallbackInfo& args); + static uint32_t FdSeek( + WASI&, WasmMemory, uint32_t, int64_t, uint32_t, uint32_t); static void FdSync(const v8::FunctionCallbackInfo& args); static void FdTell(const v8::FunctionCallbackInfo& args); static void FdWrite(const v8::FunctionCallbackInfo& args); @@ -82,6 +88,20 @@ class WASI : public BaseObject, void IncreaseAllocatedSize(size_t size); void DecreaseAllocatedSize(size_t size); + template + class WasiFunction { + public: + static void SetFunction(Environment*, + const char*, + v8::Local); + + private: + static R FastCallback(v8::Local receiver, + Args..., + v8::FastApiCallbackOptions&); + static void SlowCallback(const v8::FunctionCallbackInfo&); + }; + private: ~WASI() override; inline void readUInt8(char* memory, uint8_t* value, uint32_t offset); diff --git a/test/wasi/Makefile b/test/wasi/Makefile index 42d3b4e3fa7a80..14563feda50961 100644 --- a/test/wasi/Makefile +++ b/test/wasi/Makefile @@ -1,12 +1,13 @@ CC = /opt/wasi-sdk/bin/clang -TARGET = wasm32-unknown-wasi +TARGET = wasm32-wasi SYSROOT = +CFLAGS = -D_WASI_EMULATED_PROCESS_CLOCKS -lwasi-emulated-process-clocks OBJ = $(patsubst c/%.c, wasm/%.wasm, $(wildcard c/*.c)) all: $(OBJ) wasm/%.wasm : c/%.c - $(CC) $< --target=$(TARGET) --sysroot=$(SYSROOT) -s -o $@ + $(CC) $< $(CFLAGS) --target=$(TARGET) --sysroot=$(SYSROOT) -s -o $@ .PHONY clean: rm -f $(OBJ) diff --git a/test/wasi/test-wasi.js b/test/wasi/test-wasi.js index 6c00b5a7b72503..d2513a8e87f5c3 100644 --- a/test/wasi/test-wasi.js +++ b/test/wasi/test-wasi.js @@ -50,6 +50,7 @@ if (process.argv[2] === 'wasi-child') { opts.input = options.stdin; const child = cp.spawnSync(process.execPath, [ + ...process.argv.slice(1, -1), '--experimental-wasi-unstable-preview1', __filename, 'wasi-child', diff --git a/test/wasi/wasm/cant_dotdot.wasm b/test/wasi/wasm/cant_dotdot.wasm index 1ffbe23c6afdb2..b078dfca1afbc9 100755 Binary files a/test/wasi/wasm/cant_dotdot.wasm and b/test/wasi/wasm/cant_dotdot.wasm differ diff --git a/test/wasi/wasm/clock_getres.wasm b/test/wasi/wasm/clock_getres.wasm index 510049dca4a009..9d47599c7b9478 100755 Binary files a/test/wasi/wasm/clock_getres.wasm and b/test/wasi/wasm/clock_getres.wasm differ diff --git a/test/wasi/wasm/create_symlink.wasm b/test/wasi/wasm/create_symlink.wasm index 1612e975d87e5d..291f17eedcfa76 100755 Binary files a/test/wasi/wasm/create_symlink.wasm and b/test/wasi/wasm/create_symlink.wasm differ diff --git a/test/wasi/wasm/exitcode.wasm b/test/wasi/wasm/exitcode.wasm index ceb797f8b31ddf..0472e62d851095 100755 Binary files a/test/wasi/wasm/exitcode.wasm and b/test/wasi/wasm/exitcode.wasm differ diff --git a/test/wasi/wasm/fd_prestat_get_refresh.wasm b/test/wasi/wasm/fd_prestat_get_refresh.wasm index 159cfa9e4c8159..d645b42c806791 100755 Binary files a/test/wasi/wasm/fd_prestat_get_refresh.wasm and b/test/wasi/wasm/fd_prestat_get_refresh.wasm differ diff --git a/test/wasi/wasm/follow_symlink.wasm b/test/wasi/wasm/follow_symlink.wasm index f5f236c53f2440..b79242eb13141a 100755 Binary files a/test/wasi/wasm/follow_symlink.wasm and b/test/wasi/wasm/follow_symlink.wasm differ diff --git a/test/wasi/wasm/freopen.wasm b/test/wasi/wasm/freopen.wasm index fb417fbe21fa69..5f774d782332f2 100755 Binary files a/test/wasi/wasm/freopen.wasm and b/test/wasi/wasm/freopen.wasm differ diff --git a/test/wasi/wasm/ftruncate.wasm b/test/wasi/wasm/ftruncate.wasm index a16e90d98ddb28..60542eeb6a0ff6 100755 Binary files a/test/wasi/wasm/ftruncate.wasm and b/test/wasi/wasm/ftruncate.wasm differ diff --git a/test/wasi/wasm/getentropy.wasm b/test/wasi/wasm/getentropy.wasm index 527ac6a17d3008..f9e4cb52869892 100755 Binary files a/test/wasi/wasm/getentropy.wasm and b/test/wasi/wasm/getentropy.wasm differ diff --git a/test/wasi/wasm/getrusage.wasm b/test/wasi/wasm/getrusage.wasm index c9546e232c7956..ff131ee42a09b6 100755 Binary files a/test/wasi/wasm/getrusage.wasm and b/test/wasi/wasm/getrusage.wasm differ diff --git a/test/wasi/wasm/gettimeofday.wasm b/test/wasi/wasm/gettimeofday.wasm index 7629b119d8895d..408a5cfc9ef209 100755 Binary files a/test/wasi/wasm/gettimeofday.wasm and b/test/wasi/wasm/gettimeofday.wasm differ diff --git a/test/wasi/wasm/link.wasm b/test/wasi/wasm/link.wasm index 60f5c07601a2af..4a9719df3035d5 100755 Binary files a/test/wasi/wasm/link.wasm and b/test/wasi/wasm/link.wasm differ diff --git a/test/wasi/wasm/main_args.wasm b/test/wasi/wasm/main_args.wasm index 60cb69defe2d32..1e14b8351b71e6 100755 Binary files a/test/wasi/wasm/main_args.wasm and b/test/wasi/wasm/main_args.wasm differ diff --git a/test/wasi/wasm/notdir.wasm b/test/wasi/wasm/notdir.wasm index 6b592fc17b032f..ae22933603d049 100755 Binary files a/test/wasi/wasm/notdir.wasm and b/test/wasi/wasm/notdir.wasm differ diff --git a/test/wasi/wasm/poll.wasm b/test/wasi/wasm/poll.wasm index 37e17b8d880ad2..22c0fe859d7ad3 100755 Binary files a/test/wasi/wasm/poll.wasm and b/test/wasi/wasm/poll.wasm differ diff --git a/test/wasi/wasm/preopen_populates.wasm b/test/wasi/wasm/preopen_populates.wasm index 618050b3b4c210..1236bbe1cc6e6e 100755 Binary files a/test/wasi/wasm/preopen_populates.wasm and b/test/wasi/wasm/preopen_populates.wasm differ diff --git a/test/wasi/wasm/read_file.wasm b/test/wasi/wasm/read_file.wasm index 1c5e8107e0a9f7..bc1433e09e961e 100755 Binary files a/test/wasi/wasm/read_file.wasm and b/test/wasi/wasm/read_file.wasm differ diff --git a/test/wasi/wasm/read_file_twice.wasm b/test/wasi/wasm/read_file_twice.wasm index 6917a105eb4c08..6b73ae11309fde 100755 Binary files a/test/wasi/wasm/read_file_twice.wasm and b/test/wasi/wasm/read_file_twice.wasm differ diff --git a/test/wasi/wasm/readdir.wasm b/test/wasi/wasm/readdir.wasm index ce6cb4999524db..c315d2d5b8e87e 100755 Binary files a/test/wasi/wasm/readdir.wasm and b/test/wasi/wasm/readdir.wasm differ diff --git a/test/wasi/wasm/stat.wasm b/test/wasi/wasm/stat.wasm index 4a50c0282bb60a..6f924b74d9b22c 100755 Binary files a/test/wasi/wasm/stat.wasm and b/test/wasi/wasm/stat.wasm differ diff --git a/test/wasi/wasm/stdin.wasm b/test/wasi/wasm/stdin.wasm index 3cc548607af2e0..f9ea0fd336d50e 100755 Binary files a/test/wasi/wasm/stdin.wasm and b/test/wasi/wasm/stdin.wasm differ diff --git a/test/wasi/wasm/symlink_escape.wasm b/test/wasi/wasm/symlink_escape.wasm index fcb8cfd5790782..4ff589da7d438c 100755 Binary files a/test/wasi/wasm/symlink_escape.wasm and b/test/wasi/wasm/symlink_escape.wasm differ diff --git a/test/wasi/wasm/symlink_loop.wasm b/test/wasi/wasm/symlink_loop.wasm index 98e5c62f4b8355..409102d7ee21a0 100755 Binary files a/test/wasi/wasm/symlink_loop.wasm and b/test/wasi/wasm/symlink_loop.wasm differ diff --git a/test/wasi/wasm/write_file.wasm b/test/wasi/wasm/write_file.wasm index c21d0c2bfef5c7..005c3da09de043 100755 Binary files a/test/wasi/wasm/write_file.wasm and b/test/wasi/wasm/write_file.wasm differ