diff --git a/NEWS b/NEWS index 486a9ab6619..d7f8fd5bf2c 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,8 @@ PHP NEWS . Fixed bug GH-11830 (ParentNode methods should perform their checks upfront). (nielsdos) . Fix viable next sibling search for replaceWith. (nielsdos) + . Fix segfault when DOMParentNode::prepend() is called when the child + disappears. (nielsdos) - FFI: . Fix leaking definitions when using FFI::cdef()->new(...). (ilutov) diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c index 4f3187db969..d6b0705545a 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -280,6 +280,33 @@ static zend_result dom_sanity_check_node_list_for_insertion(php_libxml_ref_obj * return SUCCESS; } +static void dom_pre_insert(xmlNodePtr insertion_point, xmlNodePtr parentNode, xmlNodePtr newchild, xmlNodePtr fragment) +{ + if (!insertion_point) { + /* Place it as last node */ + if (parentNode->children) { + /* There are children */ + newchild->prev = parentNode->last; + parentNode->last->next = newchild; + } else { + /* No children, because they moved out when they became a fragment */ + parentNode->children = newchild; + } + parentNode->last = fragment->last; + } else { + /* Insert fragment before insertion_point */ + fragment->last->next = insertion_point; + if (insertion_point->prev) { + insertion_point->prev->next = newchild; + newchild->prev = insertion_point->prev; + } + insertion_point->prev = fragment->last; + if (parentNode->children == insertion_point) { + parentNode->children = newchild; + } + } +} + void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) { xmlNode *parentNode = dom_object_get_node(context); @@ -331,21 +358,18 @@ void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc) return; } - xmlNodePtr newchild, nextsib; xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); if (fragment == NULL) { return; } - newchild = fragment->children; - nextsib = parentNode->children; + xmlNode *newchild = fragment->children; if (newchild) { xmlNodePtr last = fragment->last; - parentNode->children = newchild; - fragment->last->next = nextsib; - nextsib->prev = last; + + dom_pre_insert(parentNode->children, parentNode, newchild, fragment); dom_fragment_assign_parent_node(parentNode, fragment); @@ -355,33 +379,6 @@ void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc) xmlFree(fragment); } -static void dom_pre_insert(xmlNodePtr insertion_point, xmlNodePtr parentNode, xmlNodePtr newchild, xmlNodePtr fragment) -{ - if (!insertion_point) { - /* Place it as last node */ - if (parentNode->children) { - /* There are children */ - newchild->prev = parentNode->last; - parentNode->last->next = newchild; - } else { - /* No children, because they moved out when they became a fragment */ - parentNode->children = newchild; - } - parentNode->last = fragment->last; - } else { - /* Insert fragment before insertion_point */ - fragment->last->next = insertion_point; - if (insertion_point->prev) { - insertion_point->prev->next = newchild; - newchild->prev = insertion_point->prev; - } - insertion_point->prev = fragment->last; - if (parentNode->children == insertion_point) { - parentNode->children = newchild; - } - } -} - void dom_parent_node_after(dom_object *context, zval *nodes, int nodesc) { /* Spec link: https://dom.spec.whatwg.org/#dom-childnode-after */ diff --git a/ext/dom/tests/gh11906.phpt b/ext/dom/tests/gh11906.phpt new file mode 100644 index 00000000000..4298051e580 --- /dev/null +++ b/ext/dom/tests/gh11906.phpt @@ -0,0 +1,45 @@ +--TEST-- +GH-11906 (prepend without children after creating fragment results in segfault) +--EXTENSIONS-- +dom +--FILE-- +loadXML(<< + + + +XML); + +$container = $doc->documentElement; +$child = $container->firstElementChild; + +$test = $doc->createElement('foo'); +$test->append($child); +echo "--- document output ---\n"; +echo $doc->saveXML(); +echo "--- \$test output ---\n"; +echo $doc->saveXML($test), "\n"; +$test->prepend($child); +echo "--- document output ---\n"; +echo $doc->saveXML(); +echo "--- \$test output ---\n"; +echo $doc->saveXML($test), "\n"; +$test->append($child); +?> +--EXPECT-- +--- document output --- + + + + +--- $test output --- + +--- document output --- + + + + +--- $test output --- +