Skip to content

Commit d28e55c

Browse files
devnexenSakiTakamachi
authored andcommitted
Fix GH-21496: UAF in dom_objects_free_storage.
Cloning a non-document DOM node creates a copy within the same xmlDoc. importStylesheet then passes that original document to xsltParseStylesheetDoc, which may strip and free nodes during processing, invalidating PHP objects still referencing them. Resolve the ownerDocument for non-document nodes and clone that instead. close GH-21500
1 parent 97bb48e commit d28e55c

File tree

3 files changed

+65
-1
lines changed

3 files changed

+65
-1
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ PHP NEWS
6565
- XSL:
6666
. Fix GH-21357 (XSLTProcessor works with DOMDocument, but fails with
6767
Dom\XMLDocument). (ndossche)
68+
. Fixed bug GH-21496 (UAF in dom_objects_free_storage).
69+
(David Carlier/ndossche)
6870

6971
12 Mar 2026, PHP 8.4.19
7072

ext/xsl/tests/gh21496.phpt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
GH-21496 (UAF in dom_objects_free_storage when importing non-document node as stylesheet)
3+
--EXTENSIONS--
4+
dom
5+
xsl
6+
--CREDITS--
7+
YuanchengJiang
8+
--FILE--
9+
<?php
10+
$comment = new DOMComment("my value");
11+
$doc = new DOMDocument();
12+
$doc->loadXML(<<<XML
13+
<container/>
14+
XML);
15+
$doc->documentElement->appendChild($comment);
16+
unset($doc);
17+
$proc = new XSLTProcessor();
18+
var_dump($proc->importStylesheet($comment));
19+
$sxe = simplexml_load_string('<container/>');
20+
$proc = new XSLTProcessor();
21+
$proc->importStylesheet($sxe);
22+
?>
23+
--EXPECTF--
24+
Warning: XSLTProcessor::importStylesheet(): compilation error: file %s line 1 element container in %s on line %d
25+
26+
Warning: XSLTProcessor::importStylesheet(): xsltParseStylesheetProcess : document is not a stylesheet in %s on line %d
27+
bool(false)
28+
29+
Warning: XSLTProcessor::importStylesheet(): compilation error: element container in %s on line %d
30+
31+
Warning: XSLTProcessor::importStylesheet(): xsltParseStylesheetProcess : document is not a stylesheet in %s on line %d
32+

ext/xsl/xsltprocessor.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,18 +167,48 @@ PHP_METHOD(XSLTProcessor, importStylesheet)
167167
xsltStylesheetPtr sheetp;
168168
bool clone_docu = false;
169169
xmlNode *nodep = NULL;
170-
zval *cloneDocu, rv, clone_zv;
170+
zval *cloneDocu, rv, clone_zv, owner_zv;
171171
zend_string *member;
172172

173173
id = ZEND_THIS;
174174
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &docp) == FAILURE) {
175175
RETURN_THROWS();
176176
}
177177

178+
nodep = php_libxml_import_node(docp);
179+
if (nodep == NULL) {
180+
zend_argument_type_error(1, "must be a valid XML node");
181+
RETURN_THROWS();
182+
}
183+
184+
if (Z_OBJ_HANDLER_P(docp, clone_obj) == NULL) {
185+
zend_argument_type_error(1, "must be a cloneable node");
186+
RETURN_THROWS();
187+
}
188+
189+
ZVAL_UNDEF(&owner_zv);
190+
191+
/* For non-document nodes, resolve the ownerDocument and clone that
192+
* instead as xsltParseStylesheetProcess may free nodes in the document. */
193+
if (nodep->type != XML_DOCUMENT_NODE && nodep->type != XML_HTML_DOCUMENT_NODE) {
194+
if (nodep->doc == NULL) {
195+
zend_argument_value_error(1, "must be part of a document");
196+
RETURN_THROWS();
197+
}
198+
199+
/* See dom_import_simplexml_common */
200+
201+
dom_object *nodeobj = (dom_object *) ((char *) Z_OBJ_P(docp) - Z_OBJ_HT_P(docp)->offset);
202+
203+
php_dom_create_object((xmlNodePtr) nodep->doc, &owner_zv, nodeobj);
204+
docp = &owner_zv;
205+
}
206+
178207
/* libxslt uses _private, so we must copy the imported
179208
* stylesheet document otherwise the node proxies will be a mess.
180209
* We will clone the object and detach the libxml internals later. */
181210
zend_object *clone = Z_OBJ_HANDLER_P(docp, clone_obj)(Z_OBJ_P(docp));
211+
zval_ptr_dtor(&owner_zv);
182212
if (!clone) {
183213
RETURN_THROWS();
184214
}

0 commit comments

Comments
 (0)