diff --git a/NEWS b/NEWS index 5ab1602dbd69..97dae0de5565 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,8 @@ PHP NEWS - DOM: . Fixed a use-after-free when cloning a DOMNameSpaceNode after DOMDocument::xinclude(). (iliaal) + . Fixed a use-after-free when Dom\Element::setAttributeNS() replaces the + value of an attribute whose child still has a live wrapper. (iliaal) - Intl: . Fixed a double-free when IntlGregorianCalendar construction fails after diff --git a/ext/dom/element.c b/ext/dom/element.c index 3bce1bdac2a5..8af5d54db338 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -1030,6 +1030,10 @@ static void dom_set_attribute_ns_modern(dom_object *intern, xmlNodePtr elemp, ze if (errorcode == 0) { php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern); xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), uri); + xmlNodePtr existing = (xmlNodePtr) xmlHasNsProp(elemp, localname, ns == NULL ? NULL : ns->href); + if (existing != NULL && existing->type != XML_ATTRIBUTE_DECL) { + node_list_unlink(existing->children); + } xmlAttrPtr attr = xmlSetNsProp(elemp, ns, localname, BAD_CAST value); if (UNEXPECTED(attr == NULL)) { php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); diff --git a/ext/dom/tests/modern/common/Element_setAttributeNS_live_child.phpt b/ext/dom/tests/modern/common/Element_setAttributeNS_live_child.phpt new file mode 100644 index 000000000000..22475c33cf88 --- /dev/null +++ b/ext/dom/tests/modern/common/Element_setAttributeNS_live_child.phpt @@ -0,0 +1,37 @@ +--TEST-- +setAttributeNS() keeps an attribute child that still has a live wrapper +--EXTENSIONS-- +dom +--FILE-- +'); +$el = $doc->documentElement; +$text = $el->getAttributeNodeNS('urn:x', 'attr')->firstChild; +$el->setAttributeNS('urn:x', 'p:attr', 'new'); +echo "prefixed, detached: "; +var_dump($text->parentNode === null); +echo "prefixed, text: "; +var_dump($text->textContent); +echo "prefixed, new value: "; +var_dump($el->getAttributeNS('urn:x', 'attr')); + +$doc = Dom\XMLDocument::createFromString(''); +$el = $doc->documentElement; +$text = $el->getAttributeNode('attr')->firstChild; +$el->setAttributeNS(null, 'attr', 'new'); +echo "no namespace, detached: "; +var_dump($text->parentNode === null); +echo "no namespace, text: "; +var_dump($text->textContent); +echo "no namespace, new value: "; +var_dump($el->getAttribute('attr')); + +?> +--EXPECT-- +prefixed, detached: bool(true) +prefixed, text: string(3) "old" +prefixed, new value: string(3) "new" +no namespace, detached: bool(true) +no namespace, text: string(3) "old" +no namespace, new value: string(3) "new"