OK aja

On Fri, Aug 29, 2025 at 04:56:09PM +0200, Theo Buehler wrote:
> There's been a bit of noise about this recently so I looked and noticed
> we have not yet pulled in the available patches. It doesn't sound like a
> big deal since it appears untrusted style sheets are involved. Still,
> it seems like something we want to patch.
> 
> https://vuxml.freebsd.org/freebsd/b0a3466f-5efc-11f0-ae84-99047d0a6bcc.html
> 
> lists four issues, three of which are public and have patches from apple
> and google.
> 
> Two of them are for libxslt and apply cleanly. Those are included below:
> 
> https://gitlab.gnome.org/GNOME/libxslt/-/issues/139
> https://gitlab.gnome.org/GNOME/libxslt/-/issues/144
> 
> Regress still passes.
> 
> The third one is quite confusing since part of it seems to be about
> libxml2 and some of the fixes implicate binary compatibility. The apple
> patch doesn't apply cleanly to our port:
> 
> https://gitlab.gnome.org/GNOME/libxslt/-/issues/140
> 
> I leave that one to the maintainer to sort out.
> 
> Index: Makefile
> ===================================================================
> RCS file: /cvs/ports/textproc/libxslt/Makefile,v
> diff -u -p -r1.110 Makefile
> --- Makefile  15 Mar 2025 09:34:42 -0000      1.110
> +++ Makefile  29 Aug 2025 13:17:48 -0000
> @@ -2,7 +2,7 @@ COMMENT=              XSLT C Library for GNOME
>  
>  GNOME_VERSION=               1.1.43
>  GNOME_PROJECT=               libxslt
> -REVISION=            0
> +REVISION=            1
>  
>  SHARED_LIBS +=  xslt                 4.2      # 2.43
>  SHARED_LIBS +=  exslt                9.8      # 8.24
> Index: patches/patch-libxslt_functions_c
> ===================================================================
> RCS file: patches/patch-libxslt_functions_c
> diff -N patches/patch-libxslt_functions_c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ patches/patch-libxslt_functions_c 29 Aug 2025 13:17:48 -0000
> @@ -0,0 +1,55 @@
> +https://gitlab.gnome.org/GNOME/libxslt/-/issues/139
> +
> +From 345d6826d0eae6f0a962456b8ed6f6a1bad0877d Mon Sep 17 00:00:00 2001
> +From: David Kilzer <[email protected]>
> +Date: Sat, 24 May 2025 15:06:42 -0700
> +Subject: [PATCH] libxslt: Type confusion in xmlNode.psvi between stylesheet
> + and source nodes
> +
> +* libxslt/functions.c:
> +(xsltDocumentFunctionLoadDocument):
> +- Implement fix suggested by Ivan Fratric.  This copies the xmlDoc,
> +  calls xsltCleanupSourceDoc() to remove pvsi fields, then adds the
> +  xmlDoc to tctxt->docList.
> +- Add error handling for functions that may return NULL.
> +* libxslt/transform.c:
> +- Remove static keyword so this can be called from
> +  xsltDocumentFunctionLoadDocument().
> +* libxslt/transformInternals.h: Add.
> +(xsltCleanupSourceDoc): Add declaration.
> +
> +Fixes #139.
> +
> +Index: libxslt/functions.c
> +--- libxslt/functions.c.orig
> ++++ libxslt/functions.c
> +@@ -34,6 +34,7 @@
> + #include "numbersInternals.h"
> + #include "keys.h"
> + #include "documents.h"
> ++#include "transformInternals.h"
> + 
> + #ifdef WITH_XSLT_DEBUG
> + #define WITH_XSLT_DEBUG_FUNCTION
> +@@ -125,7 +126,20 @@ xsltDocumentFunctionLoadDocument(xmlXPathParserContext
> +         /*
> +         * This selects the stylesheet's doc itself.
> +         */
> +-        doc = tctxt->style->doc;
> ++        doc = xmlCopyDoc(tctxt->style->doc, 1);
> ++        if (doc == NULL) {
> ++            xsltTransformError(tctxt, NULL, NULL,
> ++                "document() : failed to copy style doc\n");
> ++            goto out_fragment;
> ++        }
> ++        xsltCleanupSourceDoc(doc); /* Remove psvi fields. */
> ++        idoc = xsltNewDocument(tctxt, doc);
> ++        if (idoc == NULL) {
> ++            xsltTransformError(tctxt, NULL, NULL,
> ++                "document() : failed to create xsltDocument\n");
> ++            xmlFreeDoc(doc);
> ++            goto out_fragment;
> ++        }
> +     } else {
> +             goto out_fragment;
> +     }
> Index: patches/patch-libxslt_transformInternals_h
> ===================================================================
> RCS file: patches/patch-libxslt_transformInternals_h
> diff -N patches/patch-libxslt_transformInternals_h
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ patches/patch-libxslt_transformInternals_h        29 Aug 2025 13:17:48 
> -0000
> @@ -0,0 +1,35 @@
> +https://gitlab.gnome.org/GNOME/libxslt/-/issues/139
> +
> +From 345d6826d0eae6f0a962456b8ed6f6a1bad0877d Mon Sep 17 00:00:00 2001
> +From: David Kilzer <[email protected]>
> +Date: Sat, 24 May 2025 15:06:42 -0700
> +Subject: [PATCH] libxslt: Type confusion in xmlNode.psvi between stylesheet
> + and source nodes
> +
> +* libxslt/functions.c:
> +(xsltDocumentFunctionLoadDocument):
> +- Implement fix suggested by Ivan Fratric.  This copies the xmlDoc,
> +  calls xsltCleanupSourceDoc() to remove pvsi fields, then adds the
> +  xmlDoc to tctxt->docList.
> +- Add error handling for functions that may return NULL.
> +* libxslt/transform.c:
> +- Remove static keyword so this can be called from
> +  xsltDocumentFunctionLoadDocument().
> +* libxslt/transformInternals.h: Add.
> +(xsltCleanupSourceDoc): Add declaration.
> +
> +Fixes #139.
> +
> +Index: libxslt/transformInternals.h
> +--- libxslt/transformInternals.h.orig
> ++++ libxslt/transformInternals.h
> +@@ -0,0 +1,9 @@
> ++/*
> ++ * Summary: set of internal interfaces for the XSLT engine transformation 
> part.
> ++ *
> ++ * Copy: See Copyright for the status of this software.
> ++ *
> ++ * Author: David Kilzer <[email protected]>
> ++ */
> ++
> ++void xsltCleanupSourceDoc(xmlDocPtr doc);
> Index: patches/patch-libxslt_transform_c
> ===================================================================
> RCS file: patches/patch-libxslt_transform_c
> diff -N patches/patch-libxslt_transform_c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ patches/patch-libxslt_transform_c 29 Aug 2025 13:17:48 -0000
> @@ -0,0 +1,258 @@
> +https://gitlab.gnome.org/GNOME/libxslt/-/issues/139
> +https://gitlab.gnome.org/GNOME/libxslt/-/issues/144
> +
> +From 345d6826d0eae6f0a962456b8ed6f6a1bad0877d Mon Sep 17 00:00:00 2001
> +From: David Kilzer <[email protected]>
> +Date: Sat, 24 May 2025 15:06:42 -0700
> +Subject: [PATCH] libxslt: Type confusion in xmlNode.psvi between stylesheet
> + and source nodes
> +
> +* libxslt/functions.c:
> +(xsltDocumentFunctionLoadDocument):
> +- Implement fix suggested by Ivan Fratric.  This copies the xmlDoc,
> +  calls xsltCleanupSourceDoc() to remove pvsi fields, then adds the
> +  xmlDoc to tctxt->docList.
> +- Add error handling for functions that may return NULL.
> +* libxslt/transform.c:
> +- Remove static keyword so this can be called from
> +  xsltDocumentFunctionLoadDocument().
> +* libxslt/transformInternals.h: Add.
> +(xsltCleanupSourceDoc): Add declaration.
> +
> +Fixes #139.
> +
> +From f94e7e9796edeb6f3bedd3fdb1099e9b556aea21 Mon Sep 17 00:00:00 2001
> +From: Daniel Cheng <[email protected]>
> +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.
> +
> +Index: libxslt/transform.c
> +--- libxslt/transform.c.orig
> ++++ libxslt/transform.c
> +@@ -43,6 +43,7 @@
> + #include "xsltlocale.h"
> + #include "pattern.h"
> + #include "transform.h"
> ++#include "transformInternals.h"
> + #include "variables.h"
> + #include "numbersInternals.h"
> + #include "namespaces.h"
> +@@ -518,19 +519,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 +2265,40 @@ 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;
> ++    /* Reset localRVTList early because some RVTs might be registered 
> again. */
> ++    ctxt->localRVTList = base;
> +     if (base != NULL)
> +         base->prev = NULL;
> + 
> +     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 +2326,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr c
> +     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 +2372,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr c
> +     }
> +     ctxt->depth++;
> + 
> +-    oldLocalFragmentTop = ctxt->localRVT;
> ++    oldLocalFragmentTop = ctxt->localRVTList;
> +     oldInsert = insert = ctxt->insert;
> +     oldInst = oldCurInst = ctxt->inst;
> +     oldContextNode = ctxt->node;
> +@@ -2602,7 +2606,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr c
> +                 /*
> +                 * Cleanup temporary tree fragments.
> +                 */
> +-                if (oldLocalFragmentTop != ctxt->localRVT)
> ++                if (oldLocalFragmentTop != ctxt->localRVTList)
> +                     xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
> + 
> +                 ctxt->insert = oldInsert;
> +@@ -2697,7 +2701,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr c
> +                 /*
> +                 * Cleanup temporary tree fragments.
> +                 */
> +-                if (oldLocalFragmentTop != ctxt->localRVT)
> ++                if (oldLocalFragmentTop != ctxt->localRVTList)
> +                     xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
> + 
> +                 ctxt->insert = oldInsert;
> +@@ -2763,7 +2767,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr c
> +             /*
> +             * Cleanup temporary tree fragments.
> +             */
> +-            if (oldLocalFragmentTop != ctxt->localRVT)
> ++            if (oldLocalFragmentTop != ctxt->localRVTList)
> +                 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
> + 
> +                 ctxt->insert = oldInsert;
> +@@ -2893,7 +2897,7 @@ xsltApplySequenceConstructor(xsltTransformContextPtr c
> +             /*
> +             * Cleanup temporary tree fragments.
> +             */
> +-            if (oldLocalFragmentTop != ctxt->localRVT)
> ++            if (oldLocalFragmentTop != ctxt->localRVTList)
> +                 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
> + 
> +                 ctxt->insert = oldInsert;
> +@@ -3072,7 +3076,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 +3124,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 +3236,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 +5323,7 @@ xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contex
> + 
> + #ifdef XSLT_FAST_IF
> +     {
> +-    xmlDocPtr oldLocalFragmentTop = ctxt->localRVT;
> ++    xsltRVTListPtr oldLocalFragmentTop = ctxt->localRVTList;
> + 
> +     res = xsltPreCompEvalToBoolean(ctxt, contextNode, comp);
> + 
> +@@ -5327,7 +5331,7 @@ xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contex
> +     * Cleanup fragments created during evaluation of the
> +     * "select" expression.
> +     */
> +-    if (oldLocalFragmentTop != ctxt->localRVT)
> ++    if (oldLocalFragmentTop != ctxt->localRVTList)
> +         xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
> +     }
> + 
> +@@ -5757,7 +5761,7 @@ xsltCountKeys(xsltTransformContextPtr ctxt)
> +  *
> +  * Resets source node flags and ids stored in 'psvi' member.
> +  */
> +-static void
> ++void
> + xsltCleanupSourceDoc(xmlDocPtr doc) {
> +     xmlNodePtr cur = (xmlNodePtr) doc;
> +     void **psviPtr;
> Index: patches/patch-libxslt_variables_c
> ===================================================================
> RCS file: patches/patch-libxslt_variables_c
> diff -N patches/patch-libxslt_variables_c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ patches/patch-libxslt_variables_c 29 Aug 2025 13:17:49 -0000
> @@ -0,0 +1,428 @@
> +https://gitlab.gnome.org/GNOME/libxslt/-/issues/144
> +
> +From f94e7e9796edeb6f3bedd3fdb1099e9b556aea21 Mon Sep 17 00:00:00 2001
> +From: Daniel Cheng <[email protected]>
> +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.
> +
> +Index: libxslt/variables.c
> +--- libxslt/variables.c.orig
> ++++ 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,17 @@ 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->prev = NULL;
> ++    list->RVT = RVT;
> + 
> +     /*
> +     * We'll restrict the lifetime of user-created fragments
> +@@ -131,15 +152,15 @@ xsltRegisterTmpRVT(xsltTransformContextPtr ctxt, xmlDo
> +     * 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;
> ++    if (ctxt->tmpRVTList != NULL)
> ++    ctxt->tmpRVTList->prev = list;
> ++    ctxt->tmpRVTList = list;
> +     return(0);
> + }
> + 
> +@@ -159,11 +180,17 @@ 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->prev = NULL;
> ++    list->RVT = RVT;
> + 
> +     /*
> +     * When evaluating "select" expressions of xsl:variable
> +@@ -174,8 +201,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 +210,10 @@ 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;
> ++    if (ctxt->localRVTList != NULL)
> ++    ctxt->localRVTList->prev = list;
> ++    ctxt->localRVTList = list;
> +     return(0);
> + }
> + 
> +@@ -344,8 +371,9 @@ xsltFlagRVTs(xsltTransformContextPtr ctxt, xmlXPathObj
> +  * @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 +381,68 @@ xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr
> +     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 (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 (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;
> ++        /*
> ++         * prev is not set nor used in the cache, since elements are only
> ++         * pushed/popped from the front of the list.
> ++         */
> ++    list->next = ctxt->cache->rvtList;
> ++    ctxt->cache->rvtList = list;
> + 
> +     ctxt->cache->nbRVT++;
> + 
> +@@ -394,11 +454,12 @@ xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr
> +     /*
> +     * 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 +477,20 @@ xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr
> + 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->prev = NULL;
> ++    list->next = ctxt->persistRVTList;
> ++    if (ctxt->persistRVTList != NULL)
> ++    ctxt->persistRVTList->prev = list;
> ++    ctxt->persistRVTList = list;
> +     return(0);
> + }
> + 
> +@@ -438,52 +505,55 @@ xsltRegisterPersistRVT(xsltTransformContextPtr ctxt, x
> + 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 +641,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 +1015,7 @@ xsltEvalVariable(xsltTransformContextPtr ctxt, xsltSta
> +     } else {
> +         if (variable->tree) {
> +             xmlDocPtr container;
> ++                xsltRVTListPtr rvtList;
> +             xmlNodePtr oldInsert;
> +             xmlDocPtr  oldOutput;
> +                 const xmlChar *oldLastText;
> +@@ -968,7 +1040,11 @@ xsltEvalVariable(xsltTransformContextPtr ctxt, xsltSta
> +             * 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 +2437,3 @@ local_variable_found:
> + 
> +     return(valueObj);
> + }
> +-
> +-
> Index: patches/patch-libxslt_xsltInternals_h
> ===================================================================
> RCS file: patches/patch-libxslt_xsltInternals_h
> diff -N patches/patch-libxslt_xsltInternals_h
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ patches/patch-libxslt_xsltInternals_h     29 Aug 2025 13:17:49 -0000
> @@ -0,0 +1,94 @@
> +https://gitlab.gnome.org/GNOME/libxslt/-/issues/144
> +
> +From f94e7e9796edeb6f3bedd3fdb1099e9b556aea21 Mon Sep 17 00:00:00 2001
> +From: Daniel Cheng <[email protected]>
> +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.
> +
> +Index: libxslt/xsltInternals.h
> +--- libxslt/xsltInternals.h.orig
> ++++ 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,16 @@ struct _xsltStylesheet {
> +     unsigned long opCount;
> + };
> + 
> ++struct _xsltRVTList {
> ++  xmlDocPtr RVT;
> ++  xsltRVTListPtr prev;
> ++  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 +1940,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__ */
> +-

-- 
Antoine


Reply via email to