1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fix GH-19612: Mitigate libxml2 tree dictionary bug

This code is very similar to code on PHP 8.4 and higher, but the
mitigation is extended to entity references and to attribute children.

Closes GH-19620.
This commit is contained in:
Niels Dossche
2025-08-28 21:55:33 +02:00
parent f9ce6d8f3a
commit 080fd14458
3 changed files with 92 additions and 2 deletions

3
NEWS
View File

@@ -15,6 +15,9 @@ PHP NEWS
. Fixed date_sunrise() and date_sunset() with partial-hour UTC offset. . Fixed date_sunrise() and date_sunset() with partial-hour UTC offset.
(ilutov) (ilutov)
- DOM:
. Fixed bug GH-19612 (Mitigate libxml2 tree dictionary bug). (nielsdos)
- FPM: - FPM:
. Fixed failed debug assertion when php_admin_value setting fails. (ilutov) . Fixed failed debug assertion when php_admin_value setting fails. (ilutov)

View File

@@ -1076,10 +1076,62 @@ static void php_dom_transfer_document_ref(xmlNodePtr node, php_libxml_ref_obj *n
} }
} }
/* Workaround for bug that was fixed in https://github.com/GNOME/libxml2/commit/4bc3ebf3eaba352fbbce2ef70ad00a3c7752478a
* and https://github.com/GNOME/libxml2/commit/bc7ab5a2e61e4b36accf6803c5b0e245c11154b1 */
#if LIBXML_VERSION < 21300
static xmlChar *libxml_copy_dicted_string(xmlDictPtr src_dict, xmlDictPtr dst_dict, xmlChar *str)
{
if (str == NULL) {
return NULL;
}
if (xmlDictOwns(src_dict, str) == 1) {
if (dst_dict == NULL) {
return xmlStrdup(str);
}
return BAD_CAST xmlDictLookup(dst_dict, str, -1);
}
return str;
}
static void libxml_fixup_name_and_content(xmlDocPtr src_doc, xmlDocPtr dst_doc, xmlNodePtr node)
{
if (node->type == XML_ENTITY_REF_NODE) {
node->children = NULL; /* Break link with original document. */
}
if (src_doc != NULL && src_doc->dict != NULL) {
ZEND_ASSERT(dst_doc != src_doc);
node->name = libxml_copy_dicted_string(src_doc->dict, dst_doc->dict, BAD_CAST node->name);
node->content = libxml_copy_dicted_string(src_doc->dict, NULL, node->content);
}
}
static void libxml_fixup_name_and_content_outer(xmlDocPtr src_doc, xmlDocPtr dst_doc, xmlNodePtr node)
{
libxml_fixup_name_and_content(src_doc, dst_doc, node);
if (node->type == XML_ELEMENT_NODE) {
for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
libxml_fixup_name_and_content(src_doc, dst_doc, (xmlNodePtr) attr);
for (xmlNodePtr attr_child = attr->children; attr_child != NULL; attr_child = attr_child->next) {
libxml_fixup_name_and_content(src_doc, dst_doc, attr_child);
}
}
}
if (node->type == XML_ELEMENT_NODE || node->type == XML_ATTRIBUTE_NODE) {
for (xmlNodePtr child = node->children; child != NULL; child = child->next) {
libxml_fixup_name_and_content_outer(src_doc, dst_doc, child);
}
}
}
#endif
bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, xmlDocPtr new_document) bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, xmlDocPtr new_document)
{ {
php_libxml_invalidate_node_list_cache_from_doc(nodep->doc); xmlDocPtr old_doc = nodep->doc;
if (nodep->doc != new_document) {
php_libxml_invalidate_node_list_cache_from_doc(old_doc);
if (old_doc != new_document) {
php_libxml_invalidate_node_list_cache(dom_object_new_document->document); php_libxml_invalidate_node_list_cache(dom_object_new_document->document);
/* Note for ATTRIBUTE_NODE: specified is always true in ext/dom, /* Note for ATTRIBUTE_NODE: specified is always true in ext/dom,
@@ -1089,6 +1141,11 @@ bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, x
return false; return false;
} }
#if LIBXML_VERSION < 21300
/* Must be first before transferring the ref to ensure the old document dictionary stays alive. */
libxml_fixup_name_and_content_outer(old_doc, new_document, nodep);
#endif
php_dom_transfer_document_ref(nodep, dom_object_new_document->document); php_dom_transfer_document_ref(nodep, dom_object_new_document->document);
} else { } else {
xmlUnlinkNode(nodep); xmlUnlinkNode(nodep);

View File

@@ -0,0 +1,30 @@
--TEST--
GH-19612 (Mitigate libxml2 tree dictionary bug)
--EXTENSIONS--
dom
--FILE--
<?php
$xml = new DOMDocument;
$xml->loadXML(<<<XML
<!DOCTYPE root [
<!ENTITY foo "foo">
]>
<root><el x="&foo;"/></root>
XML);
$html = new DOMDocument;
$html->loadHTML('<p>foo</p>', LIBXML_NOERROR);
$p = $html->documentElement->firstChild->firstChild;
$p->appendChild($html->adoptNode($xml->documentElement->firstElementChild->cloneNode(true)));
echo $html->saveXML();
echo $xml->saveXML();
?>
--EXPECT--
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>foo<el x="&foo;"/></p></body></html>
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY foo "foo">
]>
<root><el x="&foo;"/></root>