diff --git a/libxml2-2.9.7-CVE-2025-9714.patch b/libxml2-2.9.7-CVE-2025-9714.patch new file mode 100644 index 0000000..b5778b0 --- /dev/null +++ b/libxml2-2.9.7-CVE-2025-9714.patch @@ -0,0 +1,1299 @@ +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 + diff --git a/libxml2.spec b/libxml2.spec index 0494f77..900163a 100644 --- a/libxml2.spec +++ b/libxml2.spec @@ -7,7 +7,7 @@ Name: libxml2 Version: 2.9.7 -Release: 21%{?dist}.3 +Release: 21%{?dist}.4 Summary: Library providing XML and HTML support License: MIT @@ -83,6 +83,8 @@ Patch32: libxml2-2.9.13-CVE-2025-49794.patch Patch33: libxml2-2.9.7-CVE-2025-7425.patch # https://issues.redhat.com/browse/RHEL-100177 Patch34: libxml2-2.12.5-CVE-2025-32415.patch +# https://issues.redhat.com/browse/RHEL-119279 +Patch35: libxml2-2.9.7-CVE-2025-9714.patch BuildRequires: gcc BuildRequires: cmake-rpm-macros @@ -254,6 +256,9 @@ gzip -9 -c doc/libxml2-api.xml > doc/libxml2-api.xml.gz %{python3_sitearch}/libxml2mod.so %changelog +* Tue Dec 09 2025 David King - 2.9.7-21.4 +- Fix CVE-2025-9714 (RHEL-119279) + * Tue Aug 05 2025 David King - 2.9.7.21.3 - Fix CVE-2025-32415 (RHEL-100177)