CVE-2022-32414: Use After Free (CWE-416)
value: Nginx NJS v0.7.2 was discovered to contain a segmentation violation in the function njs_vmcode_interpreter at src/njs_vmcode.c.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 5.5
Published: 2022-06-21T13:15Z
Repository: https://github.com/nginx/njs
Language: C
Files changed: src/njs_promise.c, src/test/njs_unit_test.c
Commit date: 2022-04-26 16:07:02 -0700
Commit message: Fixed aggregation methods of Promise ctor with array-like object.

Previously, while iterating over an array-like object the methods may be
resolved with INVALID values. INVALID value is a special internal type which
should never be visible by ordinary functions.

The fix is to ensure that absent elements are represented by undefined value.

The following methods were fixed Promise.all(), Promise.allSettled(),
    Promise.any(), Promise.race().

This closes #483 issue on Github.

Diff: diff --git a/src/njs_promise.c b/src/njs_promise.c
index 52ae5b570..ea162d533 100644
--- a/src/njs_promise.c
+++ b/src/njs_promise.c
@@ -1367,10 +1367,14 @@ njs_promise_perform_all_handler(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_function_t               *on_fulfilled;
     njs_promise_capability_t     *capability;
     njs_promise_all_context_t    *context;
     njs_promise_iterator_args_t  *pargs;
 
+    if (!njs_is_valid(value)) {
+        value = njs_value_arg(&njs_value_undefined);
+    }
+
     pargs = (njs_promise_iterator_args_t *) args;
 
     capability = pargs->capability;
 
     array = args->data;
@@ -1457,10 +1461,14 @@ njs_promise_perform_all_settled_handler(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_function_t               *on_fulfilled, *on_rejected;
     njs_promise_capability_t     *capability;
     njs_promise_all_context_t    *context;
     njs_promise_iterator_args_t  *pargs;
 
+    if (!njs_is_valid(value)) {
+        value = njs_value_arg(&njs_value_undefined);
+    }
+
     pargs = (njs_promise_iterator_args_t *) args;
 
     capability = pargs->capability;
 
     array = args->data;
@@ -1596,10 +1604,14 @@ njs_promise_perform_any_handler(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_function_t               *on_rejected;
     njs_promise_capability_t     *capability;
     njs_promise_all_context_t    *context;
     njs_promise_iterator_args_t  *pargs;
 
+    if (!njs_is_valid(value)) {
+        value = njs_value_arg(&njs_value_undefined);
+    }
+
     pargs = (njs_promise_iterator_args_t *) args;
 
     capability = pargs->capability;
 
     array = pargs->args.data;
@@ -1743,10 +1755,14 @@ njs_promise_perform_race_handler(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_int_t                    ret;
     njs_value_t                  arguments[2], next;
     njs_promise_capability_t     *capability;
     njs_promise_iterator_args_t  *pargs;
 
+    if (!njs_is_valid(value)) {
+        value = njs_value_arg(&njs_value_undefined);
+    }
+
     pargs = (njs_promise_iterator_args_t *) args;
 
     ret = njs_function_call(vm, pargs->function, pargs->constructor, value,
                             1, &next);
     if (njs_slow_path(ret == NJS_ERROR)) {
diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c
index b28e34fef..def152aa8 100644
--- a/src/test/njs_unit_test.c
+++ b/src/test/njs_unit_test.c
@@ -21147,10 +21147,17 @@ static njs_unit_test_t  njs_externals_test[] =
               "    let r2 = await pr(r + ':async2');"
               "    return r2 + ':r';"
               "}))"
               ".then(v => $r.retval(v))"),
       njs_str("a:async:pr:async2:pr:r,b:async:pr:async2:pr:r,c:async:pr:async2:pr:r") },
+
+    { njs_str("async function f () {"
+              "    var p = await Promise.race({length:1});"
+              "    for (const v in 'test') { }"
+              "};"
+              "f().then(v => $r.retval('done'))"),
+      njs_str("done") },
 };
 
 
 static njs_unit_test_t  njs_async_handler_test[] =
 {

Fixed code:     if (!njs_is_valid(value)) {
        value = njs_value_arg(&njs_value_undefined);
    }

    if (!njs_is_valid(value)) {
        value = njs_value_arg(&njs_value_undefined);
    }

    if (!njs_is_valid(value)) {
        value = njs_value_arg(&njs_value_undefined);
    }

    if (!njs_is_valid(value)) {
        value = njs_value_arg(&njs_value_undefined);
    }


    { njs_str("async function f () {"
              "    var p = await Promise.race({length:1});"
              "    for (const v in 'test') { }"
              "};"
              "f().then(v => $r.retval('done'))"),
      njs_str("done") },