Skip to content

Commit 8226e70

Browse files
committed
Fix #70078: XSL callbacks with nodes as parameter leak memory
The fix for bug #49634 solved a double-free by copying the node with `xmlDocCopyNodeList()`, but the copied node is later freed by calling `xmlFreeNode()` instead of `xmlFreeNodeList()`, thus leaking memory. However, there is no need to treat the node as node list, i.e. to copy also the node's siblings; just creating a recursive copy of the node with `xmlDocCopyNode()` is sufficient, while that also avoids the leak.
1 parent 494615f commit 8226e70

File tree

3 files changed

+55
-1
lines changed

3 files changed

+55
-1
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ PHP NEWS
3636
- Standard:
3737
. Fixed bug #78902 (Memory leak when using stream_filter_append). (liudaixiao)
3838

39+
- XSL:
40+
. Fixed bug #70078 (XSL callbacks with nodes as parameter leak memory). (cmb)
41+
3942
23 Jan 2020, PHP 7.3.14
4043

4144
- Core

ext/xsl/tests/bug70078.phpt

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
--TEST--
2+
Bug #70078 (XSL callbacks with nodes as parameter leak memory)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('xsl')) die('skip xsl extension not available');
6+
?>
7+
--FILE--
8+
<?php
9+
// create big dummy document:
10+
$dom = new \DOMDocument();
11+
$rootNode = $dom->appendChild($dom->createElement('root'));
12+
for ($i = 0; $i <= 100; $i++) {
13+
$level1Node = $rootNode->appendChild($dom->createElement('level1'));
14+
for ($j = 0; $j <= 100; $j++) {
15+
$level2Node = $level1Node->appendChild($dom->createElement('level2'));
16+
for ($k = 0; $k <= 10; $k++) {
17+
$level3Node = $level2Node->appendChild($dom->createElement('level3', 'test'));
18+
}
19+
}
20+
}
21+
22+
function testPhpFunction($node) {
23+
return 'test2';
24+
}
25+
26+
$xslStr = <<<EOF
27+
<?xml version="1.0" encoding="utf-8"?>
28+
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl">
29+
<xsl:template match="root">
30+
<output>
31+
<xsl:for-each select="level1">
32+
<node>
33+
<xsl:value-of select="php:function('testPhpFunction', .)" />
34+
</node>
35+
</xsl:for-each>
36+
</output>
37+
</xsl:template>
38+
</xsl:stylesheet>
39+
EOF;
40+
41+
$xsl = new \DOMDocument();
42+
$xsl->loadXML($xslStr);
43+
$xslt = new \XSLTProcessor();
44+
$xslt->registerPHPFunctions('testPhpFunction');
45+
$xslt->importStyleSheet($xsl);
46+
47+
echo $xslt->transformToXML($dom);
48+
?>
49+
--EXPECT--
50+
<?xml version="1.0"?>
51+
<output xmlns:php="http://php.net/xsl"><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node><node>test2</node></output>

ext/xsl/xsltprocessor.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t
274274
node->parent = nsparent;
275275
node->ns = curns;
276276
} else {
277-
node = xmlDocCopyNodeList(domintern->document->ptr, node);
277+
node = xmlDocCopyNode(node, domintern->document->ptr, 1);
278278
}
279279

280280
php_dom_create_object(node, &child, domintern);

0 commit comments

Comments
 (0)