From 497fb24df7bdced5a8548f50b7b282ec73886a51 Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer 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 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 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 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 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 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 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 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 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 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 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 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