[njs] Fixed Array.prototype.slice() when array is changed while iterating.

Dmitry Volyntsev xeioex at nginx.com
Fri Jan 14 14:57:21 UTC 2022


details:   https://hg.nginx.org/njs/rev/d940c6aaec5d
branches:  
changeset: 1803:d940c6aaec5d
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Thu Jan 13 15:59:08 2022 +0000
description:
Fixed Array.prototype.slice() when array is changed while iterating.

Previously, the flat array may be converted to a slow one as a
side-effect of a custom getter invocation for a proto array object.
The function erroneously assumed that the this array remains flat
while iterating.

The fix is to eliminate the micro-optimization which uses direct
pointers.

The problem is similar to the previous (9578cc729205) commit.

This closes #445 issue on Github.

diffstat:

 src/njs_array.c          |  49 +++++++++++------------------------------------
 src/test/njs_unit_test.c |  10 +++++++++
 2 files changed, 22 insertions(+), 37 deletions(-)

diffs (95 lines):

diff -r 9578cc729205 -r d940c6aaec5d src/njs_array.c
--- a/src/njs_array.c	Wed Jan 12 17:59:42 2022 +0000
+++ b/src/njs_array.c	Thu Jan 13 15:59:08 2022 +0000
@@ -729,7 +729,7 @@ njs_array_prototype_slice_copy(njs_vm_t 
     uint32_t           n;
     njs_int_t          ret;
     njs_array_t        *array, *keys;
-    njs_value_t        *value, retval, self;
+    njs_value_t        *value, *last, retval, self;
     const u_char       *src, *end;
     njs_slice_prop_t   string_slice;
     njs_string_prop_t  string;
@@ -748,34 +748,7 @@ njs_array_prototype_slice_copy(njs_vm_t 
     n = 0;
 
     if (njs_fast_path(array->object.fast_array)) {
-        if (njs_fast_path(njs_is_fast_array(this))) {
-            value = njs_array_start(this);
-
-            do {
-                if (njs_fast_path(njs_is_valid(&value[start]))) {
-                    array->start[n++] = value[start++];
-
-                } else {
-
-                    /* src value may be in Array.prototype object. */
-
-                    ret = njs_value_property_i64(vm, this, start++,
-                                                 &array->start[n]);
-                    if (njs_slow_path(ret == NJS_ERROR)) {
-                        return NJS_ERROR;
-                    }
-
-                    if (ret != NJS_OK) {
-                        njs_set_invalid(&array->start[n]);
-                    }
-
-                    n++;
-                }
-
-                length--;
-            } while (length != 0);
-
-        } else if (njs_is_string(this) || njs_is_object_string(this)) {
+        if (njs_is_string(this) || njs_is_object_string(this)) {
 
             if (njs_is_object_string(this)) {
                 this = njs_object_value(this);
@@ -816,16 +789,18 @@ njs_array_prototype_slice_copy(njs_vm_t 
 
         } else if (njs_is_object(this)) {
 
-            do {
-                value = &array->start[n++];
-                ret = njs_value_property_i64(vm, this, start++, value);
-
-                if (ret != NJS_OK) {
+            last = &array->start[length];
+
+            for (value = array->start; value < last; value++, start++) {
+                ret = njs_value_property_i64(vm, this, start, value);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    if (ret == NJS_ERROR) {
+                        return NJS_ERROR;
+                    }
+
                     njs_set_invalid(value);
                 }
-
-                length--;
-            } while (length != 0);
+            }
 
         } else {
 
diff -r 9578cc729205 -r d940c6aaec5d src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Wed Jan 12 17:59:42 2022 +0000
+++ b/src/test/njs_unit_test.c	Thu Jan 13 15:59:08 2022 +0000
@@ -4525,6 +4525,16 @@ static njs_unit_test_t  njs_test[] =
                  "Array.prototype.slice.call(1, 0, 2)"),
       njs_str(",") },
 
+    { njs_str("var a = [1, /**/, 3, 4];"
+              "Object.defineProperty(a.__proto__, 1, {"
+              "    get: () => {"
+              "        a.length = 10**6;"
+              "        return 2;"
+              "    }"
+              "});"
+              "a.slice(1)"),
+      njs_str("2,3,4") },
+
     { njs_str("Array.prototype.pop()"),
       njs_str("undefined") },
 



More information about the nginx-devel mailing list