Skip to content

Commit cb927e0

Browse files
committed
Merge branch 'PHP-8.2'
* PHP-8.2: Fix segfault when DOMParentNode::prepend() is called when the child disappears
2 parents 038b2ae + 8f1cbc8 commit cb927e0

File tree

2 files changed

+75
-33
lines changed

2 files changed

+75
-33
lines changed

ext/dom/parentnode.c

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,33 @@ static zend_result dom_sanity_check_node_list_for_insertion(php_libxml_ref_obj *
277277
return SUCCESS;
278278
}
279279

280+
static void dom_pre_insert(xmlNodePtr insertion_point, xmlNodePtr parentNode, xmlNodePtr newchild, xmlNodePtr fragment)
281+
{
282+
if (!insertion_point) {
283+
/* Place it as last node */
284+
if (parentNode->children) {
285+
/* There are children */
286+
newchild->prev = parentNode->last;
287+
parentNode->last->next = newchild;
288+
} else {
289+
/* No children, because they moved out when they became a fragment */
290+
parentNode->children = newchild;
291+
}
292+
parentNode->last = fragment->last;
293+
} else {
294+
/* Insert fragment before insertion_point */
295+
fragment->last->next = insertion_point;
296+
if (insertion_point->prev) {
297+
insertion_point->prev->next = newchild;
298+
newchild->prev = insertion_point->prev;
299+
}
300+
insertion_point->prev = fragment->last;
301+
if (parentNode->children == insertion_point) {
302+
parentNode->children = newchild;
303+
}
304+
}
305+
}
306+
280307
void dom_parent_node_append(dom_object *context, zval *nodes, uint32_t nodesc)
281308
{
282309
xmlNode *parentNode = dom_object_get_node(context);
@@ -332,21 +359,18 @@ void dom_parent_node_prepend(dom_object *context, zval *nodes, uint32_t nodesc)
332359

333360
php_libxml_invalidate_node_list_cache_from_doc(parentNode->doc);
334361

335-
xmlNodePtr newchild, nextsib;
336362
xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
337363

338364
if (fragment == NULL) {
339365
return;
340366
}
341367

342-
newchild = fragment->children;
343-
nextsib = parentNode->children;
368+
xmlNode *newchild = fragment->children;
344369

345370
if (newchild) {
346371
xmlNodePtr last = fragment->last;
347-
parentNode->children = newchild;
348-
fragment->last->next = nextsib;
349-
nextsib->prev = last;
372+
373+
dom_pre_insert(parentNode->children, parentNode, newchild, fragment);
350374

351375
dom_fragment_assign_parent_node(parentNode, fragment);
352376

@@ -356,33 +380,6 @@ void dom_parent_node_prepend(dom_object *context, zval *nodes, uint32_t nodesc)
356380
xmlFree(fragment);
357381
}
358382

359-
static void dom_pre_insert(xmlNodePtr insertion_point, xmlNodePtr parentNode, xmlNodePtr newchild, xmlNodePtr fragment)
360-
{
361-
if (!insertion_point) {
362-
/* Place it as last node */
363-
if (parentNode->children) {
364-
/* There are children */
365-
newchild->prev = parentNode->last;
366-
parentNode->last->next = newchild;
367-
} else {
368-
/* No children, because they moved out when they became a fragment */
369-
parentNode->children = newchild;
370-
}
371-
parentNode->last = fragment->last;
372-
} else {
373-
/* Insert fragment before insertion_point */
374-
fragment->last->next = insertion_point;
375-
if (insertion_point->prev) {
376-
insertion_point->prev->next = newchild;
377-
newchild->prev = insertion_point->prev;
378-
}
379-
insertion_point->prev = fragment->last;
380-
if (parentNode->children == insertion_point) {
381-
parentNode->children = newchild;
382-
}
383-
}
384-
}
385-
386383
void dom_parent_node_after(dom_object *context, zval *nodes, uint32_t nodesc)
387384
{
388385
/* Spec link: https://dom.spec.whatwg.org/#dom-childnode-after */

ext/dom/tests/gh11906.phpt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--TEST--
2+
GH-11906 (prepend without children after creating fragment results in segfault)
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
$doc = new DOMDocument;
8+
$doc->loadXML(<<<XML
9+
<?xml version="1.0"?>
10+
<container>
11+
<child/>
12+
</container>
13+
XML);
14+
15+
$container = $doc->documentElement;
16+
$child = $container->firstElementChild;
17+
18+
$test = $doc->createElement('foo');
19+
$test->append($child);
20+
echo "--- document output ---\n";
21+
echo $doc->saveXML();
22+
echo "--- \$test output ---\n";
23+
echo $doc->saveXML($test), "\n";
24+
$test->prepend($child);
25+
echo "--- document output ---\n";
26+
echo $doc->saveXML();
27+
echo "--- \$test output ---\n";
28+
echo $doc->saveXML($test), "\n";
29+
$test->append($child);
30+
?>
31+
--EXPECT--
32+
--- document output ---
33+
<?xml version="1.0"?>
34+
<container>
35+
36+
</container>
37+
--- $test output ---
38+
<foo><child/></foo>
39+
--- document output ---
40+
<?xml version="1.0"?>
41+
<container>
42+
43+
</container>
44+
--- $test output ---
45+
<foo><child/></foo>

0 commit comments

Comments
 (0)