[libxslt] Reuse XPath context when compiling stylesheets



commit 4cefab0663a77791f4daba1b0700ac8ad74a150d
Author: Nick Wellnhofer <wellnhofer aevum de>
Date:   Mon Apr 22 17:05:29 2019 +0200

    Reuse XPath context when compiling stylesheets
    
    Also introduce xsltParseStylesheetUser to parse a stylesheet using
    a custom xsltStylesheet struct.
    
    Set XPath resource limits when fuzzing stylesheets.

 libxslt/pattern.c       |  14 +--
 libxslt/xslt.c          | 226 ++++++++++++++++++++++++------------------------
 libxslt/xsltInternals.h |  16 ++--
 libxslt/xsltutils.c     |  26 +-----
 tests/fuzz/xslt.c       |  22 +++--
 5 files changed, 142 insertions(+), 162 deletions(-)
---
diff --git a/libxslt/pattern.c b/libxslt/pattern.c
index 55778774..32de1b48 100644
--- a/libxslt/pattern.c
+++ b/libxslt/pattern.c
@@ -342,20 +342,14 @@ xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp,
            xsltAllocateExtra(ctxt->style);
     }
     if (op == XSLT_OP_PREDICATE) {
-       xmlXPathContextPtr xctxt;
+        int flags = 0;
 
-       if (ctxt->style != NULL)
-           xctxt = xmlXPathNewContext(ctxt->style->doc);
-       else
-           xctxt = xmlXPathNewContext(NULL);
 #ifdef XML_XPATH_NOVAR
        if (novar != 0)
-           xctxt->flags = XML_XPATH_NOVAR;
+           flags = XML_XPATH_NOVAR;
 #endif
-       if (ctxt->style != NULL)
-           xctxt->dict = ctxt->style->dict;
-       comp->steps[comp->nbStep].comp = xmlXPathCtxtCompile(xctxt, value);
-       xmlXPathFreeContext(xctxt);
+       comp->steps[comp->nbStep].comp = xsltXPathCompileFlags(ctxt->style,
+                value, flags);
        if (comp->steps[comp->nbStep].comp == NULL) {
            xsltTransformError(NULL, ctxt->style, ctxt->elem,
                    "Failed to compile predicate\n");
diff --git a/libxslt/xslt.c b/libxslt/xslt.c
index 5fda7604..53d43c38 100644
--- a/libxslt/xslt.c
+++ b/libxslt/xslt.c
@@ -592,10 +592,6 @@ xsltCompilationCtxtFree(xsltCompilerCtxtPtr cctxt)
     }
     if (cctxt->tmpList != NULL)
        xsltPointerListFree(cctxt->tmpList);
-#ifdef XSLT_REFACTORED_XPATHCOMP
-    if (cctxt->xpathCtxt != NULL)
-       xmlXPathFreeContext(cctxt->xpathCtxt);
-#endif
     if (cctxt->nsAliases != NULL)
        xsltFreeNsAliasList(cctxt->nsAliases);
 
@@ -631,15 +627,6 @@ xsltCompilationCtxtCreate(xsltStylesheetPtr style) {
     if (ret->tmpList == NULL) {
        goto internal_err;
     }
-#ifdef XSLT_REFACTORED_XPATHCOMP
-    /*
-    * Create the XPath compilation context in order
-    * to speed up precompilation of XPath expressions.
-    */
-    ret->xpathCtxt = xmlXPathNewContext(NULL);
-    if (ret->xpathCtxt == NULL)
-       goto internal_err;
-#endif
 
     return(ret);
 
@@ -761,14 +748,15 @@ internal_err:
 #endif
 
 /**
- * xsltNewStylesheet:
+ * xsltNewStylesheetInternal:
+ * @parent:  the parent stylesheet or NULL
  *
  * Create a new XSLT Stylesheet
  *
  * Returns the newly allocated xsltStylesheetPtr or NULL in case of error
  */
-xsltStylesheetPtr
-xsltNewStylesheet(void) {
+static xsltStylesheetPtr
+xsltNewStylesheetInternal(xsltStylesheetPtr parent) {
     xsltStylesheetPtr ret = NULL;
 
     ret = (xsltStylesheetPtr) xmlMalloc(sizeof(xsltStylesheet));
@@ -779,6 +767,7 @@ xsltNewStylesheet(void) {
     }
     memset(ret, 0, sizeof(xsltStylesheet));
 
+    ret->parent = parent;
     ret->omitXmlDeclaration = -1;
     ret->standalone = -1;
     ret->decimalFormat = xsltNewDecimalFormat(NULL, NULL);
@@ -799,6 +788,21 @@ xsltNewStylesheet(void) {
        "creating dictionary for stylesheet\n");
 #endif
 
+    if (parent == NULL) {
+        ret->principal = ret;
+
+        ret->xpathCtxt = xmlXPathNewContext(NULL);
+        if (ret->xpathCtxt == NULL) {
+            xsltTransformError(NULL, NULL, NULL,
+                    "xsltNewStylesheet: xmlXPathNewContext failed\n");
+            goto internal_err;
+        }
+        if (xmlXPathContextSetCache(ret->xpathCtxt, 1, -1, 0) == -1)
+            goto internal_err;
+    } else {
+        ret->principal = parent->principal;
+    }
+
     xsltInit();
 
     return(ret);
@@ -809,6 +813,18 @@ internal_err:
     return(NULL);
 }
 
+/**
+ * xsltNewStylesheet:
+ *
+ * Create a new XSLT Stylesheet
+ *
+ * Returns the newly allocated xsltStylesheetPtr or NULL in case of error
+ */
+xsltStylesheetPtr
+xsltNewStylesheet(void) {
+    return xsltNewStylesheetInternal(NULL);
+}
+
 /**
  * xsltAllocateExtra:
  * @style:  an XSLT stylesheet
@@ -1065,6 +1081,9 @@ xsltFreeStylesheet(xsltStylesheetPtr style)
 #endif
     xmlDictFree(style->dict);
 
+    if (style->xpathCtxt != NULL)
+       xmlXPathFreeContext(style->xpathCtxt);
+
     memset(style, -1, sizeof(xsltStylesheet));
     xmlFree(style);
 }
@@ -6532,54 +6551,68 @@ xsltParseStylesheetImportedDoc(xmlDocPtr doc,
     if (doc == NULL)
        return(NULL);
 
-    retStyle = xsltNewStylesheet();
+    retStyle = xsltNewStylesheetInternal(parentStyle);
     if (retStyle == NULL)
        return(NULL);
-    /*
-    * Set the importing stylesheet module; also used to detect recursion.
-    */
-    retStyle->parent = parentStyle;
+
+    if (xsltParseStylesheetUser(retStyle, doc) != 0) {
+        xsltFreeStylesheet(retStyle);
+        return(NULL);
+    }
+
+    return(retStyle);
+}
+
+/**
+ * xsltParseStylesheetUser:
+ * @doc:  an xmlDoc parsed XML
+ * @style: pointer to the stylesheet
+ * @parentStyle: pointer to the parent stylesheet (if it exists)
+ *
+ * Parse an XSLT stylesheet with a user-provided stylesheet struct.
+ *
+ * Returns 0 if successful, -1 in case of error.
+ */
+int
+xsltParseStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc) {
+    if ((style == NULL) || (doc == NULL))
+       return(-1);
+
     /*
     * Adjust the string dict.
     */
     if (doc->dict != NULL) {
-        xmlDictFree(retStyle->dict);
-       retStyle->dict = doc->dict;
+        xmlDictFree(style->dict);
+       style->dict = doc->dict;
 #ifdef WITH_XSLT_DEBUG
         xsltGenericDebug(xsltGenericDebugContext,
            "reusing dictionary from %s for stylesheet\n",
            doc->URL);
 #endif
-       xmlDictReference(retStyle->dict);
+       xmlDictReference(style->dict);
     }
 
     /*
     * TODO: Eliminate xsltGatherNamespaces(); we must not restrict
     *  the stylesheet to containt distinct namespace prefixes.
     */
-    xsltGatherNamespaces(retStyle);
+    xsltGatherNamespaces(style);
 
 #ifdef XSLT_REFACTORED
     {
        xsltCompilerCtxtPtr cctxt;
        xsltStylesheetPtr oldCurSheet;
 
-       if (parentStyle == NULL) {
+       if (style->parent == NULL) {
            xsltPrincipalStylesheetDataPtr principalData;
            /*
-           * Principal stylesheet
-           * --------------------
-           */
-           retStyle->principal = retStyle;
-           /*
            * Create extra data for the principal stylesheet.
            */
            principalData = xsltNewPrincipalStylesheetData();
            if (principalData == NULL) {
-               xsltFreeStylesheet(retStyle);
-               return(NULL);
+               return(-1);
            }
-           retStyle->principalData = principalData;
+           style->principalData = principalData;
            /*
            * Create the compilation context
            * ------------------------------
@@ -6587,14 +6620,13 @@ xsltParseStylesheetImportedDoc(xmlDocPtr doc,
            * This is currently the only function where the
            * compilation context is created.
            */
-           cctxt = xsltCompilationCtxtCreate(retStyle);
+           cctxt = xsltCompilationCtxtCreate(style);
            if (cctxt == NULL) {
-               xsltFreeStylesheet(retStyle);
-               return(NULL);
+               return(-1);
            }
-           retStyle->compCtxt = (void *) cctxt;
-           cctxt->style = retStyle;
-           cctxt->dict = retStyle->dict;
+           style->compCtxt = (void *) cctxt;
+           cctxt->style = style;
+           cctxt->dict = style->dict;
            cctxt->psData = principalData;
            /*
            * Push initial dummy node info.
@@ -6605,22 +6637,21 @@ xsltParseStylesheetImportedDoc(xmlDocPtr doc,
            /*
            * Imported stylesheet.
            */
-           retStyle->principal = parentStyle->principal;
-           cctxt = parentStyle->compCtxt;
-           retStyle->compCtxt = cctxt;
+           cctxt = style->parent->compCtxt;
+           style->compCtxt = cctxt;
        }
        /*
        * Save the old and set the current stylesheet structure in the
        * compilation context.
        */
        oldCurSheet = cctxt->style;
-       cctxt->style = retStyle;
+       cctxt->style = style;
 
-       retStyle->doc = doc;
-       xsltParseStylesheetProcess(retStyle, doc);
+       style->doc = doc;
+       xsltParseStylesheetProcess(style, doc);
 
        cctxt->style = oldCurSheet;
-       if (parentStyle == NULL) {
+       if (style->parent == NULL) {
            /*
            * Pop the initial dummy node info.
            */
@@ -6631,65 +6662,54 @@ xsltParseStylesheetImportedDoc(xmlDocPtr doc,
            * stylesheets.
            * TODO: really?
            */
-           /* retStyle->compCtxt = NULL; */
+           /* style->compCtxt = NULL; */
        }
-       /*
-       * Free the stylesheet if there were errors.
-       */
-       if (retStyle != NULL) {
-           if (retStyle->errors != 0) {
+
 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
-               /*
-               * Restore all changes made to namespace URIs of ns-decls.
-               */
-               if (cctxt->psData->nsMap)
-                   xsltRestoreDocumentNamespaces(cctxt->psData->nsMap, doc);
+        if (style->errors != 0) {
+            /*
+            * Restore all changes made to namespace URIs of ns-decls.
+            */
+            if (cctxt->psData->nsMap)
+                xsltRestoreDocumentNamespaces(cctxt->psData->nsMap, doc);
+        }
 #endif
-               /*
-               * Detach the doc from the stylesheet; otherwise the doc
-               * will be freed in xsltFreeStylesheet().
-               */
-               retStyle->doc = NULL;
-               /*
-               * Cleanup the doc if its the main stylesheet.
-               */
-               if (parentStyle == NULL) {
-                   xsltCleanupStylesheetTree(doc, xmlDocGetRootElement(doc));
-                   if (retStyle->compCtxt != NULL) {
-                       xsltCompilationCtxtFree(retStyle->compCtxt);
-                       retStyle->compCtxt = NULL;
-                   }
-               }
 
-               xsltFreeStylesheet(retStyle);
-               retStyle = NULL;
-           }
-       }
+        if (style->parent == NULL) {
+            xsltCompilationCtxtFree(style->compCtxt);
+            style->compCtxt = NULL;
+        }
     }
 
 #else /* XSLT_REFACTORED */
     /*
     * Old behaviour.
     */
-    retStyle->doc = doc;
-    if (xsltParseStylesheetProcess(retStyle, doc) == NULL) {
-               retStyle->doc = NULL;
-               xsltFreeStylesheet(retStyle);
-               retStyle = NULL;
-    }
-    if (retStyle != NULL) {
-       if (retStyle->errors != 0) {
-           retStyle->doc = NULL;
-           if (parentStyle == NULL)
-               xsltCleanupStylesheetTree(doc,
-                   xmlDocGetRootElement(doc));
-           xsltFreeStylesheet(retStyle);
-           retStyle = NULL;
-       }
+    style->doc = doc;
+    if (xsltParseStylesheetProcess(style, doc) == NULL) {
+        style->doc = NULL;
+        return(-1);
     }
 #endif /* else of XSLT_REFACTORED */
 
-    return(retStyle);
+    if (style->errors != 0) {
+        /*
+        * Detach the doc from the stylesheet; otherwise the doc
+        * will be freed in xsltFreeStylesheet().
+        */
+        style->doc = NULL;
+        /*
+        * Cleanup the doc if its the main stylesheet.
+        */
+        if (style->parent == NULL)
+            xsltCleanupStylesheetTree(doc, xmlDocGetRootElement(doc));
+        return(-1);
+    }
+
+    if (style->parent == NULL)
+        xsltResolveStylesheetAttributeSet(style);
+
+    return(0);
 }
 
 /**
@@ -6707,27 +6727,9 @@ xsltParseStylesheetImportedDoc(xmlDocPtr doc,
 
 xsltStylesheetPtr
 xsltParseStylesheetDoc(xmlDocPtr doc) {
-    xsltStylesheetPtr ret;
-
     xsltInitGlobals();
 
-    ret = xsltParseStylesheetImportedDoc(doc, NULL);
-    if (ret == NULL)
-       return(NULL);
-
-    xsltResolveStylesheetAttributeSet(ret);
-#ifdef XSLT_REFACTORED
-    /*
-    * Free the compilation context.
-    * TODO: Check if it's better to move this cleanup to
-    *   xsltParseStylesheetImportedDoc().
-    */
-    if (ret->compCtxt != NULL) {
-       xsltCompilationCtxtFree(XSLT_CCTXT(ret));
-       ret->compCtxt = NULL;
-    }
-#endif
-    return(ret);
+    return(xsltParseStylesheetImportedDoc(doc, NULL));
 }
 
 /**
diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h
index f9066adc..464c5cd9 100644
--- a/libxslt/xsltInternals.h
+++ b/libxslt/xsltInternals.h
@@ -105,14 +105,6 @@ extern const xmlChar *xsltXSLTAttrMarker;
  */
 /* #define XSLT_REFACTORED_XSLT_NSCOMP */
 
-/**
- * XSLT_REFACTORED_XPATHCOMP:
- *
- * Internal define to enable the optimization of the
- * compilation of XPath expressions.
- */
-#define XSLT_REFACTORED_XPATHCOMP
-
 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
 
 extern const xmlChar *xsltConstNamespaceNameXSLT;
@@ -1346,9 +1338,6 @@ struct _xsltCompilerCtxt {
     */
     int strict;
     xsltPrincipalStylesheetDataPtr psData;
-#ifdef XSLT_REFACTORED_XPATHCOMP
-    xmlXPathContextPtr xpathCtxt;
-#endif
     xsltStyleItemUknownPtr unknownItem;
     int hasNsAliases; /* Indicator if there was an xsl:namespace-alias. */
     xsltNsAliasPtr nsAliases;
@@ -1642,6 +1631,8 @@ struct _xsltStylesheet {
     int forwards_compatible;
 
     xmlHashTablePtr namedTemplates; /* hash table of named templates */
+
+    xmlXPathContextPtr xpathCtxt;
 };
 
 typedef struct _xsltTransformCache xsltTransformCache;
@@ -1871,6 +1862,9 @@ XSLTPUBFUN xsltStylesheetPtr XSLTCALL
 XSLTPUBFUN xsltStylesheetPtr XSLTCALL
                        xsltParseStylesheetImportedDoc(xmlDocPtr doc,
                                                xsltStylesheetPtr style);
+XSLTPUBFUN int XSLTCALL
+                       xsltParseStylesheetUser(xsltStylesheetPtr style,
+                                               xmlDocPtr doc);
 XSLTPUBFUN xsltStylesheetPtr XSLTCALL
                        xsltLoadStylesheetPI    (xmlDocPtr doc);
 XSLTPUBFUN void XSLTCALL
diff --git a/libxslt/xsltutils.c b/libxslt/xsltutils.c
index adefde98..61f5c25d 100644
--- a/libxslt/xsltutils.c
+++ b/libxslt/xsltutils.c
@@ -2288,25 +2288,7 @@ xsltXPathCompileFlags(xsltStylesheetPtr style, const xmlChar *str, int flags) {
     xmlXPathCompExprPtr ret;
 
     if (style != NULL) {
-#ifdef XSLT_REFACTORED_XPATHCOMP
-       if (XSLT_CCTXT(style)) {
-           /*
-           * Proposed by Jerome Pesenti
-           * --------------------------
-           * For better efficiency we'll reuse the compilation
-           * context's XPath context. For the common stylesheet using
-           * XPath expressions this will reduce compilation time to
-           * about 50%.
-           *
-           * See http://mail.gnome.org/archives/xslt/2006-April/msg00037.html
-           */
-           xpathCtxt = XSLT_CCTXT(style)->xpathCtxt;
-           xpathCtxt->doc = style->doc;
-       } else
-           xpathCtxt = xmlXPathNewContext(style->doc);
-#else
-       xpathCtxt = xmlXPathNewContext(style->doc);
-#endif
+        xpathCtxt = style->principal->xpathCtxt;
        if (xpathCtxt == NULL)
            return NULL;
        xpathCtxt->dict = style->dict;
@@ -2322,13 +2304,9 @@ xsltXPathCompileFlags(xsltStylesheetPtr style, const xmlChar *str, int flags) {
     */
     ret = xmlXPathCtxtCompile(xpathCtxt, str);
 
-#ifdef XSLT_REFACTORED_XPATHCOMP
-    if ((style == NULL) || (! XSLT_CCTXT(style))) {
+    if (style == NULL) {
        xmlXPathFreeContext(xpathCtxt);
     }
-#else
-    xmlXPathFreeContext(xpathCtxt);
-#endif
     /*
      * TODO: there is a lot of optimizations which should be possible
      *       like variable slot precomputations, function precomputations, etc.
diff --git a/tests/fuzz/xslt.c b/tests/fuzz/xslt.c
index db3d45ca..0520550d 100644
--- a/tests/fuzz/xslt.c
+++ b/tests/fuzz/xslt.c
@@ -79,6 +79,13 @@ LLVMFuzzerInitialize(int *argc_p ATTRIBUTE_UNUSED,
     return 0;
 }
 
+static void
+xsltSetXPathResourceLimits(xmlXPathContextPtr ctxt) {
+    ctxt->maxParserDepth = 15;
+    ctxt->maxDepth = 100;
+    ctxt->opLimit = 100000;
+}
+
 int
 LLVMFuzzerTestOneInput(const char *data, size_t size) {
     xmlDocPtr xsltDoc;
@@ -101,19 +108,24 @@ LLVMFuzzerTestOneInput(const char *data, size_t size) {
     xmlNewNs(xsltRoot, EXSLT_STRINGS_NAMESPACE, BAD_CAST "str");
     xmlNewNs(xsltRoot, SAXON_NAMESPACE, BAD_CAST "saxon");
 
-    sheet = xsltParseStylesheetDoc(xsltDoc);
+    sheet = xsltNewStylesheet();
     if (sheet == NULL) {
         xmlFreeDoc(xsltDoc);
         return 0;
     }
+    xsltSetXPathResourceLimits(sheet->xpathCtxt);
+    sheet->xpathCtxt->opCount = 0;
+    if (xsltParseStylesheetUser(sheet, xsltDoc) != 0) {
+        xsltFreeStylesheet(sheet);
+        xmlFreeDoc(xsltDoc);
+        return 0;
+    }
 
     ctxt = xsltNewTransformContext(sheet, doc);
     xsltSetCtxtSecurityPrefs(sec, ctxt);
     ctxt->maxTemplateDepth = 100;
-    ctxt->xpathCtxt->maxParserDepth = 15;
-    ctxt->xpathCtxt->maxDepth = 100;
-    ctxt->xpathCtxt->opCount = 0;
-    ctxt->xpathCtxt->opLimit = 100000;
+    xsltSetXPathResourceLimits(ctxt->xpathCtxt);
+    ctxt->xpathCtxt->opCount = sheet->xpathCtxt->opCount;
 
     result = xsltApplyStylesheetUser(sheet, doc, NULL, NULL, NULL, ctxt);
 


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