Skip to content
Permalink
Browse files Browse the repository at this point in the history
Fixed type confusion bug while resolving promises.
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.
  • Loading branch information
xeioex committed Jan 19, 2022
1 parent 1406f64 commit 6a40a85
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 23 deletions.
33 changes: 11 additions & 22 deletions src/njs_promise.c
Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/njs_vmcode.c
Expand Up @@ -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);

Expand Down
11 changes: 11 additions & 0 deletions test/js/promise_prototype_reject_type_confusion.t.js
@@ -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);
11 changes: 11 additions & 0 deletions test/js/promise_prototype_then_type_confusion.t.js
@@ -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);

0 comments on commit 6a40a85

Please sign in to comment.