Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions ext/dom/document.c
Original file line number Diff line number Diff line change
Expand Up @@ -1086,21 +1086,26 @@ static zend_always_inline void php_dom_transfer_document_ref_single_node(xmlNode
}
}

static void php_dom_transfer_document_ref(xmlNodePtr node, php_libxml_ref_obj *new_document)
static zend_always_inline void php_dom_transfer_document_ref_single_aux(xmlNodePtr node, php_libxml_ref_obj *new_document)
{
if (node->children) {
php_dom_transfer_document_ref(node->children, new_document);
}

while (node) {
if (node->type == XML_ELEMENT_NODE) {
for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
php_dom_transfer_document_ref_single_node((xmlNodePtr) attr, new_document);
php_dom_transfer_document_ref_single_node(node, new_document);
if (node->type == XML_ELEMENT_NODE) {
for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) {
php_dom_transfer_document_ref_single_node((xmlNodePtr) attr, new_document);
for (xmlNodePtr child = attr->children; child; child = child->next) {
php_dom_transfer_document_ref_single_node((xmlNodePtr) child, new_document);
}
}
}
}

php_dom_transfer_document_ref_single_node(node, new_document);
node = node->next;
static void php_dom_transfer_document_ref(xmlNodePtr node, php_libxml_ref_obj *new_document)
{
php_dom_transfer_document_ref_single_aux(node, new_document);
xmlNodePtr tmp = node->children;
while (tmp) {
php_dom_transfer_document_ref_single_aux(tmp, new_document);
tmp = php_dom_next_in_tree_order(tmp, node);
}
}

Expand Down
22 changes: 22 additions & 0 deletions ext/dom/tests/DOMDocument_adoptNode_sibling_subtree.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
DOMDocument::adoptNode() with a node retained under a later sibling
--EXTENSIONS--
dom
--FILE--
<?php

$source = new DOMDocument();
$root = $source->appendChild($source->createElement('root'));
$root->appendChild($source->createElement('first'));
$second = $root->appendChild($source->createElement('second'));
$victim = $second->appendChild($source->createElement('grandchild'));

$destination = new DOMDocument();
$destination->appendChild($destination->adoptNode($root));
unset($destination, $source, $root, $second);

echo $victim->nodeName, PHP_EOL;

?>
--EXPECT--
grandchild
21 changes: 21 additions & 0 deletions ext/dom/tests/gh23352.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
GH-23352 (UAF reading an attribute value node retained across DOMDocument::adoptNode())
--EXTENSIONS--
dom
--FILE--
<?php

$source = new DOMDocument();
$element = $source->appendChild($source->createElement('element'));
$element->setAttribute('attribute', 'victim');
$victim = $element->getAttributeNode('attribute')->firstChild;

$destination = new DOMDocument();
$destination->appendChild($destination->adoptNode($element));
unset($destination, $source, $element);

echo $victim->data, PHP_EOL;

?>
--EXPECT--
victim
21 changes: 21 additions & 0 deletions ext/dom/tests/modern/spec/gh23352.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
GH-23352 (UAF reading an attribute value node retained across Dom\Document::adoptNode())
--EXTENSIONS--
dom
--FILE--
<?php

$source = Dom\XMLDocument::createFromString('<root/>');
$element = $source->documentElement;
$element->setAttribute('attribute', 'victim');
$victim = $element->getAttributeNode('attribute')->firstChild;

$destination = Dom\XMLDocument::createEmpty();
$destination->appendChild($destination->adoptNode($element));
unset($destination, $source, $element);

echo $victim->data, PHP_EOL;

?>
--EXPECT--
victim
Loading