diff --git a/deps/uvwasi/src/fd_table.c b/deps/uvwasi/src/fd_table.c index 0abddb5a21dd36..f3855d4da54dbe 100644 --- a/deps/uvwasi/src/fd_table.c +++ b/deps/uvwasi/src/fd_table.c @@ -291,19 +291,25 @@ uvwasi_errno_t uvwasi_fd_table_init(uvwasi_t* uvwasi, return UVWASI_EINVAL; table->fds = NULL; - r = uv_rwlock_init(&table->rwlock); - if (r != 0) - return uvwasi__translate_uv_error(r); - table->used = 0; table->size = init_size; table->fds = uvwasi__calloc(uvwasi, init_size, sizeof(struct uvwasi_fd_wrap_t*)); - if (table->fds == NULL) { - err = UVWASI_ENOMEM; - goto error_exit; + if (table->fds == NULL) + return UVWASI_ENOMEM; + + r = uv_rwlock_init(&table->rwlock); + if (r != 0) { + err = uvwasi__translate_uv_error(r); + /* Free table->fds and set it to NULL here. This is done explicitly instead + of jumping to error_exit because uvwasi_fd_table_free() relies on fds + being NULL to know whether or not to destroy the rwlock. + */ + uvwasi__free(uvwasi, table->fds); + table->fds = NULL; + return err; } /* Create the stdio FDs. */ @@ -357,11 +363,13 @@ void uvwasi_fd_table_free(uvwasi_t* uvwasi, struct uvwasi_fd_table_t* table) { uvwasi__free(uvwasi, entry); } - uvwasi__free(uvwasi, table->fds); - table->fds = NULL; - table->size = 0; - table->used = 0; - uv_rwlock_destroy(&table->rwlock); + if (table->fds != NULL) { + uvwasi__free(uvwasi, table->fds); + table->fds = NULL; + table->size = 0; + table->used = 0; + uv_rwlock_destroy(&table->rwlock); + } } diff --git a/src/node_wasi.cc b/src/node_wasi.cc index 5f4644e03ac080..699c4c46991696 100644 --- a/src/node_wasi.cc +++ b/src/node_wasi.cc @@ -74,22 +74,64 @@ using v8::ArrayBuffer; using v8::BackingStore; using v8::BigInt; using v8::Context; +using v8::Exception; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; +using v8::Integer; +using v8::Isolate; using v8::Local; +using v8::MaybeLocal; using v8::Object; using v8::String; using v8::Uint32; using v8::Value; +static MaybeLocal WASIException(Local context, + int errorno, + const char* syscall) { + Isolate* isolate = context->GetIsolate(); + Environment* env = Environment::GetCurrent(context); + CHECK_NOT_NULL(env); + const char* err_name = uvwasi_embedder_err_code_to_string(errorno); + Local js_code = OneByteString(isolate, err_name); + Local js_syscall = OneByteString(isolate, syscall); + Local js_msg = js_code; + js_msg = + String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, ", ")); + js_msg = String::Concat(isolate, js_msg, js_syscall); + Local e = + Exception::Error(js_msg)->ToObject(context) + .ToLocalChecked(); + + if (e->Set(context, + env->errno_string(), + Integer::New(isolate, errorno)).IsNothing() || + e->Set(context, env->code_string(), js_code).IsNothing() || + e->Set(context, env->syscall_string(), js_syscall).IsNothing()) { + return MaybeLocal(); + } + + return e; +} + + WASI::WASI(Environment* env, Local object, uvwasi_options_t* options) : BaseObject(env, object) { MakeWeak(); alloc_info_ = MakeAllocator(); options->allocator = &alloc_info_; - CHECK_EQ(uvwasi_init(&uvw_, options), UVWASI_ESUCCESS); + int err = uvwasi_init(&uvw_, options); + if (err != UVWASI_ESUCCESS) { + Local context = env->context(); + MaybeLocal exception = WASIException(context, err, "uvwasi_init"); + + if (exception.IsEmpty()) + return; + + context->GetIsolate()->ThrowException(exception.ToLocalChecked()); + } } diff --git a/test/wasi/test-wasi-options-validation.js b/test/wasi/test-wasi-options-validation.js index f07046b833d3ee..f0aa6932db4ea7 100644 --- a/test/wasi/test-wasi-options-validation.js +++ b/test/wasi/test-wasi-options-validation.js @@ -26,3 +26,8 @@ assert.throws(() => { new WASI({ preopens: 'fhqwhgads' }); }, assert.throws(() => { new WASI(value); }, { code: 'ERR_INVALID_ARG_TYPE' }); }); + +// Verify that exceptions thrown from the binding layer are handled. +assert.throws(() => { + new WASI({ preopens: { '/sandbox': '__/not/real/path' } }); +}, { code: 'UVWASI_ENOENT', message: /uvwasi_init/ });