1300 lines
45 KiB
Diff
1300 lines
45 KiB
Diff
From 497fb24df7bdced5a8548f50b7b282ec73886a51 Mon Sep 17 00:00:00 2001
|
|
From: Nick Wellnhofer <wellnhofer@aevum.de>
|
|
Date: Tue, 12 Mar 2019 16:12:05 +0100
|
|
Subject: [PATCH 01/12] 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 3fcdc9e1..416b837c 100644
|
|
--- a/xpath.c
|
|
+++ b/xpath.c
|
|
@@ -627,6 +627,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) / \
|
|
@@ -764,6 +765,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 *
|
|
@@ -12353,6 +12380,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;
|
|
@@ -12756,6 +12786,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:
|
|
@@ -12796,6 +12828,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);
|
|
@@ -12888,6 +12931,8 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
|
|
int cs;
|
|
|
|
CHECK_ERROR0;
|
|
+ if (OP_LIMIT_EXCEEDED(ctxt, 1))
|
|
+ return(0);
|
|
comp = ctxt->comp;
|
|
switch (op->op) {
|
|
case XPATH_OP_END:
|
|
@@ -12935,6 +12980,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);
|
|
@@ -13312,6 +13368,8 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
int cs;
|
|
|
|
CHECK_ERROR0;
|
|
+ if (OP_LIMIT_EXCEEDED(ctxt, 1))
|
|
+ return(0);
|
|
comp = ctxt->comp;
|
|
switch (op->op) {
|
|
case XPATH_OP_END:
|
|
@@ -13469,6 +13527,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) &&
|
|
@@ -14200,6 +14269,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:
|
|
@@ -14399,6 +14470,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) {
|
|
--
|
|
2.47.3
|
|
|
|
|
|
From f8abfff734d21b3b594892e0df38942a8eb07b84 Mon Sep 17 00:00:00 2001
|
|
From: Nick Wellnhofer <wellnhofer@aevum.de>
|
|
Date: Mon, 18 Mar 2019 11:34:26 +0100
|
|
Subject: [PATCH 02/12] Optional recursion limit when evaluating XPath
|
|
expressions
|
|
|
|
Useful to avoid call stack overflows when fuzzing.
|
|
---
|
|
include/libxml/xpath.h | 5 ++-
|
|
xpath.c | 84 +++++++++++++++++++++++++++++++++++++++++-
|
|
2 files changed, 86 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/include/libxml/xpath.h b/include/libxml/xpath.h
|
|
index d782ab0b..3d9b1cee 100644
|
|
--- a/include/libxml/xpath.h
|
|
+++ b/include/libxml/xpath.h
|
|
@@ -71,7 +71,8 @@ typedef enum {
|
|
XPATH_INVALID_CTXT,
|
|
XPATH_STACK_ERROR,
|
|
XPATH_FORBID_VARIABLE_ERROR,
|
|
- XPATH_OP_LIMIT_EXCEEDED
|
|
+ XPATH_OP_LIMIT_EXCEEDED,
|
|
+ XPATH_RECURSION_LIMIT_EXCEEDED
|
|
} xmlXPathError;
|
|
|
|
/*
|
|
@@ -357,6 +358,8 @@ struct _xmlXPathContext {
|
|
/* Resource limits */
|
|
unsigned long opLimit;
|
|
unsigned long opCount;
|
|
+ int depth;
|
|
+ int maxDepth;
|
|
};
|
|
|
|
/*
|
|
diff --git a/xpath.c b/xpath.c
|
|
index 416b837c..aaa85446 100644
|
|
--- a/xpath.c
|
|
+++ b/xpath.c
|
|
@@ -628,6 +628,7 @@ static const char *xmlXPathErrorMessages[] = {
|
|
"Stack usage error\n",
|
|
"Forbidden variable\n",
|
|
"Operation limit exceeded\n",
|
|
+ "Recursion limit exceeded\n",
|
|
"?? Unknown error ??\n" /* Must be last in the list! */
|
|
};
|
|
#define MAXERRNO ((int)(sizeof(xmlXPathErrorMessages) / \
|
|
@@ -6187,6 +6188,8 @@ xmlXPathNewContext(xmlDocPtr doc) {
|
|
ret->contextSize = -1;
|
|
ret->proximityPosition = -1;
|
|
|
|
+ ret->maxDepth = INT_MAX;
|
|
+
|
|
#ifdef XP_DEFAULT_CACHE_ON
|
|
if (xmlXPathContextSetCache(ret, 1, -1, 0) == -1) {
|
|
xmlXPathFreeContext(ret);
|
|
@@ -12788,9 +12791,13 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
|
|
CHECK_ERROR0;
|
|
if (OP_LIMIT_EXCEEDED(ctxt, 1))
|
|
return(0);
|
|
+ if (ctxt->context->depth >= ctxt->context->maxDepth)
|
|
+ XP_ERROR0(XPATH_RECURSION_LIMIT_EXCEEDED);
|
|
+ ctxt->context->depth += 1;
|
|
comp = ctxt->comp;
|
|
switch (op->op) {
|
|
case XPATH_OP_END:
|
|
+ ctxt->context->depth -= 1;
|
|
return (0);
|
|
case XPATH_OP_UNION:
|
|
total =
|
|
@@ -12847,9 +12854,11 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
|
|
/* optimizer */
|
|
if (total > cur)
|
|
xmlXPathCompSwap(op);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total + cur);
|
|
case XPATH_OP_ROOT:
|
|
xmlXPathRoot(ctxt);
|
|
+ ctxt->context->depth -= 1;
|
|
return (0);
|
|
case XPATH_OP_NODE:
|
|
if (op->ch1 != -1)
|
|
@@ -12860,6 +12869,7 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
|
|
CHECK_ERROR0;
|
|
valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
|
|
ctxt->context->node));
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_RESET:
|
|
if (op->ch1 != -1)
|
|
@@ -12869,21 +12879,26 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
|
|
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
|
|
CHECK_ERROR0;
|
|
ctxt->context->node = NULL;
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_COLLECT:{
|
|
- if (op->ch1 == -1)
|
|
+ if (op->ch1 == -1) {
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
+ }
|
|
|
|
total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
|
|
CHECK_ERROR0;
|
|
|
|
total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL, 0);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
case XPATH_OP_VALUE:
|
|
valuePush(ctxt,
|
|
xmlXPathCacheObjectCopy(ctxt->context,
|
|
(xmlXPathObjectPtr) op->value4));
|
|
+ ctxt->context->depth -= 1;
|
|
return (0);
|
|
case XPATH_OP_SORT:
|
|
if (op->ch1 != -1)
|
|
@@ -12896,15 +12911,21 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
|
|
&& (ctxt->value->nodesetval != NULL)
|
|
&& (ctxt->value->nodesetval->nodeNr > 1))
|
|
xmlXPathNodeSetSort(ctxt->value->nodesetval);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
#ifdef XP_OPTIMIZED_FILTER_FIRST
|
|
case XPATH_OP_FILTER:
|
|
total += xmlXPathCompOpEvalFilterFirst(ctxt, op, first);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
#endif
|
|
default:
|
|
+ ctxt->context->depth -= 1;
|
|
return (xmlXPathCompOpEval(ctxt, op));
|
|
}
|
|
+
|
|
+ ctxt->context->depth -= 1;
|
|
+ return(total);
|
|
}
|
|
|
|
/**
|
|
@@ -12933,9 +12954,13 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
|
|
CHECK_ERROR0;
|
|
if (OP_LIMIT_EXCEEDED(ctxt, 1))
|
|
return(0);
|
|
+ if (ctxt->context->depth >= ctxt->context->maxDepth)
|
|
+ XP_ERROR0(XPATH_RECURSION_LIMIT_EXCEEDED);
|
|
+ ctxt->context->depth += 1;
|
|
comp = ctxt->comp;
|
|
switch (op->op) {
|
|
case XPATH_OP_END:
|
|
+ ctxt->context->depth -= 1;
|
|
return (0);
|
|
case XPATH_OP_UNION:
|
|
bakd = ctxt->context->doc;
|
|
@@ -12999,9 +13024,11 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
|
|
/* optimizer */
|
|
if (total > cur)
|
|
xmlXPathCompSwap(op);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total + cur);
|
|
case XPATH_OP_ROOT:
|
|
xmlXPathRoot(ctxt);
|
|
+ ctxt->context->depth -= 1;
|
|
return (0);
|
|
case XPATH_OP_NODE:
|
|
if (op->ch1 != -1)
|
|
@@ -13012,6 +13039,7 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
|
|
CHECK_ERROR0;
|
|
valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
|
|
ctxt->context->node));
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_RESET:
|
|
if (op->ch1 != -1)
|
|
@@ -13021,21 +13049,26 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
|
|
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
|
|
CHECK_ERROR0;
|
|
ctxt->context->node = NULL;
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_COLLECT:{
|
|
- if (op->ch1 == -1)
|
|
+ if (op->ch1 == -1) {
|
|
+ ctxt->context->depth -= 1;
|
|
return (0);
|
|
+ }
|
|
|
|
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
|
|
CHECK_ERROR0;
|
|
|
|
total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last, 0);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
case XPATH_OP_VALUE:
|
|
valuePush(ctxt,
|
|
xmlXPathCacheObjectCopy(ctxt->context,
|
|
(xmlXPathObjectPtr) op->value4));
|
|
+ ctxt->context->depth -= 1;
|
|
return (0);
|
|
case XPATH_OP_SORT:
|
|
if (op->ch1 != -1)
|
|
@@ -13048,10 +13081,15 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
|
|
&& (ctxt->value->nodesetval != NULL)
|
|
&& (ctxt->value->nodesetval->nodeNr > 1))
|
|
xmlXPathNodeSetSort(ctxt->value->nodesetval);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
default:
|
|
+ ctxt->context->depth -= 1;
|
|
return (xmlXPathCompOpEval(ctxt, op));
|
|
}
|
|
+
|
|
+ ctxt->context->depth -= 1;
|
|
+ return (total);
|
|
}
|
|
|
|
#ifdef XP_OPTIMIZED_FILTER_FIRST
|
|
@@ -13370,9 +13408,13 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
CHECK_ERROR0;
|
|
if (OP_LIMIT_EXCEEDED(ctxt, 1))
|
|
return(0);
|
|
+ if (ctxt->context->depth >= ctxt->context->maxDepth)
|
|
+ XP_ERROR0(XPATH_RECURSION_LIMIT_EXCEEDED);
|
|
+ ctxt->context->depth += 1;
|
|
comp = ctxt->comp;
|
|
switch (op->op) {
|
|
case XPATH_OP_END:
|
|
+ ctxt->context->depth -= 1;
|
|
return (0);
|
|
case XPATH_OP_AND:
|
|
bakd = ctxt->context->doc;
|
|
@@ -13383,6 +13425,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
CHECK_ERROR0;
|
|
xmlXPathBooleanFunction(ctxt, 1);
|
|
if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
arg2 = valuePop(ctxt);
|
|
ctxt->context->doc = bakd;
|
|
@@ -13398,6 +13441,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
if (ctxt->value != NULL)
|
|
ctxt->value->boolval &= arg2->boolval;
|
|
xmlXPathReleaseObject(ctxt->context, arg2);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_OR:
|
|
bakd = ctxt->context->doc;
|
|
@@ -13408,6 +13452,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
CHECK_ERROR0;
|
|
xmlXPathBooleanFunction(ctxt, 1);
|
|
if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
arg2 = valuePop(ctxt);
|
|
ctxt->context->doc = bakd;
|
|
@@ -13423,6 +13468,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
if (ctxt->value != NULL)
|
|
ctxt->value->boolval |= arg2->boolval;
|
|
xmlXPathReleaseObject(ctxt->context, arg2);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_EQUAL:
|
|
bakd = ctxt->context->doc;
|
|
@@ -13442,6 +13488,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
else
|
|
equal = xmlXPathNotEqualValues(ctxt);
|
|
valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, equal));
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_CMP:
|
|
bakd = ctxt->context->doc;
|
|
@@ -13458,6 +13505,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
CHECK_ERROR0;
|
|
ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
|
|
valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_PLUS:
|
|
bakd = ctxt->context->doc;
|
|
@@ -13484,6 +13532,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
CAST_TO_NUMBER;
|
|
CHECK_TYPE0(XPATH_NUMBER);
|
|
}
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_MULT:
|
|
bakd = ctxt->context->doc;
|
|
@@ -13504,6 +13553,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
xmlXPathDivValues(ctxt);
|
|
else if (op->value == 2)
|
|
xmlXPathModValues(ctxt);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_UNION:
|
|
bakd = ctxt->context->doc;
|
|
@@ -13549,9 +13599,11 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
|
|
valuePush(ctxt, arg1);
|
|
xmlXPathReleaseObject(ctxt->context, arg2);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_ROOT:
|
|
xmlXPathRoot(ctxt);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_NODE:
|
|
if (op->ch1 != -1)
|
|
@@ -13562,6 +13614,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
CHECK_ERROR0;
|
|
valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
|
|
ctxt->context->node));
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_RESET:
|
|
if (op->ch1 != -1)
|
|
@@ -13571,21 +13624,25 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
|
|
CHECK_ERROR0;
|
|
ctxt->context->node = NULL;
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_COLLECT:{
|
|
if (op->ch1 == -1)
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
|
|
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
|
|
CHECK_ERROR0;
|
|
|
|
total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL, 0);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
case XPATH_OP_VALUE:
|
|
valuePush(ctxt,
|
|
xmlXPathCacheObjectCopy(ctxt->context,
|
|
(xmlXPathObjectPtr) op->value4));
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_VARIABLE:{
|
|
xmlXPathObjectPtr val;
|
|
@@ -13607,6 +13664,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
"xmlXPathCompOpEval: variable %s bound to undefined prefix %s\n",
|
|
(char *) op->value4, (char *)op->value5);
|
|
ctxt->error = XPATH_UNDEF_PREFIX_ERROR;
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
val = xmlXPathVariableLookupNS(ctxt->context,
|
|
@@ -13615,6 +13673,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
XP_ERROR0(XPATH_UNDEF_VARIABLE_ERROR);
|
|
valuePush(ctxt, val);
|
|
}
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
case XPATH_OP_FUNCTION:{
|
|
@@ -13629,6 +13688,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
|
|
if (ctxt->error != XPATH_EXPRESSION_OK) {
|
|
xmlXPathPopFrame(ctxt, frame);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
}
|
|
@@ -13637,6 +13697,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
"xmlXPathCompOpEval: parameter error\n");
|
|
ctxt->error = XPATH_INVALID_OPERAND;
|
|
xmlXPathPopFrame(ctxt, frame);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
for (i = 0; i < op->value; i++) {
|
|
@@ -13645,6 +13706,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
"xmlXPathCompOpEval: parameter error\n");
|
|
ctxt->error = XPATH_INVALID_OPERAND;
|
|
xmlXPathPopFrame(ctxt, frame);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
}
|
|
@@ -13665,6 +13727,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
(char *)op->value4, (char *)op->value5);
|
|
xmlXPathPopFrame(ctxt, frame);
|
|
ctxt->error = XPATH_UNDEF_PREFIX_ERROR;
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
func = xmlXPathFunctionLookupNS(ctxt->context,
|
|
@@ -13687,6 +13750,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
ctxt->context->function = oldFunc;
|
|
ctxt->context->functionURI = oldFuncURI;
|
|
xmlXPathPopFrame(ctxt, frame);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
case XPATH_OP_ARG:
|
|
@@ -13710,6 +13774,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
ctxt->context->doc = bakd;
|
|
CHECK_ERROR0;
|
|
}
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_PREDICATE:
|
|
case XPATH_OP_FILTER:{
|
|
@@ -13763,6 +13828,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
(ctxt->value->nodesetval->nodeNr > 1))
|
|
xmlXPathNodeSetClearFromPos(ctxt->value->nodesetval,
|
|
1, 1);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
}
|
|
@@ -13798,6 +13864,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
(ctxt->value->nodesetval->nodeTab != NULL) &&
|
|
(ctxt->value->nodesetval->nodeNr > 1))
|
|
xmlXPathNodeSetKeepLast(ctxt->value->nodesetval);
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
}
|
|
@@ -13817,8 +13884,10 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
|
|
CHECK_ERROR0;
|
|
if (op->ch2 == -1)
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
if (ctxt->value == NULL)
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
|
|
oldnode = ctxt->context->node;
|
|
@@ -13854,6 +13923,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
}
|
|
valuePush(ctxt, obj);
|
|
CHECK_ERROR0;
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
newlocset = xmlXPtrLocationSetCreate(NULL);
|
|
@@ -13913,6 +13983,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
ctxt->context->proximityPosition = -1;
|
|
valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
|
|
ctxt->context->node = oldnode;
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
#endif /* LIBXML_XPTR_ENABLED */
|
|
@@ -14064,6 +14135,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
xmlXPathCacheWrapNodeSet(ctxt->context, newset));
|
|
}
|
|
ctxt->context->node = oldnode;
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
case XPATH_OP_SORT:
|
|
@@ -14077,6 +14149,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
{
|
|
xmlXPathNodeSetSort(ctxt->value->nodesetval);
|
|
}
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
#ifdef LIBXML_XPTR_ENABLED
|
|
case XPATH_OP_RANGETO:{
|
|
@@ -14097,6 +14170,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
XP_ERROR0(XPATH_INVALID_OPERAND);
|
|
}
|
|
if (op->ch2 == -1)
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
|
|
if (ctxt->value->type == XPATH_LOCATIONSET) {
|
|
@@ -14120,6 +14194,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
}
|
|
valuePush(ctxt, obj);
|
|
CHECK_ERROR0;
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
newlocset = xmlXPtrLocationSetCreate(NULL);
|
|
@@ -14243,6 +14318,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
ctxt->context->contextSize = -1;
|
|
ctxt->context->proximityPosition = -1;
|
|
valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
#endif /* LIBXML_XPTR_ENABLED */
|
|
@@ -14250,6 +14326,8 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"XPath: unknown precompiled operation %d\n", op->op);
|
|
ctxt->error = XPATH_INVALID_OPERAND;
|
|
+
|
|
+ ctxt->context->depth -= 1;
|
|
return (total);
|
|
}
|
|
|
|
@@ -14603,6 +14681,8 @@ xmlXPathRunEval(xmlXPathParserContextPtr ctxt, int toBool)
|
|
if ((ctxt == NULL) || (ctxt->comp == NULL))
|
|
return(-1);
|
|
|
|
+ ctxt->context->depth = 0;
|
|
+
|
|
if (ctxt->valueTab == NULL) {
|
|
/* Allocate the value stack */
|
|
ctxt->valueTab = (xmlXPathObjectPtr *)
|
|
--
|
|
2.47.3
|
|
|
|
|
|
From 732305df78d8e8491e057a014df822dff0770a20 Mon Sep 17 00:00:00 2001
|
|
From: Nick Wellnhofer <wellnhofer@aevum.de>
|
|
Date: Sat, 20 Apr 2019 17:01:19 +0200
|
|
Subject: [PATCH 03/12] Limit recursion depth in xmlXPathOptimizeExpression
|
|
|
|
---
|
|
xpath.c | 33 +++++++++++++++++++++++++--------
|
|
1 file changed, 25 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/xpath.c b/xpath.c
|
|
index aaa85446..47f394aa 100644
|
|
--- a/xpath.c
|
|
+++ b/xpath.c
|
|
@@ -14923,8 +14923,12 @@ xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
|
|
#endif /* XPATH_STREAMING */
|
|
|
|
static void
|
|
-xmlXPathOptimizeExpression(xmlXPathCompExprPtr comp, xmlXPathStepOpPtr op)
|
|
+xmlXPathOptimizeExpression(xmlXPathParserContextPtr pctxt,
|
|
+ xmlXPathStepOpPtr op)
|
|
{
|
|
+ xmlXPathCompExprPtr comp = pctxt->comp;
|
|
+ xmlXPathContextPtr ctxt;
|
|
+
|
|
/*
|
|
* Try to rewrite "descendant-or-self::node()/foo" to an optimized
|
|
* internal representation.
|
|
@@ -14980,10 +14984,18 @@ xmlXPathOptimizeExpression(xmlXPathCompExprPtr comp, xmlXPathStepOpPtr op)
|
|
return;
|
|
|
|
/* Recurse */
|
|
+ ctxt = pctxt->context;
|
|
+ if (ctxt != NULL) {
|
|
+ if (ctxt->depth >= ctxt->maxDepth)
|
|
+ return;
|
|
+ ctxt->depth += 1;
|
|
+ }
|
|
if (op->ch1 != -1)
|
|
- xmlXPathOptimizeExpression(comp, &comp->steps[op->ch1]);
|
|
+ xmlXPathOptimizeExpression(pctxt, &comp->steps[op->ch1]);
|
|
if (op->ch2 != -1)
|
|
- xmlXPathOptimizeExpression(comp, &comp->steps[op->ch2]);
|
|
+ xmlXPathOptimizeExpression(pctxt, &comp->steps[op->ch2]);
|
|
+ if (ctxt != NULL)
|
|
+ ctxt->depth -= 1;
|
|
}
|
|
|
|
/**
|
|
@@ -15031,6 +15043,11 @@ xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
|
|
comp = NULL;
|
|
} else {
|
|
comp = pctxt->comp;
|
|
+ if ((comp->nbStep > 1) && (comp->last >= 0)) {
|
|
+ if (ctxt != NULL)
|
|
+ ctxt->depth = 0;
|
|
+ xmlXPathOptimizeExpression(pctxt, &comp->steps[comp->last]);
|
|
+ }
|
|
pctxt->comp = NULL;
|
|
}
|
|
xmlXPathFreeParserContext(pctxt);
|
|
@@ -15041,9 +15058,6 @@ xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
|
|
comp->string = xmlStrdup(str);
|
|
comp->nb = 0;
|
|
#endif
|
|
- if ((comp->nbStep > 1) && (comp->last >= 0)) {
|
|
- xmlXPathOptimizeExpression(comp, &comp->steps[comp->last]);
|
|
- }
|
|
}
|
|
return(comp);
|
|
}
|
|
@@ -15207,9 +15221,12 @@ xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
|
|
if (*ctxt->cur != 0)
|
|
XP_ERROR(XPATH_EXPR_ERROR);
|
|
|
|
- if ((ctxt->comp->nbStep > 1) && (ctxt->comp->last >= 0))
|
|
- xmlXPathOptimizeExpression(ctxt->comp,
|
|
+ if ((ctxt->comp->nbStep > 1) && (ctxt->comp->last >= 0)) {
|
|
+ if (ctxt->context != NULL)
|
|
+ ctxt->context->depth = 0;
|
|
+ xmlXPathOptimizeExpression(ctxt,
|
|
&ctxt->comp->steps[ctxt->comp->last]);
|
|
+ }
|
|
}
|
|
|
|
xmlXPathRunEval(ctxt, 0);
|
|
--
|
|
2.47.3
|
|
|
|
|
|
From 65b588d99e50903504bab022e2cb94b25690113b Mon Sep 17 00:00:00 2001
|
|
From: Nick Wellnhofer <wellnhofer@aevum.de>
|
|
Date: Mon, 17 Aug 2020 03:37:18 +0200
|
|
Subject: [PATCH 04/12] Stop using maxParserDepth in xpath.c
|
|
|
|
Only use a single maxDepth value.
|
|
---
|
|
xpath.c | 12 ++++++++++++
|
|
1 file changed, 12 insertions(+)
|
|
|
|
diff --git a/xpath.c b/xpath.c
|
|
index 47f394aa..8c0b793e 100644
|
|
--- a/xpath.c
|
|
+++ b/xpath.c
|
|
@@ -11069,6 +11069,18 @@ xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
|
|
*/
|
|
static void
|
|
xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort) {
|
|
+ xmlXPathContextPtr xpctxt = ctxt->context;
|
|
+
|
|
+ if (xpctxt != NULL) {
|
|
+ if (xpctxt->depth >= xpctxt->maxDepth)
|
|
+ XP_ERROR(XPATH_RECURSION_LIMIT_EXCEEDED);
|
|
+ /*
|
|
+ * Parsing a single '(' pushes about 10 functions on the call stack
|
|
+ * before recursing!
|
|
+ */
|
|
+ xpctxt->depth += 10;
|
|
+ }
|
|
+
|
|
xmlXPathCompAndExpr(ctxt);
|
|
CHECK_ERROR;
|
|
SKIP_BLANKS;
|
|
--
|
|
2.47.3
|
|
|
|
|
|
From 4afa73ca76211158e6121b1849c40952e757176c Mon Sep 17 00:00:00 2001
|
|
From: Nick Wellnhofer <wellnhofer@aevum.de>
|
|
Date: Wed, 2 Jun 2021 17:31:49 +0200
|
|
Subject: [PATCH 05/12] Fix XPath recursion limit
|
|
|
|
Fix accounting of recursion depth when parsing XPath expressions.
|
|
|
|
This silly bug introduced in commit 804c5297 could lead to spurious
|
|
errors when parsing larger expressions or XSLT documents.
|
|
|
|
Should fix #264.
|
|
---
|
|
xpath.c | 3 +++
|
|
1 file changed, 3 insertions(+)
|
|
|
|
diff --git a/xpath.c b/xpath.c
|
|
index 8c0b793e..70dda1be 100644
|
|
--- a/xpath.c
|
|
+++ b/xpath.c
|
|
@@ -11102,6 +11102,9 @@ xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort) {
|
|
*/
|
|
PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
|
|
}
|
|
+
|
|
+ if (xpctxt != NULL)
|
|
+ xpctxt->depth -= 10;
|
|
}
|
|
|
|
/**
|
|
--
|
|
2.47.3
|
|
|
|
|
|
From 20159ae640cef072e4838e38a9bf1791ea78db43 Mon Sep 17 00:00:00 2001
|
|
From: Nick Wellnhofer <wellnhofer@aevum.de>
|
|
Date: Thu, 28 Jul 2022 20:21:24 +0200
|
|
Subject: [PATCH 06/12] Make XPath depth check work with recursive invocations
|
|
|
|
EXSLT functions like dyn:map or dyn:evaluate invoke xmlXPathRunEval
|
|
recursively. Don't set depth to zero but keep and restore the original
|
|
value to avoid stack overflows when abusing these functions.
|
|
---
|
|
xpath.c | 23 +++++++++++++++++++----
|
|
1 file changed, 19 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/xpath.c b/xpath.c
|
|
index 70dda1be..b16ae0f8 100644
|
|
--- a/xpath.c
|
|
+++ b/xpath.c
|
|
@@ -14692,12 +14692,11 @@ static int
|
|
xmlXPathRunEval(xmlXPathParserContextPtr ctxt, int toBool)
|
|
{
|
|
xmlXPathCompExprPtr comp;
|
|
+ int oldDepth;
|
|
|
|
if ((ctxt == NULL) || (ctxt->comp == NULL))
|
|
return(-1);
|
|
|
|
- ctxt->context->depth = 0;
|
|
-
|
|
if (ctxt->valueTab == NULL) {
|
|
/* Allocate the value stack */
|
|
ctxt->valueTab = (xmlXPathObjectPtr *)
|
|
@@ -14751,11 +14750,13 @@ xmlXPathRunEval(xmlXPathParserContextPtr ctxt, int toBool)
|
|
"xmlXPathRunEval: last is less than zero\n");
|
|
return(-1);
|
|
}
|
|
+ oldDepth = ctxt->context->depth;
|
|
if (toBool)
|
|
return(xmlXPathCompOpEvalToBoolean(ctxt,
|
|
&comp->steps[comp->last], 0));
|
|
else
|
|
xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
|
|
+ ctxt->context->depth = oldDepth;
|
|
|
|
return(0);
|
|
}
|
|
@@ -15027,6 +15028,7 @@ xmlXPathCompExprPtr
|
|
xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
|
|
xmlXPathParserContextPtr pctxt;
|
|
xmlXPathCompExprPtr comp;
|
|
+ int oldDepth = 0;
|
|
|
|
#ifdef XPATH_STREAMING
|
|
comp = xmlXPathTryStreamCompile(ctxt, str);
|
|
@@ -15039,7 +15041,11 @@ xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
|
|
pctxt = xmlXPathNewParserContext(str, ctxt);
|
|
if (pctxt == NULL)
|
|
return NULL;
|
|
+ if (ctxt != NULL)
|
|
+ oldDepth = ctxt->depth;
|
|
xmlXPathCompileExpr(pctxt, 1);
|
|
+ if (ctxt != NULL)
|
|
+ ctxt->depth = oldDepth;
|
|
|
|
if( pctxt->error != XPATH_EXPRESSION_OK )
|
|
{
|
|
@@ -15060,8 +15066,10 @@ xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
|
|
comp = pctxt->comp;
|
|
if ((comp->nbStep > 1) && (comp->last >= 0)) {
|
|
if (ctxt != NULL)
|
|
- ctxt->depth = 0;
|
|
+ oldDepth = ctxt->depth;
|
|
xmlXPathOptimizeExpression(pctxt, &comp->steps[comp->last]);
|
|
+ if (ctxt != NULL)
|
|
+ ctxt->depth = oldDepth;
|
|
}
|
|
pctxt->comp = NULL;
|
|
}
|
|
@@ -15217,6 +15225,7 @@ xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
|
|
#ifdef XPATH_STREAMING
|
|
xmlXPathCompExprPtr comp;
|
|
#endif
|
|
+ int oldDepth = 0;
|
|
|
|
if (ctxt == NULL) return;
|
|
|
|
@@ -15229,7 +15238,11 @@ xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
|
|
} else
|
|
#endif
|
|
{
|
|
+ if (ctxt->context != NULL)
|
|
+ oldDepth = ctxt->context->depth;
|
|
xmlXPathCompileExpr(ctxt, 1);
|
|
+ if (ctxt->context != NULL)
|
|
+ ctxt->context->depth = oldDepth;
|
|
CHECK_ERROR;
|
|
|
|
/* Check for trailing characters. */
|
|
@@ -15238,9 +15251,11 @@ xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
|
|
|
|
if ((ctxt->comp->nbStep > 1) && (ctxt->comp->last >= 0)) {
|
|
if (ctxt->context != NULL)
|
|
- ctxt->context->depth = 0;
|
|
+ oldDepth = ctxt->context->depth;
|
|
xmlXPathOptimizeExpression(ctxt,
|
|
&ctxt->comp->steps[ctxt->comp->last]);
|
|
+ if (ctxt->context != NULL)
|
|
+ ctxt->context->depth = oldDepth;
|
|
}
|
|
}
|
|
|
|
--
|
|
2.47.3
|
|
|
|
|
|
From 293505285f50bb9c3e69ffa7fa49c1f25512eb09 Mon Sep 17 00:00:00 2001
|
|
From: Nick Wellnhofer <wellnhofer@aevum.de>
|
|
Date: Fri, 13 Sep 2024 20:59:47 +0200
|
|
Subject: [PATCH 07/12] xpath: Make recursion check work with xmlXPathCompile
|
|
|
|
The check for maximum recursion depth required a parser context with an
|
|
xmlXPathContext which xmlXPathCompile didn't provide.
|
|
|
|
All other code should already set up or require an xmlXPathContext.
|
|
---
|
|
xpath.c | 27 ++++++++++++++++++++++-----
|
|
1 file changed, 22 insertions(+), 5 deletions(-)
|
|
|
|
diff --git a/xpath.c b/xpath.c
|
|
index b16ae0f8..bca0ba77 100644
|
|
--- a/xpath.c
|
|
+++ b/xpath.c
|
|
@@ -15027,6 +15027,7 @@ xmlXPathOptimizeExpression(xmlXPathParserContextPtr pctxt,
|
|
xmlXPathCompExprPtr
|
|
xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
|
|
xmlXPathParserContextPtr pctxt;
|
|
+ xmlXPathContextPtr tmpctxt = NULL;
|
|
xmlXPathCompExprPtr comp;
|
|
int oldDepth = 0;
|
|
|
|
@@ -15038,18 +15039,32 @@ xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
|
|
|
|
xmlXPathInit();
|
|
|
|
+ /*
|
|
+ * We need an xmlXPathContext for the depth check.
|
|
+ */
|
|
+ if (ctxt == NULL) {
|
|
+ tmpctxt = xmlXPathNewContext(NULL);
|
|
+ if (tmpctxt == NULL)
|
|
+ return(NULL);
|
|
+ ctxt = tmpctxt;
|
|
+ }
|
|
+
|
|
pctxt = xmlXPathNewParserContext(str, ctxt);
|
|
- if (pctxt == NULL)
|
|
+ if (pctxt == NULL) {
|
|
+ if (tmpctxt != NULL)
|
|
+ xmlXPathFreeContext(tmpctxt);
|
|
return NULL;
|
|
- if (ctxt != NULL)
|
|
- oldDepth = ctxt->depth;
|
|
+ }
|
|
+
|
|
+ oldDepth = ctxt->depth;
|
|
xmlXPathCompileExpr(pctxt, 1);
|
|
- if (ctxt != NULL)
|
|
- ctxt->depth = oldDepth;
|
|
+ ctxt->depth = oldDepth;
|
|
|
|
if( pctxt->error != XPATH_EXPRESSION_OK )
|
|
{
|
|
xmlXPathFreeParserContext(pctxt);
|
|
+ if (tmpctxt != NULL)
|
|
+ xmlXPathFreeContext(tmpctxt);
|
|
return(NULL);
|
|
}
|
|
|
|
@@ -15074,6 +15089,8 @@ xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
|
|
pctxt->comp = NULL;
|
|
}
|
|
xmlXPathFreeParserContext(pctxt);
|
|
+ if (tmpctxt != NULL)
|
|
+ xmlXPathFreeContext(tmpctxt);
|
|
|
|
if (comp != NULL) {
|
|
comp->expr = xmlStrdup(str);
|
|
--
|
|
2.47.3
|
|
|
|
|
|
From 0afa658c7820cf32d5d880a4b3f4eb9ea95898b4 Mon Sep 17 00:00:00 2001
|
|
From: RHEL Packaging Agent <jotnar@redhat.com>
|
|
Date: Tue, 18 Nov 2025 14:20:08 +0000
|
|
Subject: [PATCH 08/12] xpath: Fix depth restoration in xmlXPathRunEval
|
|
|
|
Ensure that the depth is always restored in xmlXPathRunEval, even when
|
|
toBool is true or when an error occurs. This prevents depth from
|
|
accumulating incorrectly across XPath evaluations.
|
|
---
|
|
xpath.c | 13 ++++++++-----
|
|
1 file changed, 8 insertions(+), 5 deletions(-)
|
|
|
|
diff --git a/xpath.c b/xpath.c
|
|
index bca0ba77..90d624a2 100644
|
|
--- a/xpath.c
|
|
+++ b/xpath.c
|
|
@@ -14751,12 +14751,15 @@ xmlXPathRunEval(xmlXPathParserContextPtr ctxt, int toBool)
|
|
return(-1);
|
|
}
|
|
oldDepth = ctxt->context->depth;
|
|
- if (toBool)
|
|
- return(xmlXPathCompOpEvalToBoolean(ctxt,
|
|
- &comp->steps[comp->last], 0));
|
|
- else
|
|
+ if (toBool) {
|
|
+ int ret = xmlXPathCompOpEvalToBoolean(ctxt,
|
|
+ &comp->steps[comp->last], 0);
|
|
+ ctxt->context->depth = oldDepth;
|
|
+ return(ret);
|
|
+ } else {
|
|
xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
|
|
- ctxt->context->depth = oldDepth;
|
|
+ ctxt->context->depth = oldDepth;
|
|
+ }
|
|
|
|
return(0);
|
|
}
|
|
--
|
|
2.47.3
|
|
|
|
|
|
From e0315a0e773982b10220cfbad25b92bc97119d80 Mon Sep 17 00:00:00 2001
|
|
From: RHEL Packaging Agent <jotnar@redhat.com>
|
|
Date: Tue, 18 Nov 2025 14:33:12 +0000
|
|
Subject: [PATCH 09/12] Revert incorrect depth restoration in toBool branch
|
|
|
|
The manual fix that added depth restoration to the toBool branch of
|
|
xmlXPathRunEval was incorrect. The toBool branch should return directly
|
|
from xmlXPathCompOpEvalToBoolean without restoring depth, as
|
|
xmlXPathCompOpEvalToBoolean either doesn't modify depth or calls
|
|
xmlXPathCompOpEval which properly manages depth internally.
|
|
|
|
This matches the implementation in upstream master branch.
|
|
---
|
|
xpath.c | 13 +++++--------
|
|
1 file changed, 5 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/xpath.c b/xpath.c
|
|
index 90d624a2..bca0ba77 100644
|
|
--- a/xpath.c
|
|
+++ b/xpath.c
|
|
@@ -14751,15 +14751,12 @@ xmlXPathRunEval(xmlXPathParserContextPtr ctxt, int toBool)
|
|
return(-1);
|
|
}
|
|
oldDepth = ctxt->context->depth;
|
|
- if (toBool) {
|
|
- int ret = xmlXPathCompOpEvalToBoolean(ctxt,
|
|
- &comp->steps[comp->last], 0);
|
|
- ctxt->context->depth = oldDepth;
|
|
- return(ret);
|
|
- } else {
|
|
+ if (toBool)
|
|
+ return(xmlXPathCompOpEvalToBoolean(ctxt,
|
|
+ &comp->steps[comp->last], 0));
|
|
+ else
|
|
xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
|
|
- ctxt->context->depth = oldDepth;
|
|
- }
|
|
+ ctxt->context->depth = oldDepth;
|
|
|
|
return(0);
|
|
}
|
|
--
|
|
2.47.3
|
|
|
|
|
|
From cb9a07705374ae359bddde2ed7f8a578b2315e93 Mon Sep 17 00:00:00 2001
|
|
From: Nick Wellnhofer <wellnhofer@aevum.de>
|
|
Date: Wed, 8 May 2019 12:00:51 +0200
|
|
Subject: [PATCH 10/12] Limit recursion depth in xmlXPathCompOpEvalPredicate
|
|
|
|
---
|
|
xpath.c | 4 ++++
|
|
1 file changed, 4 insertions(+)
|
|
|
|
diff --git a/xpath.c b/xpath.c
|
|
index bca0ba77..9406d05f 100644
|
|
--- a/xpath.c
|
|
+++ b/xpath.c
|
|
@@ -11740,8 +11740,12 @@ xmlXPathCompOpEvalPredicate(xmlXPathParserContextPtr ctxt,
|
|
* TODO: raise an internal error.
|
|
*/
|
|
}
|
|
+ if (ctxt->context->depth >= ctxt->context->maxDepth)
|
|
+ XP_ERROR0(XPATH_RECURSION_LIMIT_EXCEEDED);
|
|
+ ctxt->context->depth += 1;
|
|
contextSize = xmlXPathCompOpEvalPredicate(ctxt,
|
|
&comp->steps[op->ch1], set, contextSize, hasNsNodes);
|
|
+ ctxt->context->depth -= 1;
|
|
CHECK_ERROR0;
|
|
if (contextSize <= 0)
|
|
return(0);
|
|
--
|
|
2.47.3
|
|
|
|
|
|
From 0e1013a03a95d5791ace90fd072a72f85ee82676 Mon Sep 17 00:00:00 2001
|
|
From: RHEL Packaging Agent <jotnar@redhat.com>
|
|
Date: Tue, 18 Nov 2025 15:03:03 +0000
|
|
Subject: [PATCH 11/12] Fix missing braces in XPATH_OP_AND and XPATH_OP_OR
|
|
cases
|
|
|
|
The depth decrement and return statement must be inside the if block,
|
|
otherwise depth is not decremented when the condition is false, causing
|
|
depth to accumulate and eventually exceed the limit.
|
|
|
|
This fixes test failures where XPath evaluation was failing with
|
|
'Error: unable to evaluate xpath expression'.
|
|
---
|
|
xpath.c | 6 ++++--
|
|
1 file changed, 4 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/xpath.c b/xpath.c
|
|
index 9406d05f..691cbd02 100644
|
|
--- a/xpath.c
|
|
+++ b/xpath.c
|
|
@@ -13443,9 +13443,10 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
|
|
CHECK_ERROR0;
|
|
xmlXPathBooleanFunction(ctxt, 1);
|
|
- if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
|
|
+ if ((ctxt->value == NULL) || (ctxt->value->boolval == 0)) {
|
|
ctxt->context->depth -= 1;
|
|
return (total);
|
|
+ }
|
|
arg2 = valuePop(ctxt);
|
|
ctxt->context->doc = bakd;
|
|
ctxt->context->node = bak;
|
|
@@ -13470,9 +13471,10 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
|
|
CHECK_ERROR0;
|
|
xmlXPathBooleanFunction(ctxt, 1);
|
|
- if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
|
|
+ if ((ctxt->value == NULL) || (ctxt->value->boolval == 1)) {
|
|
ctxt->context->depth -= 1;
|
|
return (total);
|
|
+ }
|
|
arg2 = valuePop(ctxt);
|
|
ctxt->context->doc = bakd;
|
|
ctxt->context->node = bak;
|
|
--
|
|
2.47.3
|
|
|
|
|
|
From 368c7c8da85446ca7ba3ddb1cc7f2ed3a3b36865 Mon Sep 17 00:00:00 2001
|
|
From: RHEL Packaging Agent <jotnar@redhat.com>
|
|
Date: Tue, 18 Nov 2025 15:13:35 +0000
|
|
Subject: [PATCH 12/12] Fix missing braces in XPATH_OP_COLLECT,
|
|
XPATH_OP_PREDICATE/FILTER, and XPATH_OP_RANGETO cases
|
|
|
|
The depth decrement and return statement must be inside the if block,
|
|
otherwise depth is not decremented when the condition is false, causing
|
|
depth to accumulate and eventually exceed the limit.
|
|
|
|
This fixes test failures where XPath evaluation was failing with
|
|
'Error: unable to evaluate xpath expression'.
|
|
---
|
|
xpath.c | 12 ++++++++----
|
|
1 file changed, 8 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/xpath.c b/xpath.c
|
|
index 691cbd02..07685880 100644
|
|
--- a/xpath.c
|
|
+++ b/xpath.c
|
|
@@ -13648,9 +13648,10 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
ctxt->context->depth -= 1;
|
|
return (total);
|
|
case XPATH_OP_COLLECT:{
|
|
- if (op->ch1 == -1)
|
|
+ if (op->ch1 == -1) {
|
|
ctxt->context->depth -= 1;
|
|
return (total);
|
|
+ }
|
|
|
|
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
|
|
CHECK_ERROR0;
|
|
@@ -13904,12 +13905,14 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
total +=
|
|
xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
|
|
CHECK_ERROR0;
|
|
- if (op->ch2 == -1)
|
|
+ if (op->ch2 == -1) {
|
|
ctxt->context->depth -= 1;
|
|
return (total);
|
|
- if (ctxt->value == NULL)
|
|
+ }
|
|
+ if (ctxt->value == NULL) {
|
|
ctxt->context->depth -= 1;
|
|
return (total);
|
|
+ }
|
|
|
|
oldnode = ctxt->context->node;
|
|
|
|
@@ -14190,9 +14193,10 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|
if (ctxt->value == NULL) {
|
|
XP_ERROR0(XPATH_INVALID_OPERAND);
|
|
}
|
|
- if (op->ch2 == -1)
|
|
+ if (op->ch2 == -1) {
|
|
ctxt->context->depth -= 1;
|
|
return (total);
|
|
+ }
|
|
|
|
if (ctxt->value->type == XPATH_LOCATIONSET) {
|
|
/*
|
|
--
|
|
2.47.3
|
|
|