From 0d212fc9c1129f981aa9931dcff58e598e5383ca Mon Sep 17 00:00:00 2001
From: cjihrig <cjihrig@gmail.com>
Date: Sun, 22 Dec 2019 12:05:39 -0500
Subject: [PATCH] wasi: throw on failed uvwasi_init()

Prior to this commit, if uvwasi_init() failed in any way, Node
would abort due to a failed CHECK_EQ(). This commit changes
that behavior to a thrown exception.

PR-URL: https://github.com/nodejs/node/pull/31076
Fixes: https://github.com/nodejs/node/issues/30878
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
---
 src/node_wasi.cc                          | 44 ++++++++++++++++++++++-
 test/wasi/test-wasi-options-validation.js |  5 +++
 2 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/src/node_wasi.cc b/src/node_wasi.cc
index 3502be69d29ed8..bb985e6d57b368 100644
--- a/src/node_wasi.cc
+++ b/src/node_wasi.cc
@@ -73,22 +73,64 @@ using v8::Array;
 using v8::ArrayBuffer;
 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<Value> WASIException(Local<Context> 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<String> js_code = OneByteString(isolate, err_name);
+  Local<String> js_syscall = OneByteString(isolate, syscall);
+  Local<String> 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<Object> 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<Value>();
+  }
+
+  return e;
+}
+
+
 WASI::WASI(Environment* env,
            Local<Object> 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> context = env->context();
+    MaybeLocal<Value> 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/ });