1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2025-10-26 00:37:43 +03:00

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.
This commit is contained in:
Nick Wellnhofer
2019-03-12 16:12:05 +01:00
parent 91d576de8b
commit 852c93a2dc
2 changed files with 87 additions and 1 deletions

View File

@@ -70,7 +70,8 @@ typedef enum {
XPATH_INVALID_CHAR_ERROR, XPATH_INVALID_CHAR_ERROR,
XPATH_INVALID_CTXT, XPATH_INVALID_CTXT,
XPATH_STACK_ERROR, XPATH_STACK_ERROR,
XPATH_FORBID_VARIABLE_ERROR XPATH_FORBID_VARIABLE_ERROR,
XPATH_OP_LIMIT_EXCEEDED
} xmlXPathError; } xmlXPathError;
/* /*
@@ -352,6 +353,10 @@ struct _xmlXPathContext {
/* Cache for reusal of XPath objects */ /* Cache for reusal of XPath objects */
void *cache; void *cache;
/* Resource limits */
unsigned long opLimit;
unsigned long opCount;
}; };
/* /*

81
xpath.c
View File

@@ -610,6 +610,7 @@ static const char *xmlXPathErrorMessages[] = {
"Invalid or incomplete context\n", "Invalid or incomplete context\n",
"Stack usage error\n", "Stack usage error\n",
"Forbidden variable\n", "Forbidden variable\n",
"Operation limit exceeded\n",
"?? Unknown error ??\n" /* Must be last in the list! */ "?? Unknown error ??\n" /* Must be last in the list! */
}; };
#define MAXERRNO ((int)(sizeof(xmlXPathErrorMessages) / \ #define MAXERRNO ((int)(sizeof(xmlXPathErrorMessages) / \
@@ -747,6 +748,32 @@ xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file ATTRIBUTE_UNUSED,
xmlXPathErr(ctxt, no); 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 * * Utilities *
@@ -12289,6 +12316,9 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
cur = NULL; cur = NULL;
hasNsNodes = 0; hasNsNodes = 0;
do { do {
if (OP_LIMIT_EXCEEDED(ctxt, 1))
goto error;
cur = next(ctxt, cur); cur = next(ctxt, cur);
if (cur == NULL) if (cur == NULL)
break; break;
@@ -12692,6 +12722,8 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
xmlXPathObjectPtr arg1, arg2; xmlXPathObjectPtr arg1, arg2;
CHECK_ERROR0; CHECK_ERROR0;
if (OP_LIMIT_EXCEEDED(ctxt, 1))
return(0);
comp = ctxt->comp; comp = ctxt->comp;
switch (op->op) { switch (op->op) {
case XPATH_OP_END: case XPATH_OP_END:
@@ -12732,6 +12764,17 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
xmlXPathReleaseObject(ctxt->context, arg2); xmlXPathReleaseObject(ctxt->context, arg2);
XP_ERROR0(XPATH_INVALID_TYPE); 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, arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
arg2->nodesetval); arg2->nodesetval);
@@ -12811,6 +12854,8 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
xmlXPathObjectPtr arg1, arg2; xmlXPathObjectPtr arg1, arg2;
CHECK_ERROR0; CHECK_ERROR0;
if (OP_LIMIT_EXCEEDED(ctxt, 1))
return(0);
comp = ctxt->comp; comp = ctxt->comp;
switch (op->op) { switch (op->op) {
case XPATH_OP_END: case XPATH_OP_END:
@@ -12850,6 +12895,17 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
xmlXPathReleaseObject(ctxt->context, arg2); xmlXPathReleaseObject(ctxt->context, arg2);
XP_ERROR0(XPATH_INVALID_TYPE); 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, arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
arg2->nodesetval); arg2->nodesetval);
@@ -13191,6 +13247,8 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
xmlXPathObjectPtr arg1, arg2; xmlXPathObjectPtr arg1, arg2;
CHECK_ERROR0; CHECK_ERROR0;
if (OP_LIMIT_EXCEEDED(ctxt, 1))
return(0);
comp = ctxt->comp; comp = ctxt->comp;
switch (op->op) { switch (op->op) {
case XPATH_OP_END: case XPATH_OP_END:
@@ -13292,6 +13350,17 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
xmlXPathReleaseObject(ctxt->context, arg2); xmlXPathReleaseObject(ctxt->context, arg2);
XP_ERROR0(XPATH_INVALID_TYPE); 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) || if ((arg1->nodesetval == NULL) ||
((arg2->nodesetval != NULL) && ((arg2->nodesetval != NULL) &&
@@ -13967,6 +14036,8 @@ xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt,
xmlXPathObjectPtr resObj = NULL; xmlXPathObjectPtr resObj = NULL;
start: start:
if (OP_LIMIT_EXCEEDED(ctxt, 1))
return(0);
/* comp = ctxt->comp; */ /* comp = ctxt->comp; */
switch (op->op) { switch (op->op) {
case XPATH_OP_END: case XPATH_OP_END:
@@ -14166,6 +14237,16 @@ xmlXPathRunStreamEval(xmlXPathContextPtr ctxt, xmlPatternPtr comp,
goto scan_children; goto scan_children;
next_node: next_node:
do { 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++; nb_nodes++;
switch (cur->type) { switch (cur->type) {