From a2deee2141ef2e856b416bc8851b9b7744da78af Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 14 Jan 2023 16:15:01 +0100 Subject: [PATCH 1/2] Fix bug #69168: DomNode::getNodePath() returns invalid path Upon freeing libxslt's context, every document which is not the *main* document will be freed by libxslt. If a node of a document which is not the main document gets returned to userland, we'd free the node twice: - first by the cleanup of the xslt context - and then by our own refcounting mechanism. This was reported in bug #49634, and was fixed by always copying the node (and later re-fixed in bug #70078). The original fix is not entirely correct unfortunately because of the following two main reasons: - modifications to the node will only modify the copy, and not the original - accesses to the parent, path, ... will not work This patch fixes it properly by only copying the node if it origins from a document other than the main document. --- ext/xsl/xsltprocessor.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ext/xsl/xsltprocessor.c b/ext/xsl/xsltprocessor.c index c62c0a13ceaec..8dbe6b27a113b 100644 --- a/ext/xsl/xsltprocessor.c +++ b/ext/xsl/xsltprocessor.c @@ -194,7 +194,19 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t node->parent = nsparent; node->ns = curns; } else { - node = xmlDocCopyNode(node, domintern->document->ptr, 1); + /** + * Upon freeing libxslt's context, every document which is not the *main* document will be freed by libxslt. + * If a node of a document which is *not the main* document gets returned to userland, we'd free the node twice: + * first by the cleanup of the xslt context, and then by our own refcounting mechanism. + * To prevent this, we'll take a copy if the node is not from the main document. + * It is important that we do not copy the node unconditionally, because that means that: + * - modifications to the node will only modify the copy, and not the original + * - accesses to the parent, path, ... will not work + */ + xsltTransformContextPtr transform_ctxt = (xsltTransformContextPtr) ctxt->context->extra; + if (node->doc != transform_ctxt->document->doc) { + node = xmlDocCopyNode(node, domintern->document->ptr, 1); + } } php_dom_create_object(node, &child, domintern); From 9644b932bf9be6d35981f8b678afd3e48fccc5f9 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 14 Jan 2023 16:21:35 +0100 Subject: [PATCH 2/2] =?UTF-8?q?Add=20a=20regression=20test=20for=20bug?= =?UTF-8?q?=C2=A0#69168?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This additionally tests for modifications of nodes. Co-authored-by: juha.ikavalko@agentit.fi --- ext/xsl/tests/bug69168.phpt | 43 +++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 ext/xsl/tests/bug69168.phpt diff --git a/ext/xsl/tests/bug69168.phpt b/ext/xsl/tests/bug69168.phpt new file mode 100644 index 0000000000000..41fc3aafd8209 --- /dev/null +++ b/ext/xsl/tests/bug69168.phpt @@ -0,0 +1,43 @@ +--TEST-- +bug #69168 (DomNode::getNodePath() returns invalid path) +--EXTENSIONS-- +xsl +--FILE-- +bobjoe +EOB; +$xsl = << + + + +
+
+
+
+EOB; + +function getPath($input){ + $input[0]->nodeValue .= 'a'; + return $input[0]->getNodePath() . ' = ' . $input[0]->nodeValue; +} + +$proc = new XSLTProcessor(); +$proc->registerPHPFunctions(); +$xslDoc = new DOMDocument(); +$xslDoc->loadXML($xsl); +@$proc->importStyleSheet($xslDoc); +$xmlDoc = new DOMDocument(); +$xmlDoc->loadXML($xml); +echo @$proc->transformToXML($xmlDoc); + +// Tests modification of the nodes +var_dump($xmlDoc->firstChild->firstChild->firstChild->getNodePath()); +var_dump($xmlDoc->firstChild->firstChild->firstChild->nodeValue); +?> +--EXPECT-- + +/allusers/user[1]/uid = boba
/allusers/user[2]/uid = joea
+string(21) "/allusers/user[1]/uid" +string(4) "boba"