CVE-2022-27008 (published 2022-04-14T15:15Z)

nginx njs 0.7.2 is vulnerable to Buffer Overflow. Type confused in Array.prototype.concat() when a slow array appended element is fast array.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:N/A:P)
CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-120
cwe_name: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
description: The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.
extended_description: A buffer overflow condition exists when a product attempts to put more data in a buffer than it can hold, or when it attempts to put data in a memory area outside of the boundaries of a buffer. The simplest type of error, and the most common cause of buffer overflows, is the "classic" case in which the product copies the buffer without restricting how much is copied. Other variants exist, but the existence of a classic overflow strongly suggests that the programmer is not considering even the most basic of security protections.
         

Fix information:
repo_url: https://github.com/nginx/njs
commit_details: repo_url: https://github.com/nginx/njs
author: Dmitry Volyntsev
author_date: 2022-02-21 16:52:47+00:00
msg: Fixed Array.prototype.concat() when "this" is a slow array.

Previously, when the current appended element is fast array the "this"
array was expected to always be a fast array also.  This may not be the
case when the previous appended element was not fast thus converting
the "this" array to a slow form.

Previous fix introduced in 2c1382bab643 (0.7.2) was not complete, the
correct fix is to never assume "this" is fast, whereas njs_array_add()
may only be called with fast arrays.

This closes #471 issue in Github.
repository: repo_url: https://github.com/nginx/njs
repo_name: nginx/njs
description: A subset of JavaScript language to use in nginx
repo_language: C
owner: nginx
file_changes: filename: njs_array.c
change_type: ModificationType.MODIFY
diff: @@ -364,6 +364,8 @@ njs_array_expand(njs_vm_t *vm, njs_array_t *array, uint32_t prepend,
     uint64_t     size;
     njs_value_t  *start, *old;
 
+    njs_assert(array->object.fast_array);
+
     free_before = array->start - array->data;
     free_after = array->size - array->length - free_before;
 
@@ -1754,9 +1756,10 @@ njs_array_prototype_concat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
                         njs_set_invalid(&retval);
                     }
 
-                    ret = njs_array_add(vm, array, &retval);
-                    if (njs_slow_path(ret != NJS_OK)) {
-                        return NJS_ERROR;
+                    ret = njs_value_property_i64_set(vm, &this, length,
+                                                     &retval);
+                    if (njs_slow_path(ret == NJS_ERROR)) {
+                        return ret;
                     }
                 }
 

programming_language: C
filename: njs_unit_test.c
change_type: ModificationType.MODIFY
diff: @@ -4253,6 +4253,11 @@ static njs_unit_test_t  njs_test[] =
               "njs.dump([a[0], a[33],a.length])"),
       njs_str("[1,1,65]") },
 
+    { njs_str("var a = [1]; a[1111111] = 2;"
+              "var a2 = [3].concat(a, [4]);"
+              "njs.dump(a2)"),
+      njs_str("[3,1,<1111110 empty items>,2,4]") },
+
     { njs_str("var re = /abc/; re[Symbol.isConcatSpreadable] = true;"
               "re[0] = 1, re[1] = 2, re[2] = 3, re.length = 3;"
               "[].concat(re)"),

programming_language: JavaScript