]> git.kaiwu.me - njs.git/commitdiff
XML: fixed XMLAttr object.
authorDmitry Volyntsev <xeioex@nginx.com>
Sat, 22 Nov 2025 00:32:15 +0000 (16:32 -0800)
committerDmitry Volyntsev <xeioexception@gmail.com>
Tue, 25 Nov 2025 23:36:16 +0000 (15:36 -0800)
Pointer to xmlAttr to which XMLAttr used to point might become invalid
when the parent XMLNode was modified.

The fix is to hold a pointer to xmlNode.

external/njs_xml_module.c
external/qjs_xml_module.c
test/xml/xml.t.mjs

index 95e5024a0d6a24cda89555b429f453902fdfb431..2438223870c674098e3d5c5fc45d6d840b9d4dcb 100644 (file)
@@ -792,8 +792,8 @@ njs_xml_node_ext_attrs(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t unused,
         return NJS_DECLINED;
     }
 
-    return njs_vm_external_create(vm, retval, njs_xml_attr_proto_id,
-                                  current->properties, 0);
+    return njs_vm_external_create(vm, retval, njs_xml_attr_proto_id, current,
+                                  0);
 }
 
 
@@ -1846,7 +1846,8 @@ error:
 static njs_int_t
 njs_xml_attr_ext_prop_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys)
 {
-    xmlAttr      *node, *current;
+    xmlAttr      *node;
+    xmlNode      *current;
     njs_int_t    ret;
     njs_value_t  *push;
 
@@ -1861,7 +1862,7 @@ njs_xml_attr_ext_prop_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys)
         return NJS_ERROR;
     }
 
-    for (node = current; node != NULL; node = node->next) {
+    for (node = current->properties; node != NULL; node = node->next) {
         if (node->type != XML_ATTRIBUTE_NODE) {
             continue;
         }
@@ -1888,7 +1889,8 @@ njs_xml_attr_ext_prop_handler(njs_vm_t *vm, njs_object_prop_t *prop,
     njs_value_t *retval)
 {
     size_t     size;
-    xmlAttr    *node, *current;
+    xmlAttr    *node;
+    xmlNode    *current;
     njs_int_t  ret;
     njs_str_t  name;
 
@@ -1904,7 +1906,7 @@ njs_xml_attr_ext_prop_handler(njs_vm_t *vm, njs_object_prop_t *prop,
         return NJS_DECLINED;
     }
 
-    for (node = current; node != NULL; node = node->next) {
+    for (node = current->properties; node != NULL; node = node->next) {
         if (node->type != XML_ATTRIBUTE_NODE) {
             continue;
         }
@@ -1921,6 +1923,8 @@ njs_xml_attr_ext_prop_handler(njs_vm_t *vm, njs_object_prop_t *prop,
                                           njs_strlen(node->children->content));
     }
 
+    njs_value_undefined_set(retval);
+
     return NJS_OK;
 }
 
index 8f90ee5cdc3a2636f0182f3a5fbcaae6c8e10154..274eaec000bfadfac7c8e0a13bdd8c9d5bb76d4f 100644 (file)
@@ -27,7 +27,7 @@ typedef struct {
 
 
 typedef struct {
-    xmlAttr        *attr;
+    xmlNode        *node;
     qjs_xml_doc_t  *doc;
 } qjs_xml_attr_t;
 
@@ -93,7 +93,7 @@ static void qjs_xml_node_finalizer(JSRuntime *rt, JSValue val);
 static xmlNode *qjs_xml_node(JSContext *cx, JSValueConst val, xmlDoc **doc);
 
 static JSValue qjs_xml_attr_make(JSContext *cx, qjs_xml_doc_t *doc,
-    xmlAttr *attr);
+    xmlNode *node);
 static int qjs_xml_attr_get_own_property(JSContext *cx,
     JSPropertyDescriptor *pdesc, JSValueConst obj, JSAtom prop);
 static int qjs_xml_attr_get_own_property_names(JSContext *cx,
@@ -955,8 +955,7 @@ qjs_xml_node_get_own_property(JSContext *cx, JSPropertyDescriptor *pdesc,
                 pdesc->flags = JS_PROP_ENUMERABLE;
                 pdesc->getter = JS_UNDEFINED;
                 pdesc->setter = JS_UNDEFINED;
-                pdesc->value  = qjs_xml_attr_make(cx, current->doc,
-                                                   node->properties);
+                pdesc->value  = qjs_xml_attr_make(cx, current->doc, node);
                 if (JS_IsException(pdesc->value)) {
                     return -1;
                 }
@@ -1573,7 +1572,7 @@ qjs_xml_node(JSContext *cx, JSValueConst val, xmlDoc **doc)
 
 
 static JSValue
-qjs_xml_attr_make(JSContext *cx, qjs_xml_doc_t *doc, xmlAttr *attr)
+qjs_xml_attr_make(JSContext *cx, qjs_xml_doc_t *doc, xmlNode *node)
 {
     JSValue         ret;
     qjs_xml_attr_t  *current;
@@ -1584,7 +1583,7 @@ qjs_xml_attr_make(JSContext *cx, qjs_xml_doc_t *doc, xmlAttr *attr)
         return JS_EXCEPTION;
     }
 
-    current->attr = attr;
+    current->node = node;
     current->doc = doc;
     doc->ref_count++;
 
@@ -1623,7 +1622,7 @@ qjs_xml_attr_get_own_property(JSContext *cx, JSPropertyDescriptor *pdesc,
 
     name.length = njs_strlen(name.start);
 
-    for (attr = current->attr; attr != NULL; attr = attr->next) {
+    for (attr = current->node->properties; attr != NULL; attr = attr->next) {
         if (attr->type != XML_ATTRIBUTE_NODE) {
             continue;
         }
@@ -1685,7 +1684,7 @@ qjs_xml_attr_get_own_property_names(JSContext *cx, JSPropertyEnum **ptab,
         return -1;
     }
 
-    for (attr = current->attr; attr != NULL; attr = attr->next) {
+    for (attr = current->node->properties; attr != NULL; attr = attr->next) {
         if (attr->type != XML_ATTRIBUTE_NODE) {
             continue;
         }
index 29e2fb412b178a56da3db942792051a7dd8466cc..51d80217152732310e29a2f141028e07af3e8a6b 100644 (file)
@@ -275,6 +275,19 @@ let modify_tsuite = {
             return xml.serializeToString(doc.note.to);
           },
           expected: `<to>Tove</to>` },
+        { get: (doc) => {
+            let attrs = doc.note.to.$attrs;
+            doc.note.to.removeAllAttributes();
+            return attrs.a;
+          },
+          expected: undefined },
+        { get: (doc) => {
+            let to = doc.note.to;
+            let attrs = doc.note.to.$attrs;
+            to.removeAllAttributes();
+            return attrs.a;
+          },
+          expected: undefined },
         { get: (doc) => {
             delete doc.note.to.$attr$a;
             return xml.serializeToString(doc.note.to);