]> git.kaiwu.me - njs.git/commitdiff
Fixed type confusion bug while resolving promises.
authorDmitry Volyntsev <xeioex@nginx.com>
Wed, 19 Jan 2022 13:12:09 +0000 (13:12 +0000)
committerDmitry Volyntsev <xeioex@nginx.com>
Wed, 19 Jan 2022 13:12:09 +0000 (13:12 +0000)
Previously, the internal function njs_promise_perform_then() which
implements PerformPromiseThen() expects its first argument to always be
a promise instance.  This assertion might be invalid because the
functions corresponding to Promise.prototype.then() and
Promise.resolve() incorrectly verified their arguments.

Specifically, the functions recognized their first argument as promise
if it was an object which was an Promise or had Promise object in its
prototype chain.  The later condition is not correct because internal
slots are not inherited according to the spec.

This closes #447 issue in Github.

src/njs_promise.c
src/njs_vmcode.c
test/js/promise_prototype_reject_type_confusion.t.js [new file with mode: 0644]
test/js/promise_prototype_then_type_confusion.t.js [new file with mode: 0644]

index 45ea4921c390a63c8b30665f68e96981795944e6..52ae5b570091989e75bd8b1f8db7be08b4e8a6c2 100644 (file)
@@ -771,25 +771,19 @@ njs_promise_resolve(njs_vm_t *vm, njs_value_t *constructor, njs_value_t *x)
 {
     njs_int_t                 ret;
     njs_value_t               value;
-    njs_object_t              *object;
     njs_promise_capability_t  *capability;
 
     static const njs_value_t  string_constructor = njs_string("constructor");
 
-    if (njs_is_object(x)) {
-        object = njs_object_proto_lookup(njs_object(x), NJS_PROMISE,
-                                         njs_object_t);
-
-        if (object != NULL) {
-            ret = njs_value_property(vm, x, njs_value_arg(&string_constructor),
-                                     &value);
-            if (njs_slow_path(ret == NJS_ERROR)) {
-                return NULL;
-            }
+    if (njs_is_promise(x)) {
+        ret = njs_value_property(vm, x, njs_value_arg(&string_constructor),
+                                 &value);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return NULL;
+        }
 
-            if (njs_values_same(&value, constructor)) {
-                return njs_promise(x);
-            }
+        if (njs_values_same(&value, constructor)) {
+            return njs_promise(x);
         }
     }
 
@@ -875,19 +869,12 @@ njs_promise_prototype_then(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 {
     njs_int_t                 ret;
     njs_value_t               *promise, *fulfilled, *rejected, constructor;
-    njs_object_t              *object;
     njs_function_t            *function;
     njs_promise_capability_t  *capability;
 
     promise = njs_argument(args, 0);
 
-    if (njs_slow_path(!njs_is_object(promise))) {
-        goto failed;
-    }
-
-    object = njs_object_proto_lookup(njs_object(promise), NJS_PROMISE,
-                                     njs_object_t);
-    if (njs_slow_path(object == NULL)) {
+    if (njs_slow_path(!njs_is_promise(promise))) {
         goto failed;
     }
 
@@ -933,6 +920,8 @@ njs_promise_perform_then(njs_vm_t *vm, njs_value_t *value,
     njs_promise_data_t      *data;
     njs_promise_reaction_t  *fulfilled_reaction, *rejected_reaction;
 
+    njs_assert(njs_is_promise(value));
+
     if (!njs_is_function(fulfilled)) {
         fulfilled = njs_value_arg(&njs_value_undefined);
     }
index 7591125504f17ee88dfb2504a2bfc4c80ca9a458..b371c3748e88a2a9e13ba1487d340eb4743b39ea 100644 (file)
@@ -1895,7 +1895,7 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await)
     rejected->args_count = 1;
     rejected->u.native = njs_await_rejected;
 
-    njs_set_object(&val, &promise->object);
+    njs_set_promise(&val, promise);
     njs_set_function(&on_fulfilled, fulfilled);
     njs_set_function(&on_rejected, rejected);
 
diff --git a/test/js/promise_prototype_reject_type_confusion.t.js b/test/js/promise_prototype_reject_type_confusion.t.js
new file mode 100644 (file)
index 0000000..0201f29
--- /dev/null
@@ -0,0 +1,11 @@
+/*---
+includes: []
+flags: [async]
+---*/
+
+Symbol.__proto__ = new Promise(()=>{});
+
+Promise.reject(Symbol)
+.then(v => $DONOTEVALUATE())
+.catch(err => assert.sameValue(err.name, 'Symbol'))
+.then($DONE, $DONE);
diff --git a/test/js/promise_prototype_then_type_confusion.t.js b/test/js/promise_prototype_then_type_confusion.t.js
new file mode 100644 (file)
index 0000000..72b687c
--- /dev/null
@@ -0,0 +1,11 @@
+/*---
+includes: []
+flags: [async]
+---*/
+
+Symbol.__proto__ = new Promise(()=>{});
+
+Promise.resolve(Symbol)
+.then(v => $DONOTEVALUATE())
+.catch(err => assert.sameValue(err.name, 'TypeError'))
+.then($DONE, $DONE);