Fix crashes with entity references and predefined entities

Closes GH-13004.
This commit is contained in:
Niels Dossche 2023-12-22 23:24:34 +01:00
parent 42cbace1ad
commit 120bd364aa
4 changed files with 104 additions and 8 deletions

3
NEWS
View file

@ -15,6 +15,9 @@ PHP NEWS
. Fixed bug GH-12996 (Incorrect SCRIPT_NAME with Apache ProxyPassMatch when . Fixed bug GH-12996 (Incorrect SCRIPT_NAME with Apache ProxyPassMatch when
plus in path). (Jakub Zelenka) plus in path). (Jakub Zelenka)
- LibXML:
. Fix crashes with entity references and predefined entities. (nielsdos)
- Opcache: - Opcache:
. Fixed bug GH-13145 (strtok() is not comptime). (ilutov) . Fixed bug GH-13145 (strtok() is not comptime). (ilutov)
. Fixed type inference of range(). (ilutov) . Fixed type inference of range(). (ilutov)

View file

@ -0,0 +1,46 @@
--TEST--
Freeing of a predefined DOMEntityReference
--EXTENSIONS--
dom
--FILE--
<?php
$ref = new DOMEntityReference("amp");
var_dump($ref);
?>
--EXPECT--
object(DOMEntityReference)#1 (17) {
["nodeName"]=>
string(3) "amp"
["nodeValue"]=>
NULL
["nodeType"]=>
int(5)
["parentNode"]=>
NULL
["parentElement"]=>
NULL
["childNodes"]=>
string(22) "(object value omitted)"
["firstChild"]=>
string(22) "(object value omitted)"
["lastChild"]=>
string(22) "(object value omitted)"
["previousSibling"]=>
NULL
["nextSibling"]=>
NULL
["attributes"]=>
NULL
["isConnected"]=>
bool(false)
["namespaceURI"]=>
NULL
["prefix"]=>
string(0) ""
["localName"]=>
NULL
["baseURI"]=>
NULL
["textContent"]=>
string(0) ""
}

View file

@ -9,16 +9,30 @@ $doc->loadXML(<<<'XML'
<?xml version="1.0"?> <?xml version="1.0"?>
<!DOCTYPE books [ <!DOCTYPE books [
<!ENTITY test "entity is only for test purposes"> <!ENTITY test "entity is only for test purposes">
<!ENTITY myimage PUBLIC "-" "mypicture.gif" NDATA GIF>
]> ]>
<container/> <container/>
XML); XML);
$entity = $doc->doctype->entities[0]; $ref1 = $doc->createEntityReference("test");
var_dump($entity->nodeName, $entity->parentNode->nodeName); $ref2 = $doc->createEntityReference("myimage");
$entity1 = $doc->doctype->entities[0];
$entity2 = $doc->doctype->entities[1];
if (strcmp($entity1->nodeName, $entity2->nodeName) < 0) {
// Entity ordering depends on the addresses
[$entity1, $entity2] = [$entity2, $entity1];
}
var_dump($entity1->nodeName, $entity1->parentNode->nodeName);
var_dump($entity2->nodeName, $entity2->parentNode->nodeName);
$doc->removeChild($doc->doctype); $doc->removeChild($doc->doctype);
var_dump($entity->nodeName, $entity->parentNode); var_dump($entity1->nodeName, $entity1->parentNode);
var_dump($entity2->nodeName, $entity2->parentNode);
?> ?>
--EXPECT-- --EXPECT--
string(4) "test" string(4) "test"
string(5) "books" string(5) "books"
string(7) "myimage"
string(5) "books"
string(4) "test" string(4) "test"
NULL NULL
string(7) "myimage"
NULL

View file

@ -207,12 +207,36 @@ static void php_libxml_node_free(xmlNodePtr node)
* dtd is attached to the document. This works around the issue by inspecting the parent directly. */ * dtd is attached to the document. This works around the issue by inspecting the parent directly. */
case XML_ENTITY_DECL: { case XML_ENTITY_DECL: {
xmlEntityPtr entity = (xmlEntityPtr) node; xmlEntityPtr entity = (xmlEntityPtr) node;
php_libxml_unlink_entity_decl(entity); if (entity->etype != XML_INTERNAL_PREDEFINED_ENTITY) {
if (entity->orig != NULL) { php_libxml_unlink_entity_decl(entity);
xmlFree((char *) entity->orig); #if LIBXML_VERSION >= 21200
entity->orig = NULL; xmlFreeEntity(entity);
#else
if (entity->children != NULL && entity->owner && entity == (xmlEntityPtr) entity->children->parent) {
xmlFreeNodeList(entity->children);
}
xmlDictPtr dict = entity->doc != NULL ? entity->doc->dict : NULL;
if (dict == NULL || !xmlDictOwns(dict, entity->name)) {
xmlFree((xmlChar *) entity->name);
}
if (dict == NULL || !xmlDictOwns(dict, entity->ExternalID)) {
xmlFree((xmlChar *) entity->ExternalID);
}
if (dict == NULL || !xmlDictOwns(dict, entity->SystemID)) {
xmlFree((xmlChar *) entity->SystemID);
}
if (dict == NULL || !xmlDictOwns(dict, entity->URI)) {
xmlFree((xmlChar *) entity->URI);
}
if (dict == NULL || !xmlDictOwns(dict, entity->content)) {
xmlFree(entity->content);
}
if (dict == NULL || !xmlDictOwns(dict, entity->orig)) {
xmlFree(entity->orig);
}
xmlFree(entity);
#endif
} }
xmlFreeNode(node);
break; break;
} }
case XML_NOTATION_NODE: { case XML_NOTATION_NODE: {
@ -1386,6 +1410,15 @@ PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node)
case XML_DOCUMENT_NODE: case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE:
break; break;
case XML_ENTITY_REF_NODE:
/* Entity reference nodes are special: their children point to entity declarations,
* but they don't own the declarations and therefore shouldn't free the children.
* Moreover, there can be more than one reference node for a single entity declarations. */
php_libxml_unregister_node(node);
if (node->parent == NULL) {
php_libxml_node_free(node);
}
break;
default: default:
if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) { if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
php_libxml_node_free_list((xmlNodePtr) node->children); php_libxml_node_free_list((xmlNodePtr) node->children);