CVE-2021-46462: Insufficient Information (NVD-CWE-noinfo)
value: njs through 0.7.1, used in NGINX, was discovered to contain a segmentation violation via njs_object_set_prototype in /src/njs_object.c.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
CVSS v3 base score: 7.5
Published: 2022-02-14T22:15Z
Repository: https://github.com/nginx/njs
Language: C
Files changed: src/njs_function.c, src/test/njs_unit_test.c
Commit date: 2022-01-19 14:03:49 +0000
Commit message: Fixed Function.prototype.apply() with slow arrays.

Previously, the function had two issues:
   * array->start was referenced without checking for fast array flag
   * the created arguments list was not sanity-checked for its length,
     which can be very large.

The fix is to remove micro-optimization for arrays and introduce limit
size for arguments list.

This closes #449 issue in Github.

Diff: diff --git a/src/njs_function.c b/src/njs_function.c
index 0c23ca05..ae0fa11f 100644
--- a/src/njs_function.c
+++ b/src/njs_function.c
@@ -1383,31 +1383,28 @@ njs_function_prototype_apply(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     this = njs_arg(args, nargs, 1);
     arr_like = njs_arg(args, nargs, 2);
 
     if (njs_is_null_or_undefined(arr_like)) {
         length = 0;
-
-        goto activate;
-
-    } else if (njs_is_array(arr_like)) {
-        arr = arr_like->data.u.array;
-
-        args = arr->start;
-        length = arr->length;
-
         goto activate;
+    }
 
-    } else if (njs_slow_path(!njs_is_object(arr_like))) {
+    if (njs_slow_path(!njs_is_object(arr_like))) {
         njs_type_error(vm, "second argument is not an array-like object");
         return NJS_ERROR;
     }
 
     ret = njs_object_length(vm, arr_like, &length);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
+    if (njs_slow_path(length > 1024)) {
+        njs_internal_error(vm, "argument list is too long");
+        return NJS_ERROR;
+    }
+
     arr = njs_array_alloc(vm, 1, length, NJS_ARRAY_SPARE);
     if (njs_slow_path(arr == NULL)) {
         return NJS_ERROR;
     }
 
diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c
index f332999a..35e2f132 100644
--- a/src/test/njs_unit_test.c
+++ b/src/test/njs_unit_test.c
@@ -10061,10 +10061,14 @@ static njs_unit_test_t  njs_test[] =
 
     { njs_str("var f = function() { return this };"
                  "f.apply(123, {})"),
       njs_str("123") },
 
+    { njs_str("(function(index, ...rest){ return rest[index];})"
+              ".apply({}, [1022].concat(Array(1023).fill(1).map((v,i)=>i.toString(16))))"),
+      njs_str("3fe") },
+
     { njs_str("String.prototype.concat.apply('a', "
                  "{length:2, 0:{toString:function() {return 'b'}}, 1:'c'})"),
       njs_str("abc") },
 
     { njs_str("String.prototype.concat.apply('a',"

Vulnerable code: 
        goto activate;

    } else if (njs_is_array(arr_like)) {
        arr = arr_like->data.u.array;

        args = arr->start;
        length = arr->length;

    } else if (njs_slow_path(!njs_is_object(arr_like))) {
Fixed code:     }
    if (njs_slow_path(!njs_is_object(arr_like))) {
    if (njs_slow_path(length > 1024)) {
        njs_internal_error(vm, "argument list is too long");
        return NJS_ERROR;
    }

    { njs_str("(function(index, ...rest){ return rest[index];})"
              ".apply({}, [1022].concat(Array(1023).fill(1).map((v,i)=>i.toString(16))))"),
      njs_str("3fe") },