From 1e339292bc2809fb474575ee8307f5ce09fd3e13 Mon Sep 17 00:00:00 2001 From: fly_fzc <2385803914@qq.com> Date: Mon, 29 Sep 2025 14:10:00 +0800 Subject: [PATCH] fix CVE-2025-10911 --- backport-CVE-2025-10911.patch | 704 ++++++++++++++++++++++++++++++++++ libxslt.spec | 6 +- 2 files changed, 709 insertions(+), 1 deletion(-) create mode 100644 backport-CVE-2025-10911.patch diff --git a/backport-CVE-2025-10911.patch b/backport-CVE-2025-10911.patch new file mode 100644 index 0000000..80530f8 --- /dev/null +++ b/backport-CVE-2025-10911.patch @@ -0,0 +1,704 @@ +From f94e7e9796edeb6f3bedd3fdb1099e9b556aea21 Mon Sep 17 00:00:00 2001 +From: Daniel Cheng +Date: Sat, 31 May 2025 00:15:24 -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. +--- + libxslt/transform.c | 83 +++++++------- + libxslt/variables.c | 232 ++++++++++++++++++++++++++-------------- + libxslt/xsltInternals.h | 24 +++-- + 3 files changed, 213 insertions(+), 126 deletions(-) + +diff --git a/libxslt/transform.c b/libxslt/transform.c +index 54ef821..bdff66d 100644 +--- a/libxslt/transform.c ++++ b/libxslt/transform.c +@@ -518,19 +518,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); + } + } + /* +@@ -2263,38 +2264,36 @@ 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; ++ xsltRVTListPtr cur = ctxt->localRVTList, tmp; + + if (cur == base) + return; +- if (cur->prev != NULL) +- xsltTransformError(ctxt, NULL, NULL, "localRVT not head of list\n"); + +- /* Reset localRVT early because some RVTs might be registered again. */ +- ctxt->localRVT = base; +- if (base != NULL) +- base->prev = NULL; ++ /* 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->psvi); ++ "xsltReleaseLocalRVTs: Unexpected RVT flag %d\n", ++ tmp->RVT->compression); + } + } while (cur != base); + } +@@ -2322,7 +2325,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; +@@ -2368,7 +2371,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, + } + ctxt->depth++; + +- oldLocalFragmentTop = ctxt->localRVT; ++ oldLocalFragmentTop = ctxt->localRVTList; + oldInsert = insert = ctxt->insert; + oldInst = oldCurInst = ctxt->inst; + oldContextNode = ctxt->node; +@@ -2602,7 +2605,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, + /* + * Cleanup temporary tree fragments. + */ +- if (oldLocalFragmentTop != ctxt->localRVT) ++ if (oldLocalFragmentTop != ctxt->localRVTList) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + + ctxt->insert = oldInsert; +@@ -2697,7 +2700,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, + /* + * Cleanup temporary tree fragments. + */ +- if (oldLocalFragmentTop != ctxt->localRVT) ++ if (oldLocalFragmentTop != ctxt->localRVTList) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + + ctxt->insert = oldInsert; +@@ -2763,7 +2766,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, + /* + * Cleanup temporary tree fragments. + */ +- if (oldLocalFragmentTop != ctxt->localRVT) ++ if (oldLocalFragmentTop != ctxt->localRVTList) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + + ctxt->insert = oldInsert; +@@ -2893,7 +2896,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, + /* + * Cleanup temporary tree fragments. + */ +- if (oldLocalFragmentTop != ctxt->localRVT) ++ if (oldLocalFragmentTop != ctxt->localRVTList) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + + ctxt->insert = oldInsert; +@@ -3072,7 +3075,7 @@ xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt, + int oldVarsBase = 0; + xmlNodePtr cur; + xsltStackElemPtr tmpParam = NULL; +- xmlDocPtr oldUserFragmentTop; ++ xsltRVTListPtr oldUserFragmentTop; + #ifdef WITH_PROFILER + long start = 0; + #endif +@@ -3120,8 +3123,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. +@@ -3232,16 +3235,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. +@@ -5319,7 +5322,7 @@ xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, + + #ifdef XSLT_FAST_IF + { +- xmlDocPtr oldLocalFragmentTop = ctxt->localRVT; ++ xsltRVTListPtr oldLocalFragmentTop = ctxt->localRVTList; + + res = xsltPreCompEvalToBoolean(ctxt, contextNode, comp); + +@@ -5327,7 +5330,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 eb98aab..cbe7198 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 +76,7 @@ static const xmlChar *xsltComputingGlobalVarMarker = + xmlDocPtr + xsltCreateRVT(xsltTransformContextPtr ctxt) + { ++ xsltRVTListPtr rvtList; + xmlDocPtr container; + + /* +@@ -76,12 +89,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 +131,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 +149,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 +198,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 +207,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); + } + +@@ -344,8 +368,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) +@@ -353,36 +378,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. + */ +- if (RVT->children != NULL) { +- xmlFreeNodeList(RVT->children); +- RVT->children = NULL; +- RVT->last = NULL; +- } +- if (RVT->ids != NULL) { +- xmlFreeIDTable((xmlIDTablePtr) RVT->ids); +- RVT->ids = NULL; ++ if (list->RVT->children != NULL) { ++ xmlFreeNodeList(list->RVT->children); ++ list->RVT->children = NULL; ++ list->RVT->last = 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++; + +@@ -394,11 +449,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); + } + + /** +@@ -416,14 +472,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); + } + +@@ -438,52 +500,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; + } + + /************************************************************************ +@@ -571,21 +636,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; ++ elem->fragment = 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; ++ 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); + } + } + } +@@ -944,6 +1010,7 @@ xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr variable, + } else { + if (variable->tree) { + xmlDocPtr container; ++ xsltRVTListPtr rvtList; + xmlNodePtr oldInsert; + xmlDocPtr oldOutput; + const xmlChar *oldLastText; +@@ -968,7 +1035,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; +@@ -2361,5 +2432,3 @@ local_variable_found: + + return(valueObj); + } +- +- +diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h +index 6faa07d..39d17ac 100644 +--- a/libxslt/xsltInternals.h ++++ b/libxslt/xsltInternals.h +@@ -1410,6 +1410,8 @@ struct _xsltStylePreComp { + + #endif /* XSLT_REFACTORED */ + ++typedef struct _xsltRVTList xsltRVTList; ++typedef xsltRVTList *xsltRVTListPtr; + + /* + * The in-memory structure corresponding to an XSLT Variable +@@ -1427,7 +1429,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) */ +@@ -1639,10 +1641,15 @@ struct _xsltStylesheet { + unsigned long opCount; + }; + ++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; +@@ -1749,8 +1757,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 */ + + /* +@@ -1783,7 +1791,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 */ +@@ -1932,8 +1932,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 + */ +@@ -1992,4 +2003,3 @@ XSLTPUBFUN int XSLTCALL + #endif + + #endif /* __XML_XSLT_H__ */ +- +-- +2.33.0 + diff --git a/libxslt.spec b/libxslt.spec index 3e0070f..0c692c2 100644 --- a/libxslt.spec +++ b/libxslt.spec @@ -1,6 +1,6 @@ Name: libxslt Version: 1.1.43 -Release: 3 +Release: 4 Summary: XSLT Transformation Library License: MIT URL: https://gitlab.gnome.org/GNOME/libxslt @@ -8,6 +8,7 @@ Source0: https://download.gnome.org/sources/%{name}/1.1/%{name}-%{version}.tar. # PATCH-FIX-UPSTREAM bug-fix https://github.com/GNOME/libxslt/ Patch0: CVE-2015-9019.patch Patch1: backport-CVE-2025-7424.patch +Patch2: backport-CVE-2025-10911.patch Patch9001:huawei-add-autogen.sh-for-fuzz-compile.patch @@ -96,6 +97,9 @@ pushd $RPM_BUILD_ROOT/%{_includedir}/%{name}; touch -m --reference=xslt.h ../../ %{_mandir}/man3/* %changelog +* Thu Oct 9 2025 fuanan - 1.1.43-4 +- fix CVE-2025-10911 + * Tue Jul 29 2025 fuanan - 1.1.43-3 - fix CVE-2025-7424 -- Gitee