[libxml2] Optional XPath operation limit



commit 852c93a2dc2224f020aab55a9702f992db404836
Author: Nick Wellnhofer <wellnhofer aevum de>
Date:   Tue Mar 12 16:12:05 2019 +0100

    Optional XPath operation limit
    
    Optionally limit the maximum numbers of XPath operations when evaluating
    an expression. Useful to avoid timeouts when fuzzing. The following
    operations count towards the limit:
    
    - XPath operations
    - Location step iterations
    - Union operations
    
    Enabled by setting opLimit to a non-zero value. Note that it's the user's
    responsibility to reset opCount. This allows to enforce the operation
    limit across multiple reuses of an XPath context.

 include/libxml/xpath.h |  7 ++++-
 xpath.c                | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 87 insertions(+), 1 deletion(-)
---
diff --git a/include/libxml/xpath.h b/include/libxml/xpath.h
index d96776c5..d782ab0b 100644
--- a/include/libxml/xpath.h
+++ b/include/libxml/xpath.h
@@ -70,7 +70,8 @@ typedef enum {
     XPATH_INVALID_CHAR_ERROR,
     XPATH_INVALID_CTXT,
     XPATH_STACK_ERROR,
-    XPATH_FORBID_VARIABLE_ERROR
+    XPATH_FORBID_VARIABLE_ERROR,
+    XPATH_OP_LIMIT_EXCEEDED
 } xmlXPathError;
 
 /*
@@ -352,6 +353,10 @@ struct _xmlXPathContext {
 
     /* Cache for reusal of XPath objects */
     void *cache;
+
+    /* Resource limits */
+    unsigned long opLimit;
+    unsigned long opCount;
 };
 
 /*
diff --git a/xpath.c b/xpath.c
index facd6419..cc56ea2a 100644
--- a/xpath.c
+++ b/xpath.c
@@ -610,6 +610,7 @@ static const char *xmlXPathErrorMessages[] = {
     "Invalid or incomplete context\n",
     "Stack usage error\n",
     "Forbidden variable\n",
+    "Operation limit exceeded\n",
     "?? Unknown error ??\n"    /* Must be last in the list! */
 };
 #define MAXERRNO ((int)(sizeof(xmlXPathErrorMessages) /        \
@@ -747,6 +748,32 @@ xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file ATTRIBUTE_UNUSED,
     xmlXPathErr(ctxt, no);
 }
 
+/**
+ * xmlXPathCheckOpLimit:
+ * @ctxt:  the XPath Parser context
+ * @opCount:  the number of operations to be added
+ *
+ * Adds opCount to the running total of operations and returns -1 if the
+ * operation limit is exceeded. Returns 0 otherwise.
+ */
+static int
+xmlXPathCheckOpLimit(xmlXPathParserContextPtr ctxt, unsigned long opCount) {
+    xmlXPathContextPtr xpctxt = ctxt->context;
+
+    if ((opCount > xpctxt->opLimit) ||
+        (xpctxt->opCount > xpctxt->opLimit - opCount)) {
+        xpctxt->opCount = xpctxt->opLimit;
+        xmlXPathErr(ctxt, XPATH_OP_LIMIT_EXCEEDED);
+        return(-1);
+    }
+
+    xpctxt->opCount += opCount;
+    return(0);
+}
+
+#define OP_LIMIT_EXCEEDED(ctxt, n) \
+    ((ctxt->context->opLimit != 0) && (xmlXPathCheckOpLimit(ctxt, n) < 0))
+
 /************************************************************************
  *                                                                     *
  *                     Utilities                                       *
@@ -12289,6 +12316,9 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
        cur = NULL;
        hasNsNodes = 0;
         do {
+            if (OP_LIMIT_EXCEEDED(ctxt, 1))
+                goto error;
+
             cur = next(ctxt, cur);
             if (cur == NULL)
                 break;
@@ -12692,6 +12722,8 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
     xmlXPathObjectPtr arg1, arg2;
 
     CHECK_ERROR0;
+    if (OP_LIMIT_EXCEEDED(ctxt, 1))
+        return(0);
     comp = ctxt->comp;
     switch (op->op) {
         case XPATH_OP_END:
@@ -12732,6 +12764,17 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
                xmlXPathReleaseObject(ctxt->context, arg2);
                 XP_ERROR0(XPATH_INVALID_TYPE);
             }
+            if ((ctxt->context->opLimit != 0) &&
+                (((arg1->nodesetval != NULL) &&
+                  (xmlXPathCheckOpLimit(ctxt,
+                                        arg1->nodesetval->nodeNr) < 0)) ||
+                 ((arg2->nodesetval != NULL) &&
+                  (xmlXPathCheckOpLimit(ctxt,
+                                        arg2->nodesetval->nodeNr) < 0)))) {
+               xmlXPathReleaseObject(ctxt->context, arg1);
+               xmlXPathReleaseObject(ctxt->context, arg2);
+                return(0);
+            }
 
             arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
                                                     arg2->nodesetval);
@@ -12811,6 +12854,8 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
     xmlXPathObjectPtr arg1, arg2;
 
     CHECK_ERROR0;
+    if (OP_LIMIT_EXCEEDED(ctxt, 1))
+        return(0);
     comp = ctxt->comp;
     switch (op->op) {
         case XPATH_OP_END:
@@ -12850,6 +12895,17 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
                xmlXPathReleaseObject(ctxt->context, arg2);
                 XP_ERROR0(XPATH_INVALID_TYPE);
             }
+            if ((ctxt->context->opLimit != 0) &&
+                (((arg1->nodesetval != NULL) &&
+                  (xmlXPathCheckOpLimit(ctxt,
+                                        arg1->nodesetval->nodeNr) < 0)) ||
+                 ((arg2->nodesetval != NULL) &&
+                  (xmlXPathCheckOpLimit(ctxt,
+                                        arg2->nodesetval->nodeNr) < 0)))) {
+               xmlXPathReleaseObject(ctxt->context, arg1);
+               xmlXPathReleaseObject(ctxt->context, arg2);
+                return(0);
+            }
 
             arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
                                                     arg2->nodesetval);
@@ -13191,6 +13247,8 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
     xmlXPathObjectPtr arg1, arg2;
 
     CHECK_ERROR0;
+    if (OP_LIMIT_EXCEEDED(ctxt, 1))
+        return(0);
     comp = ctxt->comp;
     switch (op->op) {
         case XPATH_OP_END:
@@ -13292,6 +13350,17 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
                xmlXPathReleaseObject(ctxt->context, arg2);
                 XP_ERROR0(XPATH_INVALID_TYPE);
             }
+            if ((ctxt->context->opLimit != 0) &&
+                (((arg1->nodesetval != NULL) &&
+                  (xmlXPathCheckOpLimit(ctxt,
+                                        arg1->nodesetval->nodeNr) < 0)) ||
+                 ((arg2->nodesetval != NULL) &&
+                  (xmlXPathCheckOpLimit(ctxt,
+                                        arg2->nodesetval->nodeNr) < 0)))) {
+               xmlXPathReleaseObject(ctxt->context, arg1);
+               xmlXPathReleaseObject(ctxt->context, arg2);
+                return(0);
+            }
 
            if ((arg1->nodesetval == NULL) ||
                ((arg2->nodesetval != NULL) &&
@@ -13967,6 +14036,8 @@ xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt,
     xmlXPathObjectPtr resObj = NULL;
 
 start:
+    if (OP_LIMIT_EXCEEDED(ctxt, 1))
+        return(0);
     /* comp = ctxt->comp; */
     switch (op->op) {
         case XPATH_OP_END:
@@ -14166,6 +14237,16 @@ xmlXPathRunStreamEval(xmlXPathContextPtr ctxt, xmlPatternPtr comp,
     goto scan_children;
 next_node:
     do {
+        if (ctxt->opLimit != 0) {
+            if (ctxt->opCount >= ctxt->opLimit) {
+                xmlGenericError(xmlGenericErrorContext,
+                        "XPath operation limit exceeded\n");
+                xmlFreeStreamCtxt(patstream);
+                return(-1);
+            }
+            ctxt->opCount++;
+        }
+
         nb_nodes++;
 
        switch (cur->type) {


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