[libxml2] Optional XPath operation limit
- From: Nick Wellnhofer <nwellnhof src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libxml2] Optional XPath operation limit
- Date: Mon, 15 Apr 2019 12:24:08 +0000 (UTC)
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]