Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ PHP NEWS
. Fixed out-of-bounds write when shm_attach() opens an existing segment with
a size larger than the segment actually is. (David Carlier)

- XSL:
. Fixed use-after-free when a DOMDocument subclass __clone() retains the
stylesheet copy made by XSLTProcessor::importStylesheet(). (iliaal)

- Zip:
. Fixed ZipArchive::addGlob() and ZipArchive::addPattern() ignoring their
default options when no options array is given. (David Carlier)
Expand Down
47 changes: 47 additions & 0 deletions ext/xsl/tests/importStylesheet_clone_retained_document.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
--TEST--
XSLTProcessor::importStylesheet() rejects a stylesheet whose __clone() retains the cloned document
--EXTENSIONS--
dom
xsl
--FILE--
<?php
const STYLESHEET = <<<XML
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/"><out/></xsl:template>
</xsl:stylesheet>
XML;

class Harmless extends DOMDocument {
public function __clone(): void {
}
}

class RetainsDocument extends DOMDocument {
public function __clone(): void {
$GLOBALS['stash'] = $this;
}
}

$doc = new Harmless;
$doc->loadXML(STYLESHEET);
$proc = new XSLTProcessor();
var_dump($proc->importStylesheet($doc));
unset($proc, $doc);

$doc = new RetainsDocument;
$doc->loadXML(STYLESHEET);
$proc = new XSLTProcessor();
try {
var_dump($proc->importStylesheet($doc));
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
$kept = $GLOBALS['stash'];
unset($GLOBALS['stash'], $proc, $doc);
echo get_class($kept), " is still usable: ", $kept->documentElement->nodeName, PHP_EOL;
?>
--EXPECT--
bool(true)
ValueError: XSLTProcessor::importStylesheet(): Argument #1 ($stylesheet) must not have its clone retained by __clone()
RetainsDocument is still usable: xsl:stylesheet
34 changes: 34 additions & 0 deletions ext/xsl/tests/importStylesheet_clone_retained_node.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--TEST--
XSLTProcessor::importStylesheet() rejects a stylesheet whose __clone() retains a node of the cloned document
--EXTENSIONS--
dom
xsl
--FILE--
<?php
class RetainsElement extends DOMDocument {
public function __clone(): void {
$GLOBALS['stash'] = $this->documentElement;
}
}

$doc = new RetainsElement;
$doc->loadXML(<<<XML
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/"><out/></xsl:template>
</xsl:stylesheet>
XML);

$proc = new XSLTProcessor();
try {
var_dump($proc->importStylesheet($doc));
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
$kept = $GLOBALS['stash'];
unset($GLOBALS['stash'], $proc, $doc);
echo get_class($kept), " is still usable: ", $kept->nodeName, PHP_EOL;
?>
--EXPECT--
ValueError: XSLTProcessor::importStylesheet(): Argument #1 ($stylesheet) must not have its clone retained by __clone()
DOMElement is still usable: xsl:stylesheet
6 changes: 6 additions & 0 deletions ext/xsl/xsltprocessor.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,12 @@ PHP_METHOD(XSLTProcessor, importStylesheet)

php_libxml_node_object *clone_lxml_obj = Z_LIBXML_NODE_P(&clone_zv);

if (GC_REFCOUNT(clone) > 1 || clone_lxml_obj->document->refcount > 1) {
OBJ_RELEASE(clone);
zend_argument_value_error(1, "must not have its clone retained by __clone()");
RETURN_THROWS();
}

PHP_LIBXML_SANITIZE_GLOBALS(parse);
ZEND_DIAGNOSTIC_IGNORED_START("-Wdeprecated-declarations")
xmlSubstituteEntitiesDefault(1);
Expand Down
Loading