Skip to content

Commit db2d448

Browse files
committed
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4: ext/xsl: XSLTProcessor::importStylesheet() use-after-free during a transformation.
2 parents 57b16fc + b55c619 commit db2d448

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
@@ -55,6 +55,7 @@ extern zend_module_entry xsl_module_entry;
5555
typedef struct xsl_object {
5656
void *ptr;
5757
HashTable *parameter;
58+
uint32_t transform_depth;
5859
bool hasKeys;
5960
php_libxml_ref_obj *sheet_ref_obj;
6061
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
@@ -175,6 +175,12 @@ PHP_METHOD(XSLTProcessor, importStylesheet)
175175
RETURN_THROWS();
176176
}
177177

178+
xsl_object *intern = Z_XSL_P(id);
179+
if (UNEXPECTED(intern->transform_depth > 0)) {
180+
zend_throw_error(NULL, "Cannot call XSLTProcessor::importStylesheet() while a transformation is in progress");
181+
RETURN_THROWS();
182+
}
183+
178184
nodep = php_libxml_import_node(docp);
179185
if (nodep == NULL) {
180186
zend_argument_type_error(1, "must be a valid XML node");
@@ -251,8 +257,6 @@ PHP_METHOD(XSLTProcessor, importStylesheet)
251257
RETURN_FALSE;
252258
}
253259

254-
xsl_object *intern = Z_XSL_P(id);
255-
256260
/* Detach object */
257261
clone_lxml_obj->document->ptr = NULL;
258262
/* The namespace mappings need to be kept alive.
@@ -333,6 +337,8 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl
333337
return NULL;
334338
}
335339

340+
intern->transform_depth++;
341+
336342
if (intern->profiling) {
337343
if (php_check_open_basedir(ZSTR_VAL(intern->profiling))) {
338344
f = NULL;
@@ -438,6 +444,8 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl
438444
efree(intern->doc);
439445
intern->doc = NULL;
440446

447+
intern->transform_depth--;
448+
441449
return newdocp;
442450

443451
}

0 commit comments

Comments
 (0)