]> git.kaiwu.me - njs.git/commitdiff
TypedArray: fix numeric property definitions
authorDmitry Volyntsev <xeioex@nginx.com>
Fri, 4 Sep 2026 01:48:39 +0000 (18:48 -0700)
committerDmitry Volyntsev <xeioexception@gmail.com>
Fri, 4 Sep 2026 21:53:08 +0000 (14:53 -0700)
Previously, integer-indexed exotic validation was restricted to keys marked
as strings.  Numeric atoms and the canonical string "NaN" could bypass the
check and be installed as ordinary typed-array properties.

The fix is to validate numeric atoms and canonical numeric strings after key
conversion, and to remove the obsolete source-type flag.

The numeric-key bypass was introduced in b28e50b1 (0.9.0); the incomplete
canonical-index handling dates to bf208048 (0.3.8).

This closes #1098 issue on GitHub.

src/njs_object.h
src/njs_object_prop.c
src/test/njs_unit_test.c

index 814964aa9949655dbb4f7d3852d9f92c7e20f1e0..319de1dbfbdf4d235a4467f9f146bc27b287c796 100644 (file)
@@ -19,7 +19,6 @@ typedef enum {
     NJS_OBJECT_PROP_CONFIGURABLE = 16,
     NJS_OBJECT_PROP_WRITABLE = 32,
     NJS_OBJECT_PROP_UNSET = 64,
-    NJS_OBJECT_PROP_IS_STRING = 128,
 #define NJS_OBJECT_PROP_VALUE_ECW (NJS_OBJECT_PROP_VALUE                     \
                                    | NJS_OBJECT_PROP_ENUMERABLE              \
                                    | NJS_OBJECT_PROP_CONFIGURABLE            \
@@ -264,10 +263,6 @@ njs_object_prop_define_val(njs_vm_t *vm, njs_value_t *object, njs_value_t *name,
         }
     }
 
-    if (njs_is_string(name)) {
-       flags |= NJS_OBJECT_PROP_IS_STRING;
-    }
-
     return njs_object_prop_define(vm, object, name->atom_id, value, flags);
 }
 
index aaf8b683fc3cbad982d231916d671ba26c8786be..6b3181d56e58b3c5d5eabb01c784cdfdc43c95f6 100644 (file)
@@ -136,6 +136,7 @@ njs_object_prop_define(njs_vm_t *vm, njs_value_t *object, unsigned atom_id,
     uint32_t              length, index, set_enumerable, set_configurable,
                           set_writable;
     njs_int_t             ret;
+    njs_str_t             string;
     njs_array_t           *array;
     njs_value_t           key, retval;
     njs_object_prop_t     _prop;
@@ -220,22 +221,38 @@ set_prop:
             return NJS_ERROR;
         }
 
-        if (njs_slow_path(njs_is_typed_array(object) &&
-           (flags & NJS_OBJECT_PROP_IS_STRING)))
-        {
+        if (njs_slow_path(njs_is_typed_array(object))) {
             /* Integer-Indexed Exotic Objects [[DefineOwnProperty]]. */
 
+            if (njs_atom_is_number(atom_id)) {
+                index = njs_atom_number(atom_id);
+
+                if (index < njs_typed_array_length(njs_typed_array(object))) {
+                    goto complete;
+                }
+
+                goto invalid_index;
+            }
+
             ret = njs_atom_to_value(vm, &key, atom_id);
             if (njs_slow_path(ret != NJS_OK)) {
                 return ret;
             }
 
-            if (!isnan(njs_string_to_index(&key))) {
-                njs_type_error(vm, "Invalid typed array index");
-                return NJS_ERROR;
+            if (njs_is_string(&key)) {
+                njs_string_get(vm, &key, &string);
+
+                if (!isnan(njs_string_to_index(&key))
+                    || (string.length == 3
+                        && memcmp(string.start, "NaN", 3) == 0))
+                {
+                    goto invalid_index;
+                }
             }
         }
 
+complete:
+
         /* 6.2.5.6 CompletePropertyDescriptor */
 
         if (njs_is_accessor_descriptor(prop)) {
@@ -559,6 +576,12 @@ exception:
     njs_atom_string_get(vm, atom_id, &pq.fhq.key);
     njs_type_error(vm, "Cannot redefine property: \"%V\"", &pq.fhq.key);
 
+    return NJS_ERROR;
+
+invalid_index:
+
+    njs_type_error(vm, "Invalid typed array index");
+
     return NJS_ERROR;
 }
 
index 2f4b080ef667c6c3dda8b8c263957e794a3fe0b8..d190182510e84c4422b47740ef8ad2c2ac371998 100644 (file)
@@ -7013,6 +7013,17 @@ static njs_unit_test_t  njs_test[] =
               "Object.values(a); a[0]"),
       njs_str("2") },
 
+    { njs_str("[257, '257', NaN, 'NaN', Infinity, 'Infinity', -0, '-0']"
+              ".every(k => { try {"
+              "  Object.defineProperty(new Uint8Array(), k, {value: 1});"
+              "} catch (e) { return e.name == 'TypeError'; } })"),
+      njs_str("true") },
+
+    { njs_str("['01', ' 1', '0x10', '1e3'].every(k => {"
+              "var a = new Uint8Array();"
+              "Object.defineProperty(a, k, {value: 1}); return a[k] == 1; })"),
+      njs_str("true") },
+
     { njs_str("var a = Buffer.from([1]);"
               "Object.defineProperty(a, 'x', {enumerable: true,"
               "  get() { return this === a && this[0]; }});"