Skip to content

Commit 043daee

Browse files
committed
Merge branch 'PHP-8.3'
* PHP-8.3: Fix crashes with entity references and predefined entities
2 parents dcd2a0d + 120bd36 commit 043daee

File tree

3 files changed

+101
-8
lines changed

3 files changed

+101
-8
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
Freeing of a predefined DOMEntityReference
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
$ref = new DOMEntityReference("amp");
8+
var_dump($ref);
9+
?>
10+
--EXPECT--
11+
object(DOMEntityReference)#1 (17) {
12+
["nodeName"]=>
13+
string(3) "amp"
14+
["nodeValue"]=>
15+
NULL
16+
["nodeType"]=>
17+
int(5)
18+
["parentNode"]=>
19+
NULL
20+
["parentElement"]=>
21+
NULL
22+
["childNodes"]=>
23+
string(22) "(object value omitted)"
24+
["firstChild"]=>
25+
string(22) "(object value omitted)"
26+
["lastChild"]=>
27+
string(22) "(object value omitted)"
28+
["previousSibling"]=>
29+
NULL
30+
["nextSibling"]=>
31+
NULL
32+
["attributes"]=>
33+
NULL
34+
["isConnected"]=>
35+
bool(false)
36+
["namespaceURI"]=>
37+
NULL
38+
["prefix"]=>
39+
string(0) ""
40+
["localName"]=>
41+
NULL
42+
["baseURI"]=>
43+
NULL
44+
["textContent"]=>
45+
string(0) ""
46+
}

ext/dom/tests/delayed_freeing/entity_declaration.phpt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,30 @@ $doc->loadXML(<<<'XML'
99
<?xml version="1.0"?>
1010
<!DOCTYPE books [
1111
<!ENTITY test "entity is only for test purposes">
12+
<!ENTITY myimage PUBLIC "-" "mypicture.gif" NDATA GIF>
1213
]>
1314
<container/>
1415
XML);
15-
$entity = $doc->doctype->entities[0];
16-
var_dump($entity->nodeName, $entity->parentNode->nodeName);
16+
$ref1 = $doc->createEntityReference("test");
17+
$ref2 = $doc->createEntityReference("myimage");
18+
$entity1 = $doc->doctype->entities[0];
19+
$entity2 = $doc->doctype->entities[1];
20+
if (strcmp($entity1->nodeName, $entity2->nodeName) < 0) {
21+
// Entity ordering depends on the addresses
22+
[$entity1, $entity2] = [$entity2, $entity1];
23+
}
24+
var_dump($entity1->nodeName, $entity1->parentNode->nodeName);
25+
var_dump($entity2->nodeName, $entity2->parentNode->nodeName);
1726
$doc->removeChild($doc->doctype);
18-
var_dump($entity->nodeName, $entity->parentNode);
27+
var_dump($entity1->nodeName, $entity1->parentNode);
28+
var_dump($entity2->nodeName, $entity2->parentNode);
1929
?>
2030
--EXPECT--
2131
string(4) "test"
2232
string(5) "books"
33+
string(7) "myimage"
34+
string(5) "books"
2335
string(4) "test"
2436
NULL
37+
string(7) "myimage"
38+
NULL

ext/libxml/libxml.c

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,36 @@ static void php_libxml_node_free(xmlNodePtr node)
200200
* dtd is attached to the document. This works around the issue by inspecting the parent directly. */
201201
case XML_ENTITY_DECL: {
202202
xmlEntityPtr entity = (xmlEntityPtr) node;
203-
php_libxml_unlink_entity_decl(entity);
204-
if (entity->orig != NULL) {
205-
xmlFree((char *) entity->orig);
206-
entity->orig = NULL;
203+
if (entity->etype != XML_INTERNAL_PREDEFINED_ENTITY) {
204+
php_libxml_unlink_entity_decl(entity);
205+
#if LIBXML_VERSION >= 21200
206+
xmlFreeEntity(entity);
207+
#else
208+
if (entity->children != NULL && entity->owner && entity == (xmlEntityPtr) entity->children->parent) {
209+
xmlFreeNodeList(entity->children);
210+
}
211+
xmlDictPtr dict = entity->doc != NULL ? entity->doc->dict : NULL;
212+
if (dict == NULL || !xmlDictOwns(dict, entity->name)) {
213+
xmlFree((xmlChar *) entity->name);
214+
}
215+
if (dict == NULL || !xmlDictOwns(dict, entity->ExternalID)) {
216+
xmlFree((xmlChar *) entity->ExternalID);
217+
}
218+
if (dict == NULL || !xmlDictOwns(dict, entity->SystemID)) {
219+
xmlFree((xmlChar *) entity->SystemID);
220+
}
221+
if (dict == NULL || !xmlDictOwns(dict, entity->URI)) {
222+
xmlFree((xmlChar *) entity->URI);
223+
}
224+
if (dict == NULL || !xmlDictOwns(dict, entity->content)) {
225+
xmlFree(entity->content);
226+
}
227+
if (dict == NULL || !xmlDictOwns(dict, entity->orig)) {
228+
xmlFree(entity->orig);
229+
}
230+
xmlFree(entity);
231+
#endif
207232
}
208-
xmlFreeNode(node);
209233
break;
210234
}
211235
case XML_NOTATION_NODE: {
@@ -1376,6 +1400,15 @@ PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node)
13761400
case XML_DOCUMENT_NODE:
13771401
case XML_HTML_DOCUMENT_NODE:
13781402
break;
1403+
case XML_ENTITY_REF_NODE:
1404+
/* Entity reference nodes are special: their children point to entity declarations,
1405+
* but they don't own the declarations and therefore shouldn't free the children.
1406+
* Moreover, there can be more than one reference node for a single entity declarations. */
1407+
php_libxml_unregister_node(node);
1408+
if (node->parent == NULL) {
1409+
php_libxml_node_free(node);
1410+
}
1411+
break;
13791412
default:
13801413
if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
13811414
php_libxml_node_free_list((xmlNodePtr) node->children);

0 commit comments

Comments
 (0)