Skip to content
Merged
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
46 changes: 31 additions & 15 deletions ext/dom/document.c
Original file line number Diff line number Diff line change
Expand Up @@ -1105,8 +1105,9 @@ 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 */
#if LIBXML_VERSION < 21000
/* 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) {
Expand All @@ -1123,30 +1124,43 @@ static xmlChar *libxml_copy_dicted_string(xmlDictPtr src_dict, xmlDictPtr dst_di

static void libxml_fixup_name_and_content(xmlDocPtr src_doc, xmlDocPtr dst_doc, xmlNodePtr node)
{
if (src_doc != NULL && dst_doc != src_doc && src_doc->dict != NULL) {
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_element(xmlDocPtr src_doc, xmlDocPtr dst_doc, xmlNodePtr node)
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);
for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
libxml_fixup_name_and_content(src_doc, dst_doc, (xmlNodePtr) attr);

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);
}
}
}

for (xmlNodePtr child = node->children; child != NULL; child = child->next) {
libxml_fixup_name_and_content_element(src_doc, dst_doc, 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)
{
xmlDocPtr original_document = nodep->doc;
php_libxml_invalidate_node_list_cache_from_doc(original_document);
if (nodep->doc != new_document) {
xmlDocPtr old_doc = nodep->doc;

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);

/* Note for ATTRIBUTE_NODE: specified is always true in ext/dom,
Expand All @@ -1156,16 +1170,18 @@ bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, x
xmlSetTreeDoc(nodep, new_document);
php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(dom_object_new_document);
php_dom_libxml_reconcile_modern(ns_mapper, nodep);
#if LIBXML_VERSION < 21000
libxml_fixup_name_and_content_element(original_document, new_document, nodep);
#endif
} else {
int ret = xmlDOMWrapAdoptNode(NULL, original_document, nodep, new_document, NULL, /* options, unused */ 0);
int ret = xmlDOMWrapAdoptNode(NULL, old_doc, nodep, new_document, NULL, /* options, unused */ 0);
if (UNEXPECTED(ret != 0)) {
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);
} else {
xmlUnlinkNode(nodep);
Expand Down
30 changes: 30 additions & 0 deletions ext/dom/tests/gh19612.phpt
Original file line number Diff line number Diff line change
@@ -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>
2 changes: 1 addition & 1 deletion ext/standard/tests/streams/bug69521.phpt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
--TEST--
Bug #69521 Segfault in gc_collect_cycles()
Bug #69521 (Segfault in gc_collect_cycles())
--FILE--
<?php
$serverUri = "tcp://127.0.0.1:74321";
Expand Down