Version in base suite: 1.1.35-1.2+deb13u2 Base version: libxslt_1.1.35-1.2+deb13u2 Target version: libxslt_1.1.35-1.2+deb13u3 Base file: /srv/ftp-master.debian.org/ftp/pool/main/libx/libxslt/libxslt_1.1.35-1.2+deb13u2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/libx/libxslt/libxslt_1.1.35-1.2+deb13u3.dsc changelog | 9 patches/0021-generate-id-store-ids-out-of-band.patch | 281 +++++++++++++++++++ patches/series | 1 3 files changed, 291 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmph468f7a6/libxslt_1.1.35-1.2+deb13u2.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmph468f7a6/libxslt_1.1.35-1.2+deb13u3.dsc: no acceptable signature found diff -Nru libxslt-1.1.35/debian/changelog libxslt-1.1.35/debian/changelog --- libxslt-1.1.35/debian/changelog 2025-09-22 07:36:44.000000000 +0000 +++ libxslt-1.1.35/debian/changelog 2026-04-10 06:56:30.000000000 +0000 @@ -1,3 +1,12 @@ +libxslt (1.1.35-1.2+deb13u3) trixie; urgency=medium + + * Non-maintainer upload. + * Fix deterministic generate-id() regression introduced in previous fix by + moving the stable ID generation into a per-transform, per-document hash + table. (Closes: #1114928) + + -- Miao Wang Fri, 10 Apr 2026 06:56:30 +0000 + libxslt (1.1.35-1.2+deb13u2) trixie-security; urgency=high * Non-maintainer upload. diff -Nru libxslt-1.1.35/debian/patches/0021-generate-id-store-ids-out-of-band.patch libxslt-1.1.35/debian/patches/0021-generate-id-store-ids-out-of-band.patch --- libxslt-1.1.35/debian/patches/0021-generate-id-store-ids-out-of-band.patch 1970-01-01 00:00:00.000000000 +0000 +++ libxslt-1.1.35/debian/patches/0021-generate-id-store-ids-out-of-band.patch 2026-04-10 06:56:30.000000000 +0000 @@ -0,0 +1,281 @@ +From: Aron Xu +Date: Thu, 1 Apr 2026 20:31:54 +0800 +Subject: [PATCH] generate-id: store deterministic IDs out-of-band +Forwarded: not-needed + +The deterministic generate-id() backport stores IDs in xmlNode.psvi. +That collides with pre-existing psvi users in libxslt and libxml2, +including result tree fragment bookkeeping, and triggers runtime +failures such as: generate-id(): psvi already set + +Move generated IDs into a per-transform, per-document hash table +instead. Purge per-document tables when documents or RVTs are freed +or returned to the RVT cache so stale node addresses cannot be reused. + +This keeps deterministic generate-id() without hijacking psvi. + +Index: libxslt-1.1.35/libxslt/xsltInternals.h +=================================================================== +--- libxslt-1.1.35.orig/libxslt/xsltInternals.h ++++ libxslt-1.1.35/libxslt/xsltInternals.h +@@ -1788,7 +1788,11 @@ + unsigned long opCount; + int sourceDocDirty; + unsigned long currentId; /* For generate-id() */ +-}; ++ xmlHashTablePtr generatedIds; /* per-doc node->ID tables */ ++}; ++ ++void xsltFreeGeneratedIds(xsltTransformContextPtr ctxt); ++void xsltFreeGeneratedIdsForDoc(xsltTransformContextPtr ctxt, xmlDocPtr doc); + + /** + * CHECK_STOPPED: +Index: libxslt-1.1.35/libxslt/functions.c +=================================================================== +--- libxslt-1.1.35.orig/libxslt/functions.c ++++ libxslt-1.1.35/libxslt/functions.c +@@ -53,6 +53,73 @@ + * in the list of decent XSLT processors + */ + #define DOCBOOK_XSL_HACK ++ ++#define XSLT_GENID_KEYSIZE (2 + sizeof(void *) * 2 + 1) ++ ++static void ++xsltGenIdBuildKey(const void *ptr, xmlChar *buf) ++{ ++ snprintf((char *) buf, XSLT_GENID_KEYSIZE, "%p", ptr); ++} ++ ++static void ++xsltFreeGeneratedIdsDocEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) ++{ ++ xmlHashFree((xmlHashTablePtr) payload, NULL); ++} ++ ++static xmlHashTablePtr ++xsltGetGeneratedIdsForDoc(xsltTransformContextPtr ctxt, xmlDocPtr doc, int create) ++{ ++ xmlHashTablePtr table; ++ xmlChar key[XSLT_GENID_KEYSIZE]; ++ ++ if ((ctxt == NULL) || (doc == NULL)) ++ return(NULL); ++ ++ if (ctxt->generatedIds == NULL) { ++ if (!create) ++ return(NULL); ++ ctxt->generatedIds = xmlHashCreate(8); ++ if (ctxt->generatedIds == NULL) ++ return(NULL); ++ } ++ ++ xsltGenIdBuildKey(doc, key); ++ table = (xmlHashTablePtr) xmlHashLookup(ctxt->generatedIds, key); ++ if ((table == NULL) && create) { ++ table = xmlHashCreate(64); ++ if (table == NULL) ++ return(NULL); ++ if (xmlHashAddEntry(ctxt->generatedIds, key, table) < 0) { ++ xmlHashFree(table, NULL); ++ return(NULL); ++ } ++ } ++ ++ return(table); ++} ++ ++void ++xsltFreeGeneratedIdsForDoc(xsltTransformContextPtr ctxt, xmlDocPtr doc) ++{ ++ xmlChar key[XSLT_GENID_KEYSIZE]; ++ ++ if ((ctxt == NULL) || (ctxt->generatedIds == NULL) || (doc == NULL)) ++ return; ++ ++ xsltGenIdBuildKey(doc, key); ++ xmlHashRemoveEntry(ctxt->generatedIds, key, xsltFreeGeneratedIdsDocEntry); ++} ++ ++void ++xsltFreeGeneratedIds(xsltTransformContextPtr ctxt) ++{ ++ if ((ctxt != NULL) && (ctxt->generatedIds != NULL)) { ++ xmlHashFree(ctxt->generatedIds, xsltFreeGeneratedIdsDocEntry); ++ ctxt->generatedIds = NULL; ++ } ++} + + /** + * xsltXPathFunctionLookup: +@@ -710,11 +777,14 @@ + xsltTransformContextPtr tctxt; + xmlNodePtr cur = NULL; + xmlXPathObjectPtr obj = NULL; ++ xmlDocPtr doc = NULL; ++ xmlHashTablePtr docIds = NULL; ++ void *payload; + char *str; + const xmlChar *nsPrefix = NULL; +- void **psviPtr; + unsigned long id; + size_t size, nsPrefixSize; ++ xmlChar key[XSLT_GENID_KEYSIZE]; + + tctxt = xsltXPathGetTransformContext(ctxt); + +@@ -765,27 +835,45 @@ + cur = (xmlNodePtr) ns->next; + } + +- psviPtr = xsltGetPSVIPtr(cur); +- if (psviPtr == NULL) { ++ switch (cur->type) { ++ case XML_DOCUMENT_NODE: ++ case XML_HTML_DOCUMENT_NODE: ++ doc = (xmlDocPtr) cur; ++ break; ++ case XML_ATTRIBUTE_NODE: ++ doc = ((xmlAttrPtr) cur)->doc; ++ break; ++ case XML_ELEMENT_NODE: ++ case XML_TEXT_NODE: ++ case XML_CDATA_SECTION_NODE: ++ case XML_PI_NODE: ++ case XML_COMMENT_NODE: ++ doc = cur->doc; ++ break; ++ default: ++ doc = NULL; ++ } ++ ++ if (doc == NULL) { + xsltTransformError(tctxt, NULL, NULL, + "generate-id(): invalid node type %d\n", cur->type); + ctxt->error = XPATH_INVALID_TYPE; + goto out; + } + +- if (xsltGetSourceNodeFlags(cur) & XSLT_SOURCE_NODE_HAS_ID) { +- id = (unsigned long) *psviPtr; ++ docIds = xsltGetGeneratedIdsForDoc(tctxt, doc, 1); ++ if (docIds == NULL) { ++ xsltTransformError(tctxt, NULL, NULL, ++ "generate-id(): out of memory\n"); ++ ctxt->error = XPATH_MEMORY_ERROR; ++ goto out; ++ } ++ ++ xsltGenIdBuildKey(cur, key); ++ payload = xmlHashLookup(docIds, key); ++ if (payload != NULL) { ++ id = (unsigned long) (size_t) payload; + } else { +- if (cur->type == XML_TEXT_NODE && cur->line == USHRT_MAX) { +- /* Text nodes store big line numbers in psvi. */ +- cur->line = 0; +- } else if (*psviPtr != NULL) { +- xsltTransformError(tctxt, NULL, NULL, +- "generate-id(): psvi already set\n"); +- ctxt->error = XPATH_MEMORY_ERROR; +- goto out; +- } +- + if (tctxt->currentId == ULONG_MAX) { + xsltTransformError(tctxt, NULL, NULL, + "generate-id(): id overflow\n"); +@@ -794,8 +882,12 @@ + } + + id = ++tctxt->currentId; +- *psviPtr = (void *) id; +- xsltSetSourceNodeFlags(tctxt, cur, XSLT_SOURCE_NODE_HAS_ID); ++ if (xmlHashAddEntry(docIds, key, (void *) (size_t) id) < 0) { ++ xsltTransformError(tctxt, NULL, NULL, ++ "generate-id(): out of memory\n"); ++ ctxt->error = XPATH_MEMORY_ERROR; ++ goto out; ++ } + } + + str = xmlMalloc(size); +Index: libxslt-1.1.35/libxslt/variables.c +=================================================================== +--- libxslt-1.1.35.orig/libxslt/variables.c ++++ libxslt-1.1.35/libxslt/variables.c +@@ -352,6 +352,7 @@ + return; + + if (ctxt && (ctxt->cache->nbRVT < 40)) { ++ xsltFreeGeneratedIdsForDoc(ctxt, RVT); + /* + * Store the Result Tree Fragment. + * Free the document info. +@@ -397,6 +398,8 @@ + /* + * Free it. + */ ++ if (ctxt != NULL) ++ xsltFreeGeneratedIdsForDoc(ctxt, RVT); + if (RVT->_private != NULL) { + xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private); + xmlFree(RVT->_private); +@@ -451,6 +454,7 @@ + cur = ctxt->localRVT; + while (cur != NULL) { + next = (xmlDocPtr) cur->next; ++ xsltFreeGeneratedIdsForDoc(ctxt, cur); + if (cur->_private != NULL) { + xsltFreeDocumentKeys(cur->_private); + xmlFree(cur->_private); +@@ -465,6 +469,7 @@ + cur = ctxt->tmpRVT; + while (cur != NULL) { + next = (xmlDocPtr) cur->next; ++ xsltFreeGeneratedIdsForDoc(ctxt, cur); + if (cur->_private != NULL) { + xsltFreeDocumentKeys(cur->_private); + xmlFree(cur->_private); +@@ -479,6 +484,7 @@ + cur = ctxt->persistRVT; + while (cur != NULL) { + next = (xmlDocPtr) cur->next; ++ xsltFreeGeneratedIdsForDoc(ctxt, cur); + if (cur->_private != NULL) { + xsltFreeDocumentKeys(cur->_private); + xmlFree(cur->_private); +Index: libxslt-1.1.35/libxslt/documents.c +=================================================================== +--- libxslt-1.1.35.orig/libxslt/documents.c ++++ libxslt-1.1.35/libxslt/documents.c +@@ -255,6 +255,8 @@ + while (cur != NULL) { + doc = cur; + cur = cur->next; ++ if (doc->doc != NULL) ++ xsltFreeGeneratedIdsForDoc(ctxt, doc->doc); + xsltFreeDocumentKeys(doc); + if (!doc->main) + xmlFreeDoc(doc->doc); +@@ -264,6 +266,8 @@ + while (cur != NULL) { + doc = cur; + cur = cur->next; ++ if (doc->doc != NULL) ++ xsltFreeGeneratedIdsForDoc(ctxt, doc->doc); + xsltFreeDocumentKeys(doc); + if (!doc->main) + xmlFreeDoc(doc->doc); +Index: libxslt-1.1.35/libxslt/transform.c +=================================================================== +--- libxslt-1.1.35.orig/libxslt/transform.c ++++ libxslt-1.1.35/libxslt/transform.c +@@ -757,6 +757,7 @@ + xsltFreeDocuments(ctxt); + xsltFreeCtxtExts(ctxt); + xsltFreeRVTs(ctxt); ++ xsltFreeGeneratedIds(ctxt); + xsltTransformCacheFree(ctxt->cache); + xmlDictFree(ctxt->dict); + #ifdef WITH_XSLT_DEBUG diff -Nru libxslt-1.1.35/debian/patches/series libxslt-1.1.35/debian/patches/series --- libxslt-1.1.35/debian/patches/series 2025-09-22 07:36:44.000000000 +0000 +++ libxslt-1.1.35/debian/patches/series 2026-04-10 06:56:30.000000000 +0000 @@ -15,3 +15,4 @@ 0019-variables-Fix-non-deterministic-generated-IDs.patch 0020-Clean-up-attributes-in-source-doc.patch gnome-libxslt-bug-139-apple-fix.diff +0021-generate-id-store-ids-out-of-band.patch