1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2025-03-10 08:58:16 +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_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;
};
/*

81
xpath.c
View File

@ -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) {