Skip to content

Commit 6db10b6

Browse files
committed
Merge branch 'PHP-8.5'
* PHP-8.5: ext/xsl: XSLTProcessor::importStylesheet() use-after-free during a transformation.
2 parents 7ad3d02 + db2d448 commit 6db10b6

3 files changed

Lines changed: 92 additions & 2 deletions

File tree

ext/xsl/php_xsl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ extern zend_module_entry xsl_module_entry;
5151
typedef struct xsl_object {
5252
void *ptr;
5353
HashTable *parameter;
54+
uint32_t transform_depth;
5455
bool hasKeys;
5556
php_libxml_ref_obj *sheet_ref_obj;
5657
zend_long securityPrefs;

ext/xsl/tests/gh23730.phpt

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
--TEST--
2+
GH-23730 (Use-after-free when a stylesheet is imported during a transformation)
3+
--EXTENSIONS--
4+
dom
5+
xsl
6+
--CREDITS--
7+
djarfluka
8+
--FILE--
9+
<?php
10+
11+
class MyElement extends DOMElement {
12+
public function __destruct() {
13+
/* Runs while the node list of the finished transformation is torn down. */
14+
import_other('destructor');
15+
}
16+
}
17+
18+
function import_other(string $from) {
19+
try {
20+
$GLOBALS['proc']->importStylesheet($GLOBALS['other']);
21+
echo $from, ': no error', PHP_EOL;
22+
} catch (Error $e) {
23+
echo $from, ': ', $e::class, ': ', $e->getMessage(), PHP_EOL;
24+
}
25+
}
26+
27+
function callback($nodes) {
28+
import_other('callback');
29+
return $nodes[0];
30+
}
31+
32+
$xml = new DOMDocument();
33+
$xml->registerNodeClass(DOMElement::class, MyElement::class);
34+
$xml->loadXML('<root><item>a</item></root>');
35+
36+
$xsl = new DOMDocument();
37+
$xsl->loadXML(<<<XML
38+
<?xml version="1.0"?>
39+
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl">
40+
<xsl:template match="/"><xsl:value-of select="php:function('callback', //item)"/></xsl:template>
41+
</xsl:stylesheet>
42+
XML);
43+
44+
$other = new DOMDocument();
45+
$other->loadXML('<?xml version="1.0"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:template match="/">REPLACED</xsl:template></xsl:stylesheet>');
46+
47+
$proc = new XSLTProcessor();
48+
$proc->registerPHPFunctions();
49+
$proc->importStylesheet($xsl);
50+
51+
$GLOBALS['proc'] = $proc;
52+
$GLOBALS['other'] = $other;
53+
54+
var_dump($proc->transformToXml($xml));
55+
var_dump($proc->transformToDoc($xml)->textContent);
56+
57+
$uri = tempnam(sys_get_temp_dir(), 'gh23730');
58+
var_dump($proc->transformToUri($xml, $uri) > 0);
59+
@unlink($uri);
60+
61+
/* Importing outside of a transformation is still allowed. */
62+
var_dump($proc->importStylesheet($other));
63+
var_dump($proc->transformToXml($xml));
64+
65+
?>
66+
--EXPECT--
67+
callback: Error: Cannot call XSLTProcessor::importStylesheet() while a transformation is in progress
68+
destructor: Error: Cannot call XSLTProcessor::importStylesheet() while a transformation is in progress
69+
string(24) "<?xml version="1.0"?>
70+
a
71+
"
72+
callback: Error: Cannot call XSLTProcessor::importStylesheet() while a transformation is in progress
73+
destructor: Error: Cannot call XSLTProcessor::importStylesheet() while a transformation is in progress
74+
string(1) "a"
75+
callback: Error: Cannot call XSLTProcessor::importStylesheet() while a transformation is in progress
76+
destructor: Error: Cannot call XSLTProcessor::importStylesheet() while a transformation is in progress
77+
bool(true)
78+
bool(true)
79+
string(31) "<?xml version="1.0"?>
80+
REPLACED
81+
"

ext/xsl/xsltprocessor.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ PHP_METHOD(XSLTProcessor, importStylesheet)
172172
RETURN_THROWS();
173173
}
174174

175+
xsl_object *intern = Z_XSL_P(id);
176+
if (UNEXPECTED(intern->transform_depth > 0)) {
177+
zend_throw_error(NULL, "Cannot call XSLTProcessor::importStylesheet() while a transformation is in progress");
178+
RETURN_THROWS();
179+
}
180+
175181
nodep = php_libxml_import_node(docp);
176182
if (nodep == NULL) {
177183
zend_argument_type_error(1, "must be a valid XML node");
@@ -240,8 +246,6 @@ PHP_METHOD(XSLTProcessor, importStylesheet)
240246
RETURN_FALSE;
241247
}
242248

243-
xsl_object *intern = Z_XSL_P(id);
244-
245249
/* Detach object */
246250
clone_lxml_obj->document->ptr = NULL;
247251
/* The namespace mappings need to be kept alive.
@@ -319,6 +323,8 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl
319323
return NULL;
320324
}
321325

326+
intern->transform_depth++;
327+
322328
if (intern->profiling) {
323329
if (php_check_open_basedir(ZSTR_VAL(intern->profiling))) {
324330
f = NULL;
@@ -422,6 +428,8 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl
422428
efree(intern->doc);
423429
intern->doc = NULL;
424430

431+
intern->transform_depth--;
432+
425433
return newdocp;
426434

427435
}

0 commit comments

Comments
 (0)