Skip to content

Commit 8e8d5ce

Browse files
committed
Fix crash in adoptNode with attribute references
I forgot to also update the document reference of attributes, so when there is no document reference anymore from a variable, but still an attribute, this can crash. Fix it by also updating the document references for attributes. Closes GH-13002.
1 parent b2d778c commit 8e8d5ce

File tree

3 files changed

+51
-11
lines changed

3 files changed

+51
-11
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ PHP NEWS
2121
. Fixed bug GH-12870 (Creating an xmlns attribute results in a DOMException).
2222
(nielsdos)
2323
. Fix crash when toggleAttribute() is used without a document. (nielsdos)
24+
. Fix crash in adoptNode with attribute references. (nielsdos)
2425

2526
- FFI:
2627
. Fixed bug GH-9698 (stream_wrapper_register crashes with FFI\CData).

ext/dom/document.c

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,21 +1041,33 @@ PHP_METHOD(DOMDocument, getElementById)
10411041
}
10421042
/* }}} end dom_document_get_element_by_id */
10431043

1044-
static void php_dom_transfer_document_ref(xmlNodePtr node, dom_object *dom_object_document, xmlDocPtr document)
1044+
static zend_always_inline void php_dom_transfer_document_ref_single_node(xmlNodePtr node, php_libxml_ref_obj *new_document)
1045+
{
1046+
php_libxml_node_ptr *iteration_object_ptr = node->_private;
1047+
if (iteration_object_ptr) {
1048+
php_libxml_node_object *iteration_object = iteration_object_ptr->_private;
1049+
ZEND_ASSERT(iteration_object != NULL);
1050+
/* Must increase refcount first because we could be the last reference holder, and the document may be equal. */
1051+
new_document->refcount++;
1052+
php_libxml_decrement_doc_ref(iteration_object);
1053+
iteration_object->document = new_document;
1054+
}
1055+
}
1056+
1057+
static void php_dom_transfer_document_ref(xmlNodePtr node, php_libxml_ref_obj *new_document)
10451058
{
10461059
if (node->children) {
1047-
php_dom_transfer_document_ref(node->children, dom_object_document, document);
1060+
php_dom_transfer_document_ref(node->children, new_document);
10481061
}
1062+
10491063
while (node) {
1050-
php_libxml_node_ptr *iteration_object_ptr = node->_private;
1051-
if (iteration_object_ptr) {
1052-
php_libxml_node_object *iteration_object = iteration_object_ptr->_private;
1053-
ZEND_ASSERT(iteration_object != NULL);
1054-
/* Must increase refcount first because we could be the last reference holder, and the document may be equal. */
1055-
dom_object_document->document->refcount++;
1056-
php_libxml_decrement_doc_ref(iteration_object);
1057-
iteration_object->document = dom_object_document->document;
1064+
if (node->type == XML_ELEMENT_NODE) {
1065+
for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
1066+
php_dom_transfer_document_ref_single_node((xmlNodePtr) attr, new_document);
1067+
}
10581068
}
1069+
1070+
php_dom_transfer_document_ref_single_node(node, new_document);
10591071
node = node->next;
10601072
}
10611073
}
@@ -1073,7 +1085,7 @@ bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, x
10731085
return false;
10741086
}
10751087

1076-
php_dom_transfer_document_ref(nodep, dom_object_new_document, new_document);
1088+
php_dom_transfer_document_ref(nodep, dom_object_new_document->document);
10771089
} else {
10781090
xmlUnlinkNode(nodep);
10791091
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
DOMDocument::adoptNode() with attribute references
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$dom = new DOMDocument;
9+
$root = $dom->appendChild($dom->createElement('root'));
10+
$root->setAttributeNS("urn:a", "a:root1", "bar");
11+
$root1 = $root->getAttributeNodeNS("urn:a", "root1");
12+
echo $dom->saveXML();
13+
14+
$dom = new DOMDocument;
15+
$dom->appendChild($dom->adoptNode($root));
16+
foreach ($dom->documentElement->attributes as $attr) {
17+
var_dump($attr->namespaceURI, $attr->prefix, $attr->localName, $attr->nodeValue);
18+
}
19+
20+
?>
21+
--EXPECT--
22+
<?xml version="1.0"?>
23+
<root xmlns:a="urn:a" a:root1="bar"/>
24+
string(5) "urn:a"
25+
string(1) "a"
26+
string(5) "root1"
27+
string(3) "bar"

0 commit comments

Comments
 (0)