Skip to content

Commit a97217d

Browse files
committed
ext/dom: fix use-after-free with XPath callback returning foreign-document node.
Fix GH-22554 A PHP XPath callback that returns a node belonging to a document created inside the callback (e.g. $d->documentElement of a throwaway DOMDocument) parks that node in the DOMXPath node_list to keep it alive. When a sibling callback consumes a node navigated into that foreign document, the proxy object was created with the DOMXPath's own dom as parent, so it took a reference on the wrong document and none on the foreign one. On teardown the foreign document could be freed while the proxy still referenced it. Route the proxy factory through dom_xpath_intern_for_doc() so the created object shares the ref_obj of the node's actual document, mirroring the query-result path.
1 parent 9a14b9b commit a97217d

2 files changed

Lines changed: 38 additions & 1 deletion

File tree

ext/dom/tests/gh22554.phpt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
GH-22554 (Use-after-free when an XPath callback returns a node from a document created inside the callback)
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$doc = new DOMDocument;
9+
$doc->loadXML('<root/>');
10+
11+
$xp = new DOMXPath($doc);
12+
$xp->registerNamespace('my', 'my.ns');
13+
14+
$xp->registerPHPFunctionNS('my.ns', 'include', function () {
15+
$d = new DOMDocument;
16+
$d->loadXML('<r><uaf/></r>');
17+
18+
return $d->documentElement;
19+
});
20+
21+
$xp->registerPHPFunctionNS('my.ns', 'process', function ($arg) {
22+
echo "process arg: ", get_class($arg[0]), " ", $arg[0]->nodeName, "\n";
23+
return 'x';
24+
});
25+
26+
$result = $xp->query('my:process(my:include()/uaf)');
27+
var_dump($result->length);
28+
unset($xp);
29+
30+
echo "Done\n";
31+
32+
?>
33+
--EXPECT--
34+
process arg: DOMElement uaf
35+
int(0)
36+
Done

ext/dom/xpath.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ static void dom_xpath_proxy_factory(xmlNodePtr node, zval *child, dom_object *in
7777

7878
ZEND_ASSERT(node->type != XML_NAMESPACE_DECL);
7979

80-
php_dom_create_object(node, child, intern);
80+
dom_xpath_object *xobj = php_xpath_obj_from_obj(&intern->std);
81+
php_dom_create_object(node, child, dom_xpath_intern_for_doc(xobj, node->doc));
8182
}
8283

8384
static dom_xpath_object *dom_xpath_ext_fetch_intern(xmlXPathParserContextPtr ctxt)

0 commit comments

Comments
 (0)