Skip to content

Commit b374ec3

Browse files
committed
Fix DOMElement::append() and DOMElement::prepend() hierarchy checks
We could end up in an invalid hierarchy, resulting in infinite loops and eventual crashes if we don't check for the DOM hierarchy validity. Closes GH-11344.
1 parent 154c251 commit b374ec3

File tree

4 files changed

+208
-0
lines changed

4 files changed

+208
-0
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ PHP NEWS
1313
. Fix return value in stub file for DOMNodeList::item. (divinity76)
1414
. Fix spec compliance error with '*' namespace for
1515
DOMDocument::getElementsByTagNameNS. (nielsdos)
16+
. Fix DOMElement::append() and DOMElement::prepend() hierarchy checks.
17+
(nielsdos)
1618

1719
- Opcache:
1820
. Fix allocation loop in zend_shared_alloc_startup(). (nielsdos)

ext/dom/parentnode.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,33 @@ static void dom_fragment_assign_parent_node(xmlNodePtr parentNode, xmlNodePtr fr
255255
fragment->last = NULL;
256256
}
257257

258+
static zend_result dom_hierarchy_node_list(xmlNodePtr parentNode, zval *nodes, int nodesc)
259+
{
260+
for (int i = 0; i < nodesc; i++) {
261+
if (Z_TYPE(nodes[i]) == IS_OBJECT) {
262+
const zend_class_entry *ce = Z_OBJCE(nodes[i]);
263+
264+
if (instanceof_function(ce, dom_node_class_entry)) {
265+
if (dom_hierarchy(parentNode, dom_object_get_node(Z_DOMOBJ_P(nodes + i))) != SUCCESS) {
266+
return FAILURE;
267+
}
268+
}
269+
}
270+
}
271+
272+
return SUCCESS;
273+
}
274+
258275
void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc)
259276
{
260277
xmlNode *parentNode = dom_object_get_node(context);
261278
xmlNodePtr newchild, prevsib;
279+
280+
if (UNEXPECTED(dom_hierarchy_node_list(parentNode, nodes, nodesc) != SUCCESS)) {
281+
php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(context->document));
282+
return;
283+
}
284+
262285
xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
263286

264287
if (fragment == NULL) {
@@ -296,6 +319,11 @@ void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc)
296319
return;
297320
}
298321

322+
if (UNEXPECTED(dom_hierarchy_node_list(parentNode, nodes, nodesc) != SUCCESS)) {
323+
php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(context->document));
324+
return;
325+
}
326+
299327
xmlNodePtr newchild, nextsib;
300328
xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
301329

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
--TEST--
2+
DOMElement::append() with hierarchy changes and errors
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$dom_original = new DOMDocument;
9+
$dom_original->loadXML('<p><b>hello</b><b><i>world</i></b></p>');
10+
11+
echo "-- Append hello with world --\n";
12+
$dom = clone $dom_original;
13+
$b_hello = $dom->firstChild->firstChild;
14+
$b_world = $b_hello->nextSibling;
15+
$b_hello->append($b_world);
16+
var_dump($dom->saveHTML());
17+
18+
echo "-- Append hello with world's child --\n";
19+
$dom = clone $dom_original;
20+
$b_hello = $dom->firstChild->firstChild;
21+
$b_world = $b_hello->nextSibling;
22+
$b_hello->append($b_world->firstChild);
23+
var_dump($dom->saveHTML());
24+
25+
echo "-- Append world's child with hello --\n";
26+
$dom = clone $dom_original;
27+
$b_hello = $dom->firstChild->firstChild;
28+
$b_world = $b_hello->nextSibling;
29+
$b_world->firstChild->append($b_hello);
30+
var_dump($dom->saveHTML());
31+
32+
echo "-- Append hello with itself --\n";
33+
$dom = clone $dom_original;
34+
$b_hello = $dom->firstChild->firstChild;
35+
try {
36+
$b_hello->append($b_hello);
37+
} catch (\DOMException $e) {
38+
echo $e->getMessage(), "\n";
39+
}
40+
var_dump($dom->saveHTML());
41+
42+
echo "-- Append world's i tag with the parent --\n";
43+
$dom = clone $dom_original;
44+
$b_hello = $dom->firstChild->firstChild;
45+
$b_world = $b_hello->nextSibling;
46+
try {
47+
$b_world->firstChild->append($b_world);
48+
} catch (\DOMException $e) {
49+
echo $e->getMessage(), "\n";
50+
}
51+
var_dump($dom->saveHTML());
52+
53+
echo "-- Append from another document --\n";
54+
$dom = clone $dom_original;
55+
$dom2 = new DOMDocument;
56+
$dom2->loadXML('<p>other</p>');
57+
try {
58+
$dom->firstChild->firstChild->prepend($dom2->firstChild);
59+
} catch (\DOMException $e) {
60+
echo $e->getMessage(), "\n";
61+
}
62+
var_dump($dom2->saveHTML());
63+
var_dump($dom->saveHTML());
64+
65+
?>
66+
--EXPECT--
67+
-- Append hello with world --
68+
string(39) "<p><b>hello<b><i>world</i></b></b></p>
69+
"
70+
-- Append hello with world's child --
71+
string(39) "<p><b>hello<i>world</i></b><b></b></p>
72+
"
73+
-- Append world's child with hello --
74+
string(39) "<p><b><i>world<b>hello</b></i></b></p>
75+
"
76+
-- Append hello with itself --
77+
Hierarchy Request Error
78+
string(39) "<p><b>hello</b><b><i>world</i></b></p>
79+
"
80+
-- Append world's i tag with the parent --
81+
Hierarchy Request Error
82+
string(39) "<p><b>hello</b><b><i>world</i></b></p>
83+
"
84+
-- Append from another document --
85+
Wrong Document Error
86+
string(13) "<p>other</p>
87+
"
88+
string(39) "<p><b>hello</b><b><i>world</i></b></p>
89+
"
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
--TEST--
2+
DOMElement::prepend() with hierarchy changes and errors
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$dom_original = new DOMDocument;
9+
$dom_original->loadXML('<p><b>hello</b><b><i>world</i></b></p>');
10+
11+
echo "-- Prepend hello with world --\n";
12+
$dom = clone $dom_original;
13+
$b_hello = $dom->firstChild->firstChild;
14+
$b_world = $b_hello->nextSibling;
15+
$b_hello->prepend($b_world);
16+
var_dump($dom->saveHTML());
17+
18+
echo "-- Prepend hello with world's child --\n";
19+
$dom = clone $dom_original;
20+
$b_hello = $dom->firstChild->firstChild;
21+
$b_world = $b_hello->nextSibling;
22+
$b_hello->prepend($b_world->firstChild);
23+
var_dump($dom->saveHTML());
24+
25+
echo "-- Prepend world's child with hello --\n";
26+
$dom = clone $dom_original;
27+
$b_hello = $dom->firstChild->firstChild;
28+
$b_world = $b_hello->nextSibling;
29+
$b_world->firstChild->prepend($b_hello);
30+
var_dump($dom->saveHTML());
31+
32+
echo "-- Prepend hello with itself --\n";
33+
$dom = clone $dom_original;
34+
$b_hello = $dom->firstChild->firstChild;
35+
try {
36+
$b_hello->prepend($b_hello);
37+
} catch (\DOMException $e) {
38+
echo $e->getMessage(), "\n";
39+
}
40+
var_dump($dom->saveHTML());
41+
42+
echo "-- Prepend world's i tag with the parent --\n";
43+
$dom = clone $dom_original;
44+
$b_hello = $dom->firstChild->firstChild;
45+
$b_world = $b_hello->nextSibling;
46+
try {
47+
$b_world->firstChild->prepend($b_world);
48+
} catch (\DOMException $e) {
49+
echo $e->getMessage(), "\n";
50+
}
51+
var_dump($dom->saveHTML());
52+
53+
echo "-- Append from another document --\n";
54+
$dom = clone $dom_original;
55+
$dom2 = new DOMDocument;
56+
$dom2->loadXML('<p>other</p>');
57+
try {
58+
$dom->firstChild->firstChild->prepend($dom2->firstChild);
59+
} catch (\DOMException $e) {
60+
echo $e->getMessage(), "\n";
61+
}
62+
var_dump($dom2->saveHTML());
63+
var_dump($dom->saveHTML());
64+
65+
?>
66+
--EXPECT--
67+
-- Prepend hello with world --
68+
string(39) "<p><b><b><i>world</i></b>hello</b></p>
69+
"
70+
-- Prepend hello with world's child --
71+
string(39) "<p><b><i>world</i>hello</b><b></b></p>
72+
"
73+
-- Prepend world's child with hello --
74+
string(39) "<p><b><i><b>hello</b>world</i></b></p>
75+
"
76+
-- Prepend hello with itself --
77+
Hierarchy Request Error
78+
string(39) "<p><b>hello</b><b><i>world</i></b></p>
79+
"
80+
-- Prepend world's i tag with the parent --
81+
Hierarchy Request Error
82+
string(39) "<p><b>hello</b><b><i>world</i></b></p>
83+
"
84+
-- Append from another document --
85+
Wrong Document Error
86+
string(13) "<p>other</p>
87+
"
88+
string(39) "<p><b>hello</b><b><i>world</i></b></p>
89+
"

0 commit comments

Comments
 (0)