]> git.kaiwu.me - njs.git/commitdiff
Added missing detached array checks.
authorDmitry Volyntsev <xeioex@nginx.com>
Tue, 18 Nov 2025 02:55:16 +0000 (18:55 -0800)
committerDmitry Volyntsev <xeioexception@gmail.com>
Fri, 21 Nov 2025 22:08:11 +0000 (14:08 -0800)
src/njs_buffer.c
src/njs_encoding.c
src/njs_json.c
src/njs_object.c
src/njs_typed_array.c
src/test/njs_unit_test.c
test/buffer.t.js
test/harness/runTsuite.js
test/text_decoder.t.js
test/text_encoder.t.js

index 93426e5c5a0c4a9ff5ad281445eea5c9931588d1..28fef5d64263e2ad255ad9e5df6cce49d3ec5523 100644 (file)
@@ -568,23 +568,27 @@ njs_buffer_byte_length(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 {
     size_t                       size;
     njs_value_t                  *value;
+    njs_typed_array_t            *array;
     const njs_buffer_encoding_t  *encoding;
 
     value = njs_arg(args, nargs, 1);
 
     switch (value->type) {
     case NJS_TYPED_ARRAY:
-        njs_set_number(retval, njs_typed_array(value)->byte_length);
+    case NJS_DATA_VIEW:
+        array = njs_typed_array(value);
+        if (njs_slow_path(njs_is_detached_buffer(array->buffer))) {
+            njs_set_number(retval, 0);
+            return NJS_OK;
+        }
+
+        njs_set_number(retval, array->byte_length);
         return NJS_OK;
 
     case NJS_ARRAY_BUFFER:
         njs_set_number(retval, njs_array_buffer(value)->size);
         return NJS_OK;
 
-    case NJS_DATA_VIEW:
-        njs_set_number(retval, njs_data_view(value)->byte_length);
-        return NJS_OK;
-
     case NJS_STRING:
         encoding = njs_buffer_encoding(vm, njs_arg(args, nargs, 2), 1);
         if (njs_slow_path(encoding == NULL)) {
@@ -681,6 +685,11 @@ njs_buffer_array_range(njs_vm_t *vm, njs_typed_array_t *array,
 
     num_start = 0;
 
+    if (njs_slow_path(njs_is_detached_buffer(array->buffer))) {
+        njs_type_error(vm, "detached buffer");
+        return NJS_ERROR;
+    }
+
     if (njs_is_defined(start)) {
         ret = njs_value_to_index(vm, njs_value_arg(start), &num_start);
         if (njs_slow_path(ret != NJS_OK)) {
@@ -712,11 +721,6 @@ njs_buffer_array_range(njs_vm_t *vm, njs_typed_array_t *array,
     }
 
     buffer = njs_typed_array_buffer(array);
-    if (njs_slow_path(njs_is_detached_buffer(buffer))) {
-        njs_type_error(vm, "detached buffer");
-        return NJS_ERROR;
-    }
-
     *out_start = &buffer->u.u8[array->offset + num_start];
     *out_end = &buffer->u.u8[array->offset + num_end];
 
@@ -1913,6 +1917,11 @@ njs_buffer_fill_typed_array(njs_vm_t *vm, const njs_value_t *value,
     buffer = njs_typed_array_buffer(array);
 
     arr_from = njs_typed_array(value);
+    if (njs_slow_path(njs_is_detached_buffer(arr_from->buffer))) {
+        njs_type_error(vm, "detached buffer");
+        return NJS_ERROR;
+    }
+
     byte_length = arr_from->byte_length;
     from = &njs_typed_array_buffer(arr_from)->u.u8[arr_from->offset];
 
index 37bdecf360dd3b400e32c6830e87bbf9db78f3fd..b1e1d90332db5a99de38b442ae1f50e32f2c4522 100644 (file)
@@ -199,6 +199,11 @@ njs_text_encoder_encode_into(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     end = start + str.length;
 
     array = njs_typed_array(dest);
+    if (njs_slow_path(njs_is_detached_buffer(array->buffer))) {
+        njs_type_error(vm, "detached buffer");
+        return NJS_ERROR;
+    }
+
     to = njs_typed_array_start(array);
     to_end = to + array->byte_length;
 
@@ -512,12 +517,20 @@ njs_text_decoder_decode(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
         if (njs_is_typed_array(value)) {
             array = njs_typed_array(value);
+            if (njs_slow_path(njs_is_detached_buffer(array->buffer))) {
+                njs_type_error(vm, "detached buffer");
+                return NJS_ERROR;
+            }
 
             start = njs_typed_array_start(array);
             end = start + array->byte_length;
 
         } else if (njs_is_array_buffer(value)) {
             buffer = njs_array_buffer(value);
+            if (njs_slow_path(njs_is_detached_buffer(buffer))) {
+                njs_type_error(vm, "detached buffer");
+                return NJS_ERROR;
+            }
 
             start = buffer->u.u8;
             end = start + buffer->size;
index 1e5f0c406ac1e6d0dbb4780a03c24284e7a77c35..166fb1b1781680ea3eaf701c665d1e6148b6b00e 100644 (file)
@@ -1790,7 +1790,12 @@ njs_dump_terminal(njs_json_stringify_t *stringify, njs_chb_t *chain,
 
         njs_chb_append_literal(chain, "[");
 
-        njs_typed_array_to_chain(stringify->vm, chain, array, NULL);
+        if (!njs_is_detached_buffer(array->buffer)) {
+            njs_typed_array_to_chain(stringify->vm, chain, array, NULL);
+
+        } else {
+            njs_chb_append_literal(chain, "<detached buffer>");
+        }
 
         njs_chb_append_literal(chain, "]");
 
index 97f17d583bb2cccfaa4e3af1c63e32ce46d19a9c..a97c4878516772592ce32a0c3706ded748a85b3b 100644 (file)
@@ -661,6 +661,11 @@ njs_object_enumerate_typed_array(njs_vm_t *vm, const njs_typed_array_t *array,
     njs_value_t  *item;
     njs_array_t  *entry;
 
+    if (njs_slow_path(njs_is_detached_buffer(array->buffer))) {
+        njs_type_error(vm, "detached buffer");
+        return NJS_ERROR;
+    }
+
     length = njs_typed_array_length(array);
 
     ret = njs_array_expand(vm, items, 0, length);
index 39440172ce235712c93ca12d940f6d01f3d9970b..f74f175faefddfab29e6521ed7910d2437b337a1 100644 (file)
@@ -2070,6 +2070,8 @@ njs_typed_array_to_chain(njs_vm_t *vm, njs_chb_t *chain,
     uint32_t           i;
     njs_string_prop_t  separator;
 
+    njs_assert(!njs_is_detached_buffer(array->buffer));
+
     if (sep != NULL && njs_is_string(sep)) {
         (void) njs_string_prop(vm, &separator, sep);
 
index cf266dfc9a84c570a11b2f39e1b5ffa729ea73c8..e205ee2038ba855adb8f8ee2ff936301f99faf9a 100644 (file)
@@ -6259,6 +6259,11 @@ static njs_unit_test_t  njs_test[] =
               "           try {a.set(init,Infinity)} catch (e) {return e.name == 'RangeError'};})"),
       njs_str("true") },
 
+    { njs_str(NJS_INT_TYPED_ARRAY_LIST
+              ".map(v=>{try { var a = new v(1); $262.detachArrayBuffer(a.buffer); Object.entries(a)} "
+              "catch (e) {return e.name}}).every(v=>v === 'TypeError')"),
+      njs_str("true") },
+
     { njs_str("[-1,-1.00001,-Infinity]"
               ".every(v=>{ try {(new Uint8Array(10)).set([], v)} catch (ee) {return ee.name === 'RangeError'}})"),
       njs_str("true") },
index 3782110481a8df404f4ea6aae337e1941d98f58f..ee7831d9bb757a07b342204ba7fc7f2168079a93 100644 (file)
@@ -254,6 +254,10 @@ let fill_tsuite = {
     name: "buf.fill() tests",
     skip: () => (!has_buffer()),
     T: async (params) => {
+        if (params.detach_value) {
+            detach(params.value.buffer);
+        }
+
         let r = params.buf.fill(params.value, params.offset, params.end);
 
         if (r.toString() !== params.expected) {
@@ -274,6 +278,8 @@ let fill_tsuite = {
           exception: 'TypeError: "utf-128" encoding is not supported' },
         { buf: Buffer.from('abc'), value: 'ABCD', offset: 1, expected: 'aAB' },
         { buf: Buffer.from('abc'), value: Buffer.from('def'), expected: 'def' },
+        { buf: Buffer.from('abc'), value: Buffer.from('def'), detach_value: true,
+          exception: 'TypeError: detached buffer' },
         { buf: Buffer.from('def'),
           value: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1),
           expected: 'abc' },
index 2a324fbebd99d7a5a44d6c5397ff6e7089e4ceb0..14deebd95150ee26801dcb4e101e60665a5ea557 100644 (file)
@@ -95,3 +95,10 @@ function merge(to, from) {
     return r;
 }
 
+function detach(ab) {
+    $262.detachArrayBuffer(ab);
+}
+
+function is_detach_available() {
+    return (typeof $262 !== 'undefined' && typeof $262.detachArrayBuffer === 'function');
+}
index afc91fc4627ad624a9b9235df201f14de3ac0ccd..eaf7e191ed09a4dad47f2da7a5592c1efd80d50f 100644 (file)
@@ -133,10 +133,40 @@ let ignoreBOM_tsuite = {
     ],
 };
 
+let detached_tsuite = {
+    name: "TextDecoder() detached buffer test",
+    skip: () => !is_detach_available(),
+
+    T: async (params) => {
+        let td = new TextDecoder('utf-8');
+        let uint8 = new Uint8Array([0]);
+
+        detach(uint8.buffer);
+
+        try {
+            td.decode(uint8);
+
+        } catch (e) {
+            if (e.toString().startsWith('TypeError:')) {
+                return 'SUCCESS';
+            } else {
+                throw e;
+            }
+        }
+
+        throw Error('Expected TypeError not thrown');
+    },
+
+    tests: [
+        { },
+    ],
+};
+
 
 run([
     stream_tsuite,
     fatal_tsuite,
     ignoreBOM_tsuite,
+    detached_tsuite,
 ])
 .then($DONE, $DONE);
index 25173cd1b7232ee39b6a60e15877f0e7392ffbbd..f9274ceba5fbebe23ec8b16a3e76f431ba10001c 100644 (file)
@@ -67,8 +67,38 @@ let encodeinto_tsuite = {
     ],
 };
 
+let detached_tsuite = {
+    name: "TextEncoder() with detached buffer tests",
+    skip: () => (!is_detach_available()),
+
+    T: async (params) => {
+        let td = new TextEncoder();
+        let uint8 = new Uint8Array(10);
+
+        detach(uint8.buffer);
+
+        try {
+            td.encodeInto("test", uint8);
+
+        } catch (e) {
+            if (e.toString().startsWith('TypeError:')) {
+                return 'SUCCESS';
+            } else {
+                throw e;
+            }
+        }
+
+        throw Error('Expected TypeError not thrown');
+    },
+
+    tests: [
+        { },
+    ],
+};
+
 run([
     encode_tsuite,
     encodeinto_tsuite,
+    detached_tsuite,
 ])
 .then($DONE, $DONE);