[libxslt] Always set context node before calling XPath iterators



commit 08b62c25871b38d5d573515ca8a065b4b8f64f6b
Author: Nick Wellnhofer <wellnhofer aevum de>
Date:   Wed Feb 20 13:24:37 2019 +0100

    Always set context node before calling XPath iterators
    
    The xmlXPathNext* iterators rely on the XPath context node being set to
    the start node of the iteration. Some parts of the code base like the
    xsl:key functions also leave the context node in an unspecified state.
    Make sure that the context node is reset before invoking the XPath
    iterators. Also backup and restore the context node in
    xsltNumberFormatGetMultipleLevel for good measure.
    
    This bug could also lead to type confusion and invalid reads in
    connection with namespace nodes.
    
    Fixes #13. Also see the Chromium bug report:
    
    https://bugs.chromium.org/p/chromium/issues/detail?id=930663
    
    Thanks to Nicolas Grégoire for the report.

 libxslt/numbers.c         | 31 ++++++++++++++++++++-----------
 tests/docs/bug-218.xml    |  1 +
 tests/general/bug-218.out |  2 ++
 tests/general/bug-218.xsl |  8 ++++++++
 4 files changed, 31 insertions(+), 11 deletions(-)
---
diff --git a/libxslt/numbers.c b/libxslt/numbers.c
index 0d34740b..89e1f668 100644
--- a/libxslt/numbers.c
+++ b/libxslt/numbers.c
@@ -646,42 +646,51 @@ xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context,
 {
     int amount = 0;
     int cnt;
+    xmlNodePtr oldCtxtNode;
     xmlNodePtr ancestor;
     xmlNodePtr preceding;
     xmlXPathParserContextPtr parser;
 
-    context->xpathCtxt->node = node;
+    oldCtxtNode = context->xpathCtxt->node;
     parser = xmlXPathNewParserContext(NULL, context->xpathCtxt);
     if (parser) {
        /* ancestor-or-self::*[count] */
-       for (ancestor = node;
-            (ancestor != NULL) && (ancestor->type != XML_DOCUMENT_NODE);
-            ancestor = xmlXPathNextAncestor(parser, ancestor)) {
-
+       ancestor = node;
+       while ((ancestor != NULL) && (ancestor->type != XML_DOCUMENT_NODE)) {
            if ((fromPat != NULL) &&
                xsltTestCompMatchList(context, ancestor, fromPat))
                break; /* for */
 
+            /*
+             * The xmlXPathNext* iterators require that the context node is
+             * set to the start node. Calls to xsltTestCompMatch* may also
+             * leave the context node in an undefined state, so make sure
+             * that the context node is reset before each iterator invocation.
+             */
+
            if (xsltTestCompMatchCount(context, ancestor, countPat, node)) {
                /* count(preceding-sibling::*) */
                cnt = 1;
-               for (preceding =
-                        xmlXPathNextPrecedingSibling(parser, ancestor);
-                    preceding != NULL;
-                    preceding =
-                       xmlXPathNextPrecedingSibling(parser, preceding)) {
-
+                context->xpathCtxt->node = ancestor;
+                preceding = xmlXPathNextPrecedingSibling(parser, ancestor);
+                while (preceding != NULL) {
                    if (xsltTestCompMatchCount(context, preceding, countPat,
                                                node))
                        cnt++;
+                    context->xpathCtxt->node = ancestor;
+                    preceding =
+                        xmlXPathNextPrecedingSibling(parser, preceding);
                }
                array[amount++] = (double)cnt;
                if (amount >= max)
                    break; /* for */
            }
+            context->xpathCtxt->node = node;
+            ancestor = xmlXPathNextAncestor(parser, ancestor);
        }
        xmlXPathFreeParserContext(parser);
     }
+    context->xpathCtxt->node = oldCtxtNode;
     return amount;
 }
 
diff --git a/tests/docs/bug-218.xml b/tests/docs/bug-218.xml
new file mode 100644
index 00000000..38065478
--- /dev/null
+++ b/tests/docs/bug-218.xml
@@ -0,0 +1 @@
+<top xmlns:ns1="foo"/>
diff --git a/tests/general/bug-218.out b/tests/general/bug-218.out
new file mode 100644
index 00000000..832a29e8
--- /dev/null
+++ b/tests/general/bug-218.out
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+1
diff --git a/tests/general/bug-218.xsl b/tests/general/bug-218.xsl
new file mode 100644
index 00000000..fdbb7b17
--- /dev/null
+++ b/tests/general/bug-218.xsl
@@ -0,0 +1,8 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="1.0">
+  <xsl:key name="aaa" match="/bbb" use="./ccc"/>
+  <xsl:template match="*">
+    <xsl:for-each select="namespace::*[position()=2]">
+      <xsl:number from="key('e','f')"/>
+    </xsl:for-each>
+  </xsl:template>
+</xsl:stylesheet>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]