Compare commits

..

No commits in common. "c8-beta" and "c8" have entirely different histories.
c8-beta ... c8

5 changed files with 2018 additions and 1 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,103 @@
From 5b3b3151e4af0f6c234c97e01e05cf6edc9eceab Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <wellnhofer@aevum.de>
Date: Tue, 21 Mar 2023 12:19:50 +0100
Subject: [PATCH 1/2] malloc-fail: Fix memory leak in exclPrefixPush
Found by OSS-Fuzz, see #84.
---
libxslt/xslt.c | 24 ++++++++----------------
1 file changed, 8 insertions(+), 16 deletions(-)
diff --git a/libxslt/xslt.c b/libxslt/xslt.c
index 7a1ce011..6d4126a1 100644
--- a/libxslt/xslt.c
+++ b/libxslt/xslt.c
@@ -157,31 +157,23 @@ exclPrefixPush(xsltStylesheetPtr style, xmlChar * value)
{
int i;
- if (style->exclPrefixMax == 0) {
- style->exclPrefixMax = 4;
- style->exclPrefixTab =
- (xmlChar * *)xmlMalloc(style->exclPrefixMax *
- sizeof(style->exclPrefixTab[0]));
- if (style->exclPrefixTab == NULL) {
- xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
- return (-1);
- }
- }
/* do not push duplicates */
for (i = 0;i < style->exclPrefixNr;i++) {
if (xmlStrEqual(style->exclPrefixTab[i], value))
return(-1);
}
if (style->exclPrefixNr >= style->exclPrefixMax) {
- style->exclPrefixMax *= 2;
- style->exclPrefixTab =
- (xmlChar * *)xmlRealloc(style->exclPrefixTab,
- style->exclPrefixMax *
- sizeof(style->exclPrefixTab[0]));
- if (style->exclPrefixTab == NULL) {
+ xmlChar **tmp;
+ size_t max = style->exclPrefixMax ? style->exclPrefixMax * 2 : 4;
+
+ tmp = xmlRealloc(style->exclPrefixTab,
+ max * sizeof(style->exclPrefixTab[0]));
+ if (tmp == NULL) {
xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
return (-1);
}
+ style->exclPrefixTab = tmp;
+ style->exclPrefixMax = max;
}
style->exclPrefixTab[style->exclPrefixNr] = value;
style->exclPrefix = value;
--
2.49.0
From 43c2b70b12717940ff9141c3bc2dc7f3a49df2b5 Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <wellnhofer@aevum.de>
Date: Thu, 5 Dec 2024 12:43:19 +0100
Subject: [PATCH 2/2] [CVE-2024-55549] Fix UAF related to excluded namespaces
Definitions of excluded namespaces could be deleted in
xsltParseTemplateContent. Store excluded namespace URIs in the
stylesheet's dictionary instead of referencing the namespace definition.
Thanks to Ivan Fratric for the report!
Fixes #127.
---
libxslt/xslt.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/libxslt/xslt.c b/libxslt/xslt.c
index 6d4126a1..11681a13 100644
--- a/libxslt/xslt.c
+++ b/libxslt/xslt.c
@@ -153,10 +153,20 @@ xsltParseContentError(xsltStylesheetPtr style,
* in case of error
*/
static int
-exclPrefixPush(xsltStylesheetPtr style, xmlChar * value)
+exclPrefixPush(xsltStylesheetPtr style, xmlChar * orig)
{
+ xmlChar *value;
int i;
+ /*
+ * orig can come from a namespace definition on a node which
+ * could be deleted later, for example in xsltParseTemplateContent.
+ * Store the string in stylesheet's dict to avoid use after free.
+ */
+ value = (xmlChar *) xmlDictLookup(style->dict, orig, -1);
+ if (value == NULL)
+ return(-1);
+
/* do not push duplicates */
for (i = 0;i < style->exclPrefixNr;i++) {
if (xmlStrEqual(style->exclPrefixTab[i], value))
--
2.49.0

View File

@ -0,0 +1,718 @@
From 5a50e73fa2e48e5ccdb8f36ebaeeaf8404e33879 Mon Sep 17 00:00:00 2001
From: Daniel Cheng <zetafunction@gmail.com>
Date: Thu, 5 Jun 2025 09:43:53 -0700
Subject: [PATCH] Use a dedicated node type to maintain the list of cached RVTs
While evaluating a stylesheet, result value trees (result tree fragments
in the XSLT spec) are represented as xmlDocs and cached on the transform
context in a linked list, using xmlDoc's prev and next pointers to
maintain the list.
However, XPath evaluations can inadvertently traverse these links, which
are an implementation detail and do not reflect the actual document
structure. Using a dedicated node type avoids these unintended
traversals.
Fixes #144.
---
libxslt/transform.c | 93 ++++++++---------
libxslt/variables.c | 224 +++++++++++++++++++++++++---------------
libxslt/xsltInternals.h | 23 +++--
3 files changed, 201 insertions(+), 139 deletions(-)
diff --git a/libxslt/transform.c b/libxslt/transform.c
index 8a424773..b500b35d 100644
--- a/libxslt/transform.c
+++ b/libxslt/transform.c
@@ -521,19 +521,20 @@ xsltTransformCacheFree(xsltTransformCachePtr cache)
/*
* Free tree fragments.
*/
- if (cache->RVT) {
- xmlDocPtr tmp, cur = cache->RVT;
+ if (cache->rvtList) {
+ xsltRVTListPtr tmp, cur = cache->rvtList;
while (cur) {
tmp = cur;
- cur = (xmlDocPtr) cur->next;
- if (tmp->_private != NULL) {
+ cur = cur->next;
+ if (tmp->RVT->_private != NULL) {
/*
- * Tree the document info.
+ * Free the document info.
*/
- xsltFreeDocumentKeys((xsltDocumentPtr) tmp->_private);
- xmlFree(tmp->_private);
+ xsltFreeDocumentKeys((xsltDocumentPtr) tmp->RVT->_private);
+ xmlFree(tmp->RVT->_private);
}
- xmlFreeDoc(tmp);
+ xmlFreeDoc(tmp->RVT);
+ xmlFree(tmp);
}
}
/*
@@ -2294,43 +2295,39 @@ xsltLocalVariablePush(xsltTransformContextPtr ctxt,
* are preserved; all other fragments are freed/cached.
*/
static void
-xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base)
+xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xsltRVTListPtr base)
{
- xmlDocPtr cur = ctxt->localRVT, tmp;
- xmlDocPtr prev = NULL;
+ xsltRVTListPtr cur = ctxt->localRVTList, tmp;
if (cur == base)
return;
- if (cur->prev != NULL)
- xsltTransformError(ctxt, NULL, NULL, "localRVT not head of list\n");
+
+ /* Reset localRVTList early because some RVTs might be registered again. */
+ ctxt->localRVTList = base;
+
do {
tmp = cur;
- cur = (xmlDocPtr) cur->next;
- if (tmp->compression == XSLT_RVT_LOCAL) {
- xsltReleaseRVT(ctxt, tmp);
- } else if (tmp->compression == XSLT_RVT_GLOBAL) {
- xsltRegisterPersistRVT(ctxt, tmp);
- } else if (tmp->compression == XSLT_RVT_FUNC_RESULT) {
+ cur = cur->next;
+ if (tmp->RVT->compression == XSLT_RVT_LOCAL) {
+ xsltReleaseRVTList(ctxt, tmp);
+ } else if (tmp->RVT->compression == XSLT_RVT_GLOBAL) {
+ xsltRegisterPersistRVT(ctxt, tmp->RVT);
+ xmlFree(tmp);
+ } else if (tmp->RVT->compression == XSLT_RVT_FUNC_RESULT) {
/*
* This will either register the RVT again or move it to the
* context variable.
*/
- xsltRegisterLocalRVT(ctxt, tmp);
- tmp->compression = XSLT_RVT_FUNC_RESULT;
+ xsltRegisterLocalRVT(ctxt, tmp->RVT);
+ tmp->RVT->compression = XSLT_RVT_FUNC_RESULT;
+ xmlFree(tmp);
} else {
xmlGenericError(xmlGenericErrorContext,
- "xsltReleaseLocalRVTs: Unexpected RVT flag %p\n",
- tmp->compression);
+ "xsltReleaseLocalRVTs: Unexpected RVT flag %d\n",
+ tmp->RVT->compression);
}
} while (cur != base);
-
- if (prev == NULL)
- ctxt->localRVT = base;
- else
- prev->next = (xmlNodePtr) base;
- if (base != NULL)
- base->prev = (xmlNodePtr) prev;
}
/**
@@ -2356,7 +2353,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
xmlNodePtr oldInsert, oldInst, oldCurInst, oldContextNode;
xmlNodePtr cur, insert, copy = NULL;
int level = 0, oldVarsNr;
- xmlDocPtr oldLocalFragmentTop;
+ xsltRVTListPtr oldLocalFragmentTop;
#ifdef XSLT_REFACTORED
xsltStylePreCompPtr info;
@@ -2402,7 +2399,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
}
ctxt->depth++;
- oldLocalFragmentTop = ctxt->localRVT;
+ oldLocalFragmentTop = ctxt->localRVTList;
oldInsert = insert = ctxt->insert;
oldInst = oldCurInst = ctxt->inst;
oldContextNode = ctxt->node;
@@ -2625,7 +2622,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
/*
* Cleanup temporary tree fragments.
*/
- if (oldLocalFragmentTop != ctxt->localRVT)
+ if (oldLocalFragmentTop != ctxt->localRVTList)
xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
ctxt->insert = oldInsert;
@@ -2720,7 +2717,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
/*
* Cleanup temporary tree fragments.
*/
- if (oldLocalFragmentTop != ctxt->localRVT)
+ if (oldLocalFragmentTop != ctxt->localRVTList)
xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
ctxt->insert = oldInsert;
@@ -2786,7 +2783,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
/*
* Cleanup temporary tree fragments.
*/
- if (oldLocalFragmentTop != ctxt->localRVT)
+ if (oldLocalFragmentTop != ctxt->localRVTList)
xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
ctxt->insert = oldInsert;
@@ -2914,7 +2911,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
/*
* Cleanup temporary tree fragments.
*/
- if (oldLocalFragmentTop != ctxt->localRVT)
+ if (oldLocalFragmentTop != ctxt->localRVTList)
xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
ctxt->insert = oldInsert;
@@ -3094,7 +3091,7 @@ xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
long start = 0;
xmlNodePtr cur;
xsltStackElemPtr tmpParam = NULL;
- xmlDocPtr oldUserFragmentTop;
+ xsltRVTListPtr oldUserFragmentTop;
#ifdef XSLT_REFACTORED
xsltStyleItemParamPtr iparam;
@@ -3139,8 +3136,8 @@ xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
return;
}
- oldUserFragmentTop = ctxt->tmpRVT;
- ctxt->tmpRVT = NULL;
+ oldUserFragmentTop = ctxt->tmpRVTList;
+ ctxt->tmpRVTList = NULL;
/*
* Initiate a distinct scope of local params/variables.
@@ -3247,16 +3244,16 @@ xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
* user code should now use xsltRegisterLocalRVT() instead
* of the obsolete xsltRegisterTmpRVT().
*/
- if (ctxt->tmpRVT) {
- xmlDocPtr curdoc = ctxt->tmpRVT, tmp;
+ if (ctxt->tmpRVTList) {
+ xsltRVTListPtr curRVTList = ctxt->tmpRVTList, tmp;
- while (curdoc != NULL) {
- tmp = curdoc;
- curdoc = (xmlDocPtr) curdoc->next;
- xsltReleaseRVT(ctxt, tmp);
+ while (curRVTList != NULL) {
+ tmp = curRVTList;
+ curRVTList = curRVTList->next;
+ xsltReleaseRVTList(ctxt, tmp);
}
}
- ctxt->tmpRVT = oldUserFragmentTop;
+ ctxt->tmpRVTList = oldUserFragmentTop;
/*
* Pop the xsl:template declaration from the stack.
@@ -5392,7 +5389,7 @@ xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
#ifdef XSLT_FAST_IF
{
- xmlDocPtr oldLocalFragmentTop = ctxt->localRVT;
+ xsltRVTListPtr oldLocalFragmentTop = ctxt->localRVTList;
res = xsltPreCompEvalToBoolean(ctxt, contextNode, comp);
@@ -5400,7 +5397,7 @@ xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
* Cleanup fragments created during evaluation of the
* "select" expression.
*/
- if (oldLocalFragmentTop != ctxt->localRVT)
+ if (oldLocalFragmentTop != ctxt->localRVTList)
xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
}
diff --git a/libxslt/variables.c b/libxslt/variables.c
index 0f4b72f3..e1d9938c 100644
--- a/libxslt/variables.c
+++ b/libxslt/variables.c
@@ -47,6 +47,21 @@ static const xmlChar *xsltComputingGlobalVarMarker =
#define XSLT_VAR_IN_SELECT (1<<1)
#define XSLT_TCTXT_VARIABLE(c) ((xsltStackElemPtr) (c)->contextVariable)
+static xsltRVTListPtr
+xsltRVTListCreate(void)
+{
+ xsltRVTListPtr ret;
+
+ ret = (xsltRVTListPtr) xmlMalloc(sizeof(xsltRVTList));
+ if (ret == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltRVTListCreate: malloc failed\n");
+ return(NULL);
+ }
+ memset(ret, 0, sizeof(xsltRVTList));
+ return(ret);
+}
+
/************************************************************************
* *
* Result Value Tree (Result Tree Fragment) interfaces *
@@ -64,6 +79,7 @@ static const xmlChar *xsltComputingGlobalVarMarker =
xmlDocPtr
xsltCreateRVT(xsltTransformContextPtr ctxt)
{
+ xsltRVTListPtr rvtList;
xmlDocPtr container;
/*
@@ -76,12 +92,11 @@ xsltCreateRVT(xsltTransformContextPtr ctxt)
/*
* Reuse a RTF from the cache if available.
*/
- if (ctxt->cache->RVT) {
- container = ctxt->cache->RVT;
- ctxt->cache->RVT = (xmlDocPtr) container->next;
- /* clear the internal pointers */
- container->next = NULL;
- container->prev = NULL;
+ if (ctxt->cache->rvtList) {
+ rvtList = ctxt->cache->rvtList;
+ container = ctxt->cache->rvtList->RVT;
+ ctxt->cache->rvtList = rvtList->next;
+ xmlFree(rvtList);
if (ctxt->cache->nbRVT > 0)
ctxt->cache->nbRVT--;
#ifdef XSLT_DEBUG_PROFILE_CACHE
@@ -119,11 +134,16 @@ xsltCreateRVT(xsltTransformContextPtr ctxt)
int
xsltRegisterTmpRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
{
+ xsltRVTListPtr list;
+
if ((ctxt == NULL) || (RVT == NULL))
return(-1);
- RVT->prev = NULL;
+ list = xsltRVTListCreate();
+ if (list == NULL) return(-1);
+
RVT->compression = XSLT_RVT_LOCAL;
+ list->RVT = RVT;
/*
* We'll restrict the lifetime of user-created fragments
@@ -131,15 +151,13 @@ xsltRegisterTmpRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
* var/param itself.
*/
if (ctxt->contextVariable != NULL) {
- RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment;
- XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT;
+ list->next = XSLT_TCTXT_VARIABLE(ctxt)->fragment;
+ XSLT_TCTXT_VARIABLE(ctxt)->fragment = list;
return(0);
}
- RVT->next = (xmlNodePtr) ctxt->tmpRVT;
- if (ctxt->tmpRVT != NULL)
- ctxt->tmpRVT->prev = (xmlNodePtr) RVT;
- ctxt->tmpRVT = RVT;
+ list->next = ctxt->tmpRVTList;
+ ctxt->tmpRVTList = list;
return(0);
}
@@ -159,11 +177,16 @@ int
xsltRegisterLocalRVT(xsltTransformContextPtr ctxt,
xmlDocPtr RVT)
{
+ xsltRVTListPtr list;
+
if ((ctxt == NULL) || (RVT == NULL))
return(-1);
- RVT->prev = NULL;
+ list = xsltRVTListCreate();
+ if (list == NULL) return(-1);
+
RVT->compression = XSLT_RVT_LOCAL;
+ list->RVT = RVT;
/*
* When evaluating "select" expressions of xsl:variable
@@ -174,8 +197,8 @@ xsltRegisterLocalRVT(xsltTransformContextPtr ctxt,
if ((ctxt->contextVariable != NULL) &&
(XSLT_TCTXT_VARIABLE(ctxt)->flags & XSLT_VAR_IN_SELECT))
{
- RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment;
- XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT;
+ list->next = XSLT_TCTXT_VARIABLE(ctxt)->fragment;
+ XSLT_TCTXT_VARIABLE(ctxt)->fragment = list;
return(0);
}
/*
@@ -183,10 +206,8 @@ xsltRegisterLocalRVT(xsltTransformContextPtr ctxt,
* If not reference by a returning instruction (like EXSLT's function),
* then this fragment will be freed, when the instruction exits.
*/
- RVT->next = (xmlNodePtr) ctxt->localRVT;
- if (ctxt->localRVT != NULL)
- ctxt->localRVT->prev = (xmlNodePtr) RVT;
- ctxt->localRVT = RVT;
+ list->next = ctxt->localRVTList;
+ ctxt->localRVTList = list;
return(0);
}
@@ -340,8 +361,9 @@ xsltFlagRVTs(xsltTransformContextPtr ctxt, xmlXPathObjectPtr obj, int val) {
* @ctxt: an XSLT transformation context
* @RVT: a result value tree (Result Tree Fragment)
*
- * Either frees the RVT (which is an xmlDoc) or stores
- * it in the context's cache for later reuse.
+ * Either frees the RVT (which is an xmlDoc) or stores it in the context's
+ * cache for later reuse. Preserved for ABI/API compatibility; internal use
+ * has all migrated to xsltReleaseRVTList().
*/
void
xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
@@ -349,41 +371,64 @@ xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
if (RVT == NULL)
return;
+ xsltRVTListPtr list = xsltRVTListCreate();
+ if (list == NULL) {
+ if (RVT->_private != NULL) {
+ xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private);
+ xmlFree(RVT->_private);
+ }
+ xmlFreeDoc(RVT);
+ return;
+ }
+
+ xsltReleaseRVTList(ctxt, list);
+}
+
+/**
+ * xsltReleaseRVTList:
+ * @ctxt: an XSLT transformation context
+ * @list: a list node containing a result value tree (Result Tree Fragment)
+ *
+ * Either frees the list node or stores it in the context's cache for later
+ * reuse. Optimization to avoid adding a fallible allocation path when the
+ * caller already has a RVT list node.
+ */
+void
+xsltReleaseRVTList(xsltTransformContextPtr ctxt, xsltRVTListPtr list)
+{
+ if (list == NULL)
+ return;
+
if (ctxt && (ctxt->cache->nbRVT < 40)) {
/*
* Store the Result Tree Fragment.
* Free the document info.
*/
- if (RVT->_private != NULL) {
- xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private);
- xmlFree(RVT->_private);
- RVT->_private = NULL;
+ if (list->RVT->_private != NULL) {
+ xsltFreeDocumentKeys((xsltDocumentPtr) list->RVT->_private);
+ xmlFree(list->RVT->_private);
+ list->RVT->_private = NULL;
}
/*
* Clear the document tree.
- * REVISIT TODO: Do we expect ID/IDREF tables to be existent?
*/
- if (RVT->children != NULL) {
- xmlFreeNodeList(RVT->children);
- RVT->children = NULL;
- RVT->last = NULL;
+ if (list->RVT->children != NULL) {
+ xmlFreeNodeList(list->RVT->children);
+ list->RVT->children = NULL;
+ list->RVT->last = NULL;
}
- if (RVT->ids != NULL) {
- xmlFreeIDTable((xmlIDTablePtr) RVT->ids);
- RVT->ids = NULL;
- }
- if (RVT->refs != NULL) {
- xmlFreeRefTable((xmlRefTablePtr) RVT->refs);
- RVT->refs = NULL;
+ if (list->RVT->ids != NULL) {
+ xmlFreeIDTable((xmlIDTablePtr) list->RVT->ids);
+ list->RVT->ids = NULL;
}
/*
* Reset the ownership information.
*/
- RVT->compression = 0;
+ list->RVT->compression = 0;
- RVT->next = (xmlNodePtr) ctxt->cache->RVT;
- ctxt->cache->RVT = RVT;
+ list->next = ctxt->cache->rvtList;
+ ctxt->cache->rvtList = list;
ctxt->cache->nbRVT++;
@@ -395,11 +440,12 @@ xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
/*
* Free it.
*/
- if (RVT->_private != NULL) {
- xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private);
- xmlFree(RVT->_private);
+ if (list->RVT->_private != NULL) {
+ xsltFreeDocumentKeys((xsltDocumentPtr) list->RVT->_private);
+ xmlFree(list->RVT->_private);
}
- xmlFreeDoc(RVT);
+ xmlFreeDoc(list->RVT);
+ xmlFree(list);
}
/**
@@ -417,14 +463,17 @@ xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
int
xsltRegisterPersistRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
{
+ xsltRVTListPtr list;
+
if ((ctxt == NULL) || (RVT == NULL)) return(-1);
+ list = xsltRVTListCreate();
+ if (list == NULL) return(-1);
+
RVT->compression = XSLT_RVT_GLOBAL;
- RVT->prev = NULL;
- RVT->next = (xmlNodePtr) ctxt->persistRVT;
- if (ctxt->persistRVT != NULL)
- ctxt->persistRVT->prev = (xmlNodePtr) RVT;
- ctxt->persistRVT = RVT;
+ list->RVT = RVT;
+ list->next = ctxt->persistRVTList;
+ ctxt->persistRVTList = list;
return(0);
}
@@ -439,52 +488,55 @@ xsltRegisterPersistRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
void
xsltFreeRVTs(xsltTransformContextPtr ctxt)
{
- xmlDocPtr cur, next;
+ xsltRVTListPtr cur, next;
if (ctxt == NULL)
return;
/*
* Local fragments.
*/
- cur = ctxt->localRVT;
+ cur = ctxt->localRVTList;
while (cur != NULL) {
- next = (xmlDocPtr) cur->next;
- if (cur->_private != NULL) {
- xsltFreeDocumentKeys(cur->_private);
- xmlFree(cur->_private);
+ next = cur->next;
+ if (cur->RVT->_private != NULL) {
+ xsltFreeDocumentKeys(cur->RVT->_private);
+ xmlFree(cur->RVT->_private);
}
- xmlFreeDoc(cur);
+ xmlFreeDoc(cur->RVT);
+ xmlFree(cur);
cur = next;
}
- ctxt->localRVT = NULL;
+ ctxt->localRVTList = NULL;
/*
* User-created per-template fragments.
*/
- cur = ctxt->tmpRVT;
+ cur = ctxt->tmpRVTList;
while (cur != NULL) {
- next = (xmlDocPtr) cur->next;
- if (cur->_private != NULL) {
- xsltFreeDocumentKeys(cur->_private);
- xmlFree(cur->_private);
+ next = cur->next;
+ if (cur->RVT->_private != NULL) {
+ xsltFreeDocumentKeys(cur->RVT->_private);
+ xmlFree(cur->RVT->_private);
}
- xmlFreeDoc(cur);
+ xmlFreeDoc(cur->RVT);
+ xmlFree(cur);
cur = next;
}
- ctxt->tmpRVT = NULL;
+ ctxt->tmpRVTList = NULL;
/*
* Global fragments.
*/
- cur = ctxt->persistRVT;
+ cur = ctxt->persistRVTList;
while (cur != NULL) {
- next = (xmlDocPtr) cur->next;
- if (cur->_private != NULL) {
- xsltFreeDocumentKeys(cur->_private);
- xmlFree(cur->_private);
+ next = cur->next;
+ if (cur->RVT->_private != NULL) {
+ xsltFreeDocumentKeys(cur->RVT->_private);
+ xmlFree(cur->RVT->_private);
}
- xmlFreeDoc(cur);
+ xmlFreeDoc(cur->RVT);
+ xmlFree(cur);
cur = next;
}
- ctxt->persistRVT = NULL;
+ ctxt->persistRVTList = NULL;
}
/************************************************************************
@@ -572,21 +624,22 @@ xsltFreeStackElem(xsltStackElemPtr elem) {
* Release the list of temporary Result Tree Fragments.
*/
if (elem->context) {
- xmlDocPtr cur;
+ xsltRVTListPtr cur;
while (elem->fragment != NULL) {
cur = elem->fragment;
- elem->fragment = (xmlDocPtr) cur->next;
-
- if (cur->compression == XSLT_RVT_LOCAL) {
- xsltReleaseRVT(elem->context, cur);
- } else if (cur->compression == XSLT_RVT_FUNC_RESULT) {
- xsltRegisterLocalRVT(elem->context, cur);
- cur->compression = XSLT_RVT_FUNC_RESULT;
+ elem->fragment = cur->next;
+
+ if (cur->RVT->compression == XSLT_RVT_LOCAL) {
+ xsltReleaseRVTList(elem->context, cur);
+ } else if (cur->RVT->compression == XSLT_RVT_FUNC_RESULT) {
+ xsltRegisterLocalRVT(elem->context, cur->RVT);
+ cur->RVT->compression = XSLT_RVT_FUNC_RESULT;
+ xmlFree(cur);
} else {
xmlGenericError(xmlGenericErrorContext,
"xsltFreeStackElem: Unexpected RVT flag %d\n",
- cur->compression);
+ cur->RVT->compression);
}
}
}
@@ -958,6 +1011,7 @@ xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr variable,
} else {
if (variable->tree) {
xmlDocPtr container;
+ xsltRVTListPtr rvtList;
xmlNodePtr oldInsert;
xmlDocPtr oldOutput;
xsltStackElemPtr oldVar = ctxt->contextVariable;
@@ -980,7 +1034,11 @@ xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr variable,
* when the variable is freed, it will also free
* the Result Tree Fragment.
*/
- variable->fragment = container;
+ rvtList = xsltRVTListCreate();
+ if (rvtList == NULL)
+ goto error;
+ rvtList->RVT = container;
+ variable->fragment = rvtList;
container->compression = XSLT_RVT_LOCAL;
oldOutput = ctxt->output;
@@ -2356,5 +2414,3 @@ local_variable_found:
return(valueObj);
}
-
-
diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h
index ae9b76af..f512ed41 100644
--- a/libxslt/xsltInternals.h
+++ b/libxslt/xsltInternals.h
@@ -1421,6 +1421,8 @@ struct _xsltStylePreComp {
#endif /* XSLT_REFACTORED */
+typedef struct _xsltRVTList xsltRVTList;
+typedef xsltRVTList *xsltRVTListPtr;
/*
* The in-memory structure corresponding to an XSLT Variable
@@ -1438,7 +1440,7 @@ struct _xsltStackElem {
xmlNodePtr tree; /* the sequence constructor if no eval
string or the location */
xmlXPathObjectPtr value; /* The value if computed */
- xmlDocPtr fragment; /* The Result Tree Fragments (needed for XSLT 1.0)
+ xsltRVTListPtr fragment; /* The Result Tree Fragments (needed for XSLT 1.0)
which are bound to the variable's lifetime. */
int level; /* the depth in the tree;
-1 if persistent (e.g. a given xsl:with-param) */
@@ -1644,10 +1646,15 @@ struct _xsltStylesheet {
xmlHashTablePtr namedTemplates; /* hash table of named templates */
};
+struct _xsltRVTList {
+ xmlDocPtr RVT;
+ xsltRVTListPtr next;
+};
+
typedef struct _xsltTransformCache xsltTransformCache;
typedef xsltTransformCache *xsltTransformCachePtr;
struct _xsltTransformCache {
- xmlDocPtr RVT;
+ xsltRVTListPtr rvtList;
int nbRVT;
xsltStackElemPtr stackItems;
int nbStackItems;
@@ -1747,8 +1754,8 @@ struct _xsltTransformContext {
* handling of temporary Result Value Tree
* (XSLT 1.0 term: "Result Tree Fragment")
*/
- xmlDocPtr tmpRVT; /* list of RVT without persistance */
- xmlDocPtr persistRVT; /* list of persistant RVTs */
+ xsltRVTListPtr tmpRVTList; /* list of RVT without persistance */
+ xsltRVTListPtr persistRVTList; /* list of persistant RVTs */
int ctxtflags; /* context processing flags */
/*
@@ -1781,7 +1788,7 @@ struct _xsltTransformContext {
xmlDocPtr initialContextDoc;
xsltTransformCachePtr cache;
void *contextVariable; /* the current variable item */
- xmlDocPtr localRVT; /* list of local tree fragments; will be freed when
+ xsltRVTListPtr localRVTList; /* list of local tree fragments; will be freed when
the instruction which created the fragment
exits */
xmlDocPtr localRVTBase; /* Obsolete */
@@ -1923,8 +1930,11 @@ XSLTPUBFUN int XSLTCALL
XSLTPUBFUN void XSLTCALL
xsltFreeRVTs (xsltTransformContextPtr ctxt);
XSLTPUBFUN void XSLTCALL
- xsltReleaseRVT (xsltTransformContextPtr ctxt,
+ xsltReleaseRVT (xsltTransformContextPtr ctxt,
xmlDocPtr RVT);
+XSLTPUBFUN void XSLTCALL
+ xsltReleaseRVTList (xsltTransformContextPtr ctxt,
+ xsltRVTListPtr list);
/*
* Extra functions for Attribute Value Templates
*/
@@ -1983,4 +1993,3 @@ XSLTPUBFUN int XSLTCALL
#endif
#endif /* __XML_XSLT_H__ */
-
--
2.54.0

View File

@ -0,0 +1,130 @@
From c7c7f1f78dd202a053996fcefe57eb994aec8ef2 Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <wellnhofer@aevum.de>
Date: Tue, 17 Dec 2024 15:56:21 +0100
Subject: [PATCH] [CVE-2025-24855] Fix use-after-free of XPath context node
There are several places where the XPath context node isn't restored
after modifying it, leading to use-after-free errors with nested XPath
evaluations and dynamically allocated context nodes.
Restore XPath context node in
- xsltNumberFormatGetValue
- xsltEvalXPathPredicate
- xsltEvalXPathStringNs
- xsltComputeSortResultInternal
In some places, the transformation context node was saved and restored
which shouldn't be necessary.
Thanks to Ivan Fratric for the report!
Fixes #128.
---
libxslt/numbers.c | 5 +++++
libxslt/templates.c | 9 ++++++---
libxslt/xsltutils.c | 4 ++--
3 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/libxslt/numbers.c b/libxslt/numbers.c
index 0e1fa136..741124d1 100644
--- a/libxslt/numbers.c
+++ b/libxslt/numbers.c
@@ -733,9 +733,12 @@ xsltNumberFormatGetValue(xmlXPathContextPtr context,
int amount = 0;
xmlBufferPtr pattern;
xmlXPathObjectPtr obj;
+ xmlNodePtr oldNode;
pattern = xmlBufferCreate();
if (pattern != NULL) {
+ oldNode = context->node;
+
xmlBufferCCat(pattern, "number(");
xmlBufferCat(pattern, value);
xmlBufferCCat(pattern, ")");
@@ -748,6 +751,8 @@ xsltNumberFormatGetValue(xmlXPathContextPtr context,
xmlXPathFreeObject(obj);
}
xmlBufferFree(pattern);
+
+ context->node = oldNode;
}
return amount;
}
diff --git a/libxslt/templates.c b/libxslt/templates.c
index f08b9bda..1c8d96e2 100644
--- a/libxslt/templates.c
+++ b/libxslt/templates.c
@@ -61,6 +61,7 @@ xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
int oldNsNr;
xmlNsPtr *oldNamespaces;
xmlNodePtr oldInst;
+ xmlNodePtr oldNode;
int oldProximityPosition, oldContextSize;
if ((ctxt == NULL) || (ctxt->inst == NULL)) {
@@ -69,6 +70,7 @@ xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
return(0);
}
+ oldNode = ctxt->xpathCtxt->node;
oldContextSize = ctxt->xpathCtxt->contextSize;
oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
oldNsNr = ctxt->xpathCtxt->nsNr;
@@ -96,8 +98,9 @@ xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
ctxt->state = XSLT_STATE_STOPPED;
ret = 0;
}
- ctxt->xpathCtxt->nsNr = oldNsNr;
+ ctxt->xpathCtxt->node = oldNode;
+ ctxt->xpathCtxt->nsNr = oldNsNr;
ctxt->xpathCtxt->namespaces = oldNamespaces;
ctxt->inst = oldInst;
ctxt->xpathCtxt->contextSize = oldContextSize;
@@ -137,7 +140,7 @@ xsltEvalXPathStringNs(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
}
oldInst = ctxt->inst;
- oldNode = ctxt->node;
+ oldNode = ctxt->xpathCtxt->node;
oldPos = ctxt->xpathCtxt->proximityPosition;
oldSize = ctxt->xpathCtxt->contextSize;
oldNsNr = ctxt->xpathCtxt->nsNr;
@@ -167,7 +170,7 @@ xsltEvalXPathStringNs(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
"xsltEvalXPathString: returns %s\n", ret));
#endif
ctxt->inst = oldInst;
- ctxt->node = oldNode;
+ ctxt->xpathCtxt->node = oldNode;
ctxt->xpathCtxt->contextSize = oldSize;
ctxt->xpathCtxt->proximityPosition = oldPos;
ctxt->xpathCtxt->nsNr = oldNsNr;
diff --git a/libxslt/xsltutils.c b/libxslt/xsltutils.c
index 0e9dc62f..a20da961 100644
--- a/libxslt/xsltutils.c
+++ b/libxslt/xsltutils.c
@@ -1065,8 +1065,8 @@ xsltComputeSortResultInternal(xsltTransformContextPtr ctxt, xmlNodePtr sort,
return(NULL);
}
- oldNode = ctxt->node;
oldInst = ctxt->inst;
+ oldNode = ctxt->xpathCtxt->node;
oldPos = ctxt->xpathCtxt->proximityPosition;
oldSize = ctxt->xpathCtxt->contextSize;
oldNsNr = ctxt->xpathCtxt->nsNr;
@@ -1137,8 +1137,8 @@ xsltComputeSortResultInternal(xsltTransformContextPtr ctxt, xmlNodePtr sort,
results[i] = NULL;
}
}
- ctxt->node = oldNode;
ctxt->inst = oldInst;
+ ctxt->xpathCtxt->node = oldNode;
ctxt->xpathCtxt->contextSize = oldSize;
ctxt->xpathCtxt->proximityPosition = oldPos;
ctxt->xpathCtxt->nsNr = oldNsNr;
--
GitLab

View File

@ -8,7 +8,7 @@
Name: libxslt
Summary: Library providing the Gnome XSLT engine
Version: 1.1.32
Release: 6%{?dist}
Release: 6.4%{?dist}
License: MIT
URL: http://xmlsoft.org/XSLT
@ -33,6 +33,14 @@ Patch3: libxslt-1.1.32-CVE-2019-18197.patch
Patch4: libxslt-1.1.32-CVE-2019-11068.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=1860467
Patch5: libxslt-1.1.32-unexpected-rvt-flag.patch
# https://issues.redhat.com/browse/RHEL-83506
Patch6: libxslt-1.1.32-CVE-2024-55549.patch
# https://issues.redhat.com/browse/RHEL-83492
Patch7: libxslt-1.1.32-CVE-2025-24855.patch
# https://issues.redhat.com/browse/RHEL-89374
Patch8: libxslt-1.1.32-CVE-2023-40403.patch
# https://redhat.atlassian.net/browse/RHEL-171739
Patch9: libxslt-1.1.32-CVE-2025-10911.patch
%description
This C library allows to transform XML files into other XML files
@ -137,6 +145,19 @@ rm -vrf %{buildroot}%{_docdir}
%endif # with python2
%changelog
* Tue May 19 2026 David King <dking@redhat.com> - 1.1.32-6.4
- Fix CVE-2025-10911 (RHEL-171739)
* Thu Aug 07 2025 David King <dking@redhat.com> - 1.1.32-6.3
- Fix misplaced endif (RHEL-107912)
* Tue Jun 03 2025 David King <dking@redhat.com> - 1.1.32-6.2
- Fix CVE-2023-40403 (aka 2022-4909) (RHEL-89374)
* Fri Apr 04 2025 David King <dking@redhat.com> - 1.1.32-6.1
- Fix CVE-2024-55549 (RHEL-83506)
- Fix CVE-2025-24855 (RHEL-83492)
* Mon Aug 24 2020 David King <dking@redhat.com> - 1.1.32-6
- Fix unexpected RVT flag error (#1860467)