File libxml2-CVE-2025-9714-0.patch of Package libxml2.41582
From 852c93a2dc2224f020aab55a9702f992db404836 Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <wellnhofer@aevum.de>
Date: Tue, 12 Mar 2019 16:12:05 +0100
Subject: [PATCH] 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(-)
Index: libxml2-2.9.7/include/libxml/xpath.h
===================================================================
--- libxml2-2.9.7.orig/include/libxml/xpath.h
+++ libxml2-2.9.7/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;
};
/*
Index: libxml2-2.9.7/xpath.c
===================================================================
--- libxml2-2.9.7.orig/xpath.c
+++ libxml2-2.9.7/xpath.c
@@ -645,6 +645,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) / \
@@ -782,6 +783,32 @@ xmlXPatherror(xmlXPathParserContextPtr c
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 *
@@ -12371,6 +12398,9 @@ xmlXPathNodeCollectAndTest(xmlXPathParse
cur = NULL;
hasNsNodes = 0;
do {
+ if (OP_LIMIT_EXCEEDED(ctxt, 1))
+ goto error;
+
cur = next(ctxt, cur);
if (cur == NULL)
break;
@@ -12774,6 +12804,8 @@ xmlXPathCompOpEvalFirst(xmlXPathParserCo
xmlXPathObjectPtr arg1, arg2;
CHECK_ERROR0;
+ if (OP_LIMIT_EXCEEDED(ctxt, 1))
+ return(0);
comp = ctxt->comp;
switch (op->op) {
case XPATH_OP_END:
@@ -12814,6 +12846,17 @@ xmlXPathCompOpEvalFirst(xmlXPathParserCo
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);
@@ -12906,6 +12949,8 @@ xmlXPathCompOpEvalLast(xmlXPathParserCon
int cs;
CHECK_ERROR0;
+ if (OP_LIMIT_EXCEEDED(ctxt, 1))
+ return(0);
comp = ctxt->comp;
switch (op->op) {
case XPATH_OP_END:
@@ -12953,6 +12998,17 @@ xmlXPathCompOpEvalLast(xmlXPathParserCon
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);
@@ -13330,6 +13386,8 @@ xmlXPathCompOpEval(xmlXPathParserContext
int cs;
CHECK_ERROR0;
+ if (OP_LIMIT_EXCEEDED(ctxt, 1))
+ return(0);
comp = ctxt->comp;
switch (op->op) {
case XPATH_OP_END:
@@ -13487,6 +13545,17 @@ xmlXPathCompOpEval(xmlXPathParserContext
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) &&
@@ -14218,6 +14287,8 @@ xmlXPathCompOpEvalToBoolean(xmlXPathPars
xmlXPathObjectPtr resObj = NULL;
start:
+ if (OP_LIMIT_EXCEEDED(ctxt, 1))
+ return(0);
/* comp = ctxt->comp; */
switch (op->op) {
case XPATH_OP_END:
@@ -14417,6 +14488,16 @@ xmlXPathRunStreamEval(xmlXPathContextPtr
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) {