libxslt/SOURCES/libxslt-1.1.32-CVE-2023-40403.patch

1062 lines
32 KiB
Diff

From e494220e4a57e87e8561e6798f57f4dcf91762b3 Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <wellnhofer@aevum.de>
Date: Wed, 31 Aug 2022 15:29:57 +0200
Subject: [PATCH 1/5] Infrastructure to store extra data in source nodes
Provide a mechanism to store bit flags in nodes from the source
document. This will later be used to store key and id status.
Provide a function to find the psvi member of a node.
Revert any changes to the source document after the transformation.
---
libxslt/transform.c | 34 ++++++++++
libxslt/xsltInternals.h | 3 +
libxslt/xsltutils.c | 135 ++++++++++++++++++++++++++++++++++++++++
libxslt/xsltutils.h | 15 ++++-
4 files changed, 186 insertions(+), 1 deletion(-)
diff --git a/libxslt/transform.c b/libxslt/transform.c
index f748f2df..8634b09e 100644
--- a/libxslt/transform.c
+++ b/libxslt/transform.c
@@ -5824,6 +5824,37 @@ xsltCountKeys(xsltTransformContextPtr ctxt)
return(ctxt->nbKeys);
}
+/**
+ * xsltCleanupSourceDoc:
+ * @doc: Document
+ *
+ * Resets source node flags and ids stored in 'psvi' member.
+ */
+static void
+xsltCleanupSourceDoc(xmlDocPtr doc) {
+ xmlNodePtr cur = (xmlNodePtr) doc;
+ void **psviPtr;
+
+ while (1) {
+ xsltClearSourceNodeFlags(cur, XSLT_SOURCE_NODE_MASK);
+ psviPtr = xsltGetPSVIPtr(cur);
+ if (psviPtr)
+ *psviPtr = NULL;
+
+ if (cur->children != NULL && cur->type != XML_ENTITY_REF_NODE) {
+ cur = cur->children;
+ } else {
+ while (cur->next == NULL) {
+ cur = cur->parent;
+ if (cur == (xmlNodePtr) doc)
+ return;
+ }
+
+ cur = cur->next;
+ }
+ }
+}
+
/**
* xsltApplyStylesheetInternal:
* @style: a parsed XSLT stylesheet
@@ -6210,6 +6241,9 @@ xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc,
printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars);
#endif
+ if (ctxt->sourceDocDirty)
+ xsltCleanupSourceDoc(doc);
+
if ((ctxt != NULL) && (userCtxt == NULL))
xsltFreeTransformContext(ctxt);
diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h
index 47125b36..39b488c0 100644
--- a/libxslt/xsltInternals.h
+++ b/libxslt/xsltInternals.h
@@ -1789,6 +1789,9 @@ struct _xsltTransformContext {
int depth; /* Needed to catch recursions */
int maxTemplateDepth;
int maxTemplateVars;
+ unsigned long opLimit;
+ unsigned long opCount;
+ int sourceDocDirty;
};
/**
diff --git a/libxslt/xsltutils.c b/libxslt/xsltutils.c
index 2154b238..a0d9d331 100644
--- a/libxslt/xsltutils.c
+++ b/libxslt/xsltutils.c
@@ -1788,6 +1788,141 @@ xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len,
return 0;
}
+/**
+ * xsltGetSourceNodeFlags:
+ * @node: Node from source document
+ *
+ * Returns the flags for a source node.
+ */
+int
+xsltGetSourceNodeFlags(xmlNodePtr node) {
+ /*
+ * Squeeze the bit flags into the upper bits of
+ *
+ * - 'int properties' member in struct _xmlDoc
+ * - 'xmlAttributeType atype' member in struct _xmlAttr
+ * - 'unsigned short extra' member in struct _xmlNode
+ */
+ switch (node->type) {
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+ return ((xmlDocPtr) node)->properties >> 27;
+
+ case XML_ATTRIBUTE_NODE:
+ return ((xmlAttrPtr) node)->atype >> 27;
+
+ case XML_ELEMENT_NODE:
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ return node->extra >> 12;
+
+ default:
+ return 0;
+ }
+}
+
+/**
+ * xsltSetSourceNodeFlags:
+ * @node: Node from source document
+ * @flags: Flags
+ *
+ * Sets the specified flags to 1.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int
+xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ int flags) {
+ if (node->doc == ctxt->initialContextDoc)
+ ctxt->sourceDocDirty = 1;
+
+ switch (node->type) {
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+ ((xmlDocPtr) node)->properties |= flags << 27;
+ return 0;
+
+ case XML_ATTRIBUTE_NODE:
+ ((xmlAttrPtr) node)->atype |= flags << 27;
+ return 0;
+
+ case XML_ELEMENT_NODE:
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ node->extra |= flags << 12;
+ return 0;
+
+ default:
+ return -1;
+ }
+}
+
+/**
+ * xsltClearSourceNodeFlags:
+ * @node: Node from source document
+ * @flags: Flags
+ *
+ * Sets the specified flags to 0.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int
+xsltClearSourceNodeFlags(xmlNodePtr node, int flags) {
+ switch (node->type) {
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+ ((xmlDocPtr) node)->properties &= ~(flags << 27);
+ return 0;
+
+ case XML_ATTRIBUTE_NODE:
+ ((xmlAttrPtr) node)->atype &= ~(flags << 27);
+ return 0;
+
+ case XML_ELEMENT_NODE:
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ node->extra &= ~(flags << 12);
+ return 0;
+
+ default:
+ return -1;
+ }
+}
+
+/**
+ * xsltGetPSVIPtr:
+ * @cur: Node
+ *
+ * Returns a pointer to the psvi member of a node or NULL on error.
+ */
+void **
+xsltGetPSVIPtr(xmlNodePtr cur) {
+ switch (cur->type) {
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+ return &((xmlDocPtr) cur)->psvi;
+
+ case XML_ATTRIBUTE_NODE:
+ return &((xmlAttrPtr) cur)->psvi;
+
+ case XML_ELEMENT_NODE:
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ return &cur->psvi;
+
+ default:
+ return NULL;
+ }
+}
+
/************************************************************************
* *
* Generating profiling informations *
diff --git a/libxslt/xsltutils.h b/libxslt/xsltutils.h
index 789865a6..df89aeb1 100644
--- a/libxslt/xsltutils.h
+++ b/libxslt/xsltutils.h
@@ -247,6 +247,19 @@ XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
const xmlChar *str,
int flags);
+#ifdef IN_LIBXSLT
+#define XSLT_SOURCE_NODE_MASK 15
+int
+xsltGetSourceNodeFlags(xmlNodePtr node);
+int
+xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ int flags);
+int
+xsltClearSourceNodeFlags(xmlNodePtr node, int flags);
+void **
+xsltGetPSVIPtr(xmlNodePtr cur);
+#endif
+
/*
* Profiling.
*/
@@ -267,6 +280,7 @@ XSLTPUBFUN void XSLTCALL
* Sampling precision for profiling
*/
#define XSLT_TIMESTAMP_TICS_PER_SEC 100000l
+#endif
/*
* Hooks for the debugger.
@@ -306,7 +320,6 @@ XSLTPUBFUN void XSLTCALL
#ifdef __cplusplus
}
-#endif
#endif /* __XML_XSLTUTILS_H__ */
--
2.49.0
From 5895aaae58025fa35a540f6f922f9137505f1e27 Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <wellnhofer@aevum.de>
Date: Wed, 31 Aug 2022 15:34:47 +0200
Subject: [PATCH 2/5] Store key status of source nodes as bit flag
This frees up the psvi member.
---
libxslt/keys.c | 19 +------------------
libxslt/pattern.c | 37 ++-----------------------------------
libxslt/xsltutils.h | 1 +
3 files changed, 4 insertions(+), 53 deletions(-)
diff --git a/libxslt/keys.c b/libxslt/keys.c
index 85902b0d..6675903d 100644
--- a/libxslt/keys.c
+++ b/libxslt/keys.c
@@ -826,24 +826,7 @@ fprintf(stderr, "xsltInitCtxtKey %s : %d\n", keyDef->name, ctxt->keyInitLevel);
*/
xmlXPathNodeSetAdd(keylist, cur);
}
- switch (cur->type) {
- case XML_ELEMENT_NODE:
- case XML_TEXT_NODE:
- case XML_CDATA_SECTION_NODE:
- case XML_PI_NODE:
- case XML_COMMENT_NODE:
- cur->psvi = keyDef;
- break;
- case XML_ATTRIBUTE_NODE:
- ((xmlAttrPtr) cur)->psvi = keyDef;
- break;
- case XML_DOCUMENT_NODE:
- case XML_HTML_DOCUMENT_NODE:
- ((xmlDocPtr) cur)->psvi = keyDef;
- break;
- default:
- break;
- }
+ xsltSetSourceNodeFlags(ctxt, cur, XSLT_SOURCE_NODE_HAS_KEY);
xmlFree(str);
str = NULL;
diff --git a/libxslt/pattern.c b/libxslt/pattern.c
index c094e974..11d8c3e5 100644
--- a/libxslt/pattern.c
+++ b/libxslt/pattern.c
@@ -2328,7 +2328,6 @@ xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
const xmlChar *name = NULL;
xsltCompMatchPtr list = NULL;
float priority;
- int keyed = 0;
if ((ctxt == NULL) || (node == NULL))
return(NULL);
@@ -2405,37 +2404,25 @@ xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
list = curstyle->rootMatch;
else
list = curstyle->elemMatch;
- if (node->psvi != NULL) keyed = 1;
break;
case XML_ATTRIBUTE_NODE: {
- xmlAttrPtr attr;
-
list = curstyle->attrMatch;
- attr = (xmlAttrPtr) node;
- if (attr->psvi != NULL) keyed = 1;
break;
}
case XML_PI_NODE:
list = curstyle->piMatch;
- if (node->psvi != NULL) keyed = 1;
break;
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE: {
- xmlDocPtr doc;
-
list = curstyle->rootMatch;
- doc = (xmlDocPtr) node;
- if (doc->psvi != NULL) keyed = 1;
break;
}
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
list = curstyle->textMatch;
- if (node->psvi != NULL) keyed = 1;
break;
case XML_COMMENT_NODE:
list = curstyle->commentMatch;
- if (node->psvi != NULL) keyed = 1;
break;
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
@@ -2496,7 +2483,7 @@ xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
}
keyed_match:
- if (keyed) {
+ if (xsltGetSourceNodeFlags(node) & XSLT_SOURCE_NODE_HAS_KEY) {
list = curstyle->keyMatch;
while ((list != NULL) &&
((ret == NULL) || (list->priority > priority))) {
@@ -2521,27 +2508,7 @@ keyed_match:
if (xsltComputeAllKeys(ctxt, node) == -1)
goto error;
- switch (node->type) {
- case XML_ELEMENT_NODE:
- if (node->psvi != NULL) keyed = 1;
- break;
- case XML_ATTRIBUTE_NODE:
- if (((xmlAttrPtr) node)->psvi != NULL) keyed = 1;
- break;
- case XML_TEXT_NODE:
- case XML_CDATA_SECTION_NODE:
- case XML_COMMENT_NODE:
- case XML_PI_NODE:
- if (node->psvi != NULL) keyed = 1;
- break;
- case XML_DOCUMENT_NODE:
- case XML_HTML_DOCUMENT_NODE:
- if (((xmlDocPtr) node)->psvi != NULL) keyed = 1;
- break;
- default:
- break;
- }
- if (keyed)
+ if (xsltGetSourceNodeFlags(node) & XSLT_SOURCE_NODE_HAS_KEY)
goto keyed_match;
}
if (ret != NULL)
diff --git a/libxslt/xsltutils.h b/libxslt/xsltutils.h
index df89aeb1..80194891 100644
--- a/libxslt/xsltutils.h
+++ b/libxslt/xsltutils.h
@@ -249,6 +249,7 @@ XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
#ifdef IN_LIBXSLT
#define XSLT_SOURCE_NODE_MASK 15
+#define XSLT_SOURCE_NODE_HAS_KEY 1
int
xsltGetSourceNodeFlags(xmlNodePtr node);
int
--
2.49.0
From 5bdb3e5c9a0a4bc42dd14fe07cf594298a79edce Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <wellnhofer@aevum.de>
Date: Wed, 31 Aug 2022 15:35:37 +0200
Subject: [PATCH 3/5] Store RVT ownership in 'compression' member
'compression' is another unused member in struct _xmlDoc which is even
better suited to store ownership status. More importantly, this frees up
the 'psvi' member.
This changes the public API but this feature is only required to
implement EXSLT functions.
---
libexslt/functions.c | 2 +-
libxslt/transform.c | 20 +++++++++----------
libxslt/variables.c | 44 ++++++++++++++++++++---------------------
libxslt/variables.h | 6 +++---
libxslt/xsltInternals.h | 2 +-
5 files changed, 37 insertions(+), 37 deletions(-)
diff --git a/libexslt/functions.c b/libexslt/functions.c
index aeb7aaf9..079acde6 100644
--- a/libexslt/functions.c
+++ b/libexslt/functions.c
@@ -758,7 +758,7 @@ exsltFuncResultElem (xsltTransformContextPtr ctxt,
}
/* Mark as function result. */
xsltRegisterLocalRVT(ctxt, container);
- container->psvi = XSLT_RVT_FUNC_RESULT;
+ container->compression = XSLT_RVT_FUNC_RESULT;
oldInsert = ctxt->insert;
ctxt->insert = (xmlNodePtr) container;
diff --git a/libxslt/transform.c b/libxslt/transform.c
index 8634b09e..a631529c 100644
--- a/libxslt/transform.c
+++ b/libxslt/transform.c
@@ -2307,21 +2307,21 @@ xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base)
do {
tmp = cur;
cur = (xmlDocPtr) cur->next;
- if (tmp->psvi == XSLT_RVT_LOCAL) {
+ if (tmp->compression == XSLT_RVT_LOCAL) {
xsltReleaseRVT(ctxt, tmp);
- } else if (tmp->psvi == XSLT_RVT_GLOBAL) {
+ } else if (tmp->compression == XSLT_RVT_GLOBAL) {
xsltRegisterPersistRVT(ctxt, tmp);
- } else if (tmp->psvi == XSLT_RVT_FUNC_RESULT) {
- if (prev == NULL)
- ctxt->localRVT = tmp;
- else
- prev->next = (xmlNodePtr) tmp;
- tmp->prev = (xmlNodePtr) prev;
- prev = tmp;
+ } else if (tmp->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;
} else {
xmlGenericError(xmlGenericErrorContext,
"xsltReleaseLocalRVTs: Unexpected RVT flag %p\n",
- tmp->psvi);
+ tmp->compression);
}
} while (cur != base);
diff --git a/libxslt/variables.c b/libxslt/variables.c
index 5fdbee0f..0f4b72f3 100644
--- a/libxslt/variables.c
+++ b/libxslt/variables.c
@@ -123,7 +123,7 @@ xsltRegisterTmpRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
return(-1);
RVT->prev = NULL;
- RVT->psvi = XSLT_RVT_LOCAL;
+ RVT->compression = XSLT_RVT_LOCAL;
/*
* We'll restrict the lifetime of user-created fragments
@@ -163,7 +163,7 @@ xsltRegisterLocalRVT(xsltTransformContextPtr ctxt,
return(-1);
RVT->prev = NULL;
- RVT->psvi = XSLT_RVT_LOCAL;
+ RVT->compression = XSLT_RVT_LOCAL;
/*
* When evaluating "select" expressions of xsl:variable
@@ -253,7 +253,7 @@ xsltExtensionInstructionResultRegister(xsltTransformContextPtr ctxt,
* Returns 0 in case of success and -1 in case of error.
*/
int
-xsltFlagRVTs(xsltTransformContextPtr ctxt, xmlXPathObjectPtr obj, void *val) {
+xsltFlagRVTs(xsltTransformContextPtr ctxt, xmlXPathObjectPtr obj, int val) {
int i;
xmlNodePtr cur;
xmlDocPtr doc;
@@ -300,34 +300,34 @@ xsltFlagRVTs(xsltTransformContextPtr ctxt, xmlXPathObjectPtr obj, void *val) {
return(-1);
}
if (doc->name && (doc->name[0] == ' ') &&
- doc->psvi != XSLT_RVT_GLOBAL) {
+ doc->compression != XSLT_RVT_GLOBAL) {
/*
* This is a result tree fragment.
- * We store ownership information in the @psvi field.
+ * We store ownership information in the @compression field.
* TODO: How do we know if this is a doc acquired via the
* document() function?
*/
#ifdef WITH_XSLT_DEBUG_VARIABLE
XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
- "Flagging RVT %p: %p -> %p\n", doc, doc->psvi, val));
+ "Flagging RVT %p: %d -> %d\n", doc, doc->compression, val));
#endif
if (val == XSLT_RVT_LOCAL) {
- if (doc->psvi == XSLT_RVT_FUNC_RESULT)
- doc->psvi = XSLT_RVT_LOCAL;
+ if (doc->compression == XSLT_RVT_FUNC_RESULT)
+ doc->compression = XSLT_RVT_LOCAL;
} else if (val == XSLT_RVT_GLOBAL) {
- if (doc->psvi != XSLT_RVT_LOCAL) {
+ if (doc->compression != XSLT_RVT_LOCAL) {
xmlGenericError(xmlGenericErrorContext,
- "xsltFlagRVTs: Invalid transition %p => GLOBAL\n",
- doc->psvi);
- doc->psvi = XSLT_RVT_GLOBAL;
+ "xsltFlagRVTs: Invalid transition %d => GLOBAL\n",
+ doc->compression);
+ doc->compression = XSLT_RVT_GLOBAL;
return(-1);
}
/* Will be registered as persistant in xsltReleaseLocalRVTs. */
- doc->psvi = XSLT_RVT_GLOBAL;
+ doc->compression = XSLT_RVT_GLOBAL;
} else if (val == XSLT_RVT_FUNC_RESULT) {
- doc->psvi = val;
+ doc->compression = val;
}
}
}
@@ -380,7 +380,7 @@ xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
/*
* Reset the ownership information.
*/
- RVT->psvi = NULL;
+ RVT->compression = 0;
RVT->next = (xmlNodePtr) ctxt->cache->RVT;
ctxt->cache->RVT = RVT;
@@ -419,7 +419,7 @@ xsltRegisterPersistRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
{
if ((ctxt == NULL) || (RVT == NULL)) return(-1);
- RVT->psvi = XSLT_RVT_GLOBAL;
+ RVT->compression = XSLT_RVT_GLOBAL;
RVT->prev = NULL;
RVT->next = (xmlNodePtr) ctxt->persistRVT;
if (ctxt->persistRVT != NULL)
@@ -578,15 +578,15 @@ xsltFreeStackElem(xsltStackElemPtr elem) {
cur = elem->fragment;
elem->fragment = (xmlDocPtr) cur->next;
- if (cur->psvi == XSLT_RVT_LOCAL) {
+ if (cur->compression == XSLT_RVT_LOCAL) {
xsltReleaseRVT(elem->context, cur);
- } else if (cur->psvi == XSLT_RVT_FUNC_RESULT) {
+ } else if (cur->compression == XSLT_RVT_FUNC_RESULT) {
xsltRegisterLocalRVT(elem->context, cur);
- cur->psvi = XSLT_RVT_FUNC_RESULT;
+ cur->compression = XSLT_RVT_FUNC_RESULT;
} else {
xmlGenericError(xmlGenericErrorContext,
- "xsltFreeStackElem: Unexpected RVT flag %p\n",
- cur->psvi);
+ "xsltFreeStackElem: Unexpected RVT flag %d\n",
+ cur->compression);
}
}
}
@@ -981,7 +981,7 @@ xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr variable,
* the Result Tree Fragment.
*/
variable->fragment = container;
- container->psvi = XSLT_RVT_LOCAL;
+ container->compression = XSLT_RVT_LOCAL;
oldOutput = ctxt->output;
oldInsert = ctxt->insert;
diff --git a/libxslt/variables.h b/libxslt/variables.h
index 039288fb..e2adee0f 100644
--- a/libxslt/variables.h
+++ b/libxslt/variables.h
@@ -43,7 +43,7 @@ extern "C" {
*
* RVT is destroyed after the current instructions ends.
*/
-#define XSLT_RVT_LOCAL ((void *)1)
+#define XSLT_RVT_LOCAL 1
/**
* XSLT_RVT_FUNC_RESULT:
@@ -52,14 +52,14 @@ extern "C" {
* destroyed after exiting a template and will be reset to XSLT_RVT_LOCAL or
* XSLT_RVT_VARIABLE in the template that receives the return value.
*/
-#define XSLT_RVT_FUNC_RESULT ((void *)2)
+#define XSLT_RVT_FUNC_RESULT 2
/**
* XSLT_RVT_GLOBAL:
*
* RVT is part of a global variable.
*/
-#define XSLT_RVT_GLOBAL ((void *)3)
+#define XSLT_RVT_GLOBAL 3
/*
* Interfaces for the variable module.
diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h
index 39b488c0..e7a058cc 100644
--- a/libxslt/xsltInternals.h
+++ b/libxslt/xsltInternals.h
@@ -1918,7 +1918,7 @@ XSLTPUBFUN int XSLTCALL
xsltFlagRVTs(
xsltTransformContextPtr ctxt,
xmlXPathObjectPtr obj,
- void *val);
+ int val);
XSLTPUBFUN void XSLTCALL
xsltFreeRVTs (xsltTransformContextPtr ctxt);
XSLTPUBFUN void XSLTCALL
--
2.49.0
From 957e111edb935259e777c15e809b1d4de8ee2f3f Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <wellnhofer@aevum.de>
Date: Wed, 31 Aug 2022 13:35:23 +0200
Subject: [PATCH 4/5] Make generate-id() deterministic
Rework the generate-id() function to return deterministic values. We use
a simple incrementing counter and store ids in the 'psvi' member of
nodes which was freed up by previous commits. The presence of an id is
indicated by a new "source node" flag.
This fixes long-standing problems with reproducible builds, see
https://bugzilla.gnome.org/show_bug.cgi?id=751621
This also hardens security, as the old implementation leaked the
difference between a heap and a global pointer, see
https://bugs.chromium.org/p/chromium/issues/detail?id=1356211
The old implementation could also generate the same id for dynamically
created nodes which happened to reuse the same memory. Ids for namespace
nodes were completely broken. They now use the id of the parent element
together with the hex-encoded namespace prefix.
---
libxslt/functions.c | 107 +++++++++++++++++++++++++-----
libxslt/xsltInternals.h | 1 +
libxslt/xsltutils.h | 1 +
tests/REC/test-12.4-1.out | 11 +++
tests/REC/test-12.4-1.xml | 6 ++
tests/REC/test-12.4-1.xsl | 38 +++++++++++
tests/exslt/common/dynamic-id.out | 13 ++++
tests/exslt/common/dynamic-id.xml | 1 +
tests/exslt/common/dynamic-id.xsl | 29 ++++++++
9 files changed, 191 insertions(+), 16 deletions(-)
create mode 100644 tests/REC/test-12.4-1.out
create mode 100644 tests/REC/test-12.4-1.xml
create mode 100644 tests/REC/test-12.4-1.xsl
create mode 100644 tests/exslt/common/dynamic-id.out
create mode 100644 tests/exslt/common/dynamic-id.xml
create mode 100644 tests/exslt/common/dynamic-id.xsl
diff --git a/libxslt/functions.c b/libxslt/functions.c
index 43d82f6d..cd759d4e 100644
--- a/libxslt/functions.c
+++ b/libxslt/functions.c
@@ -680,11 +680,16 @@ xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs)
*/
void
xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
- static char base_address;
+ xsltTransformContextPtr tctxt;
xmlNodePtr cur = NULL;
xmlXPathObjectPtr obj = NULL;
- long val;
- xmlChar str[30];
+ char *str;
+ const xmlChar *nsPrefix = NULL;
+ void **psviPtr;
+ unsigned long id;
+ size_t size, nsPrefixSize;
+
+ tctxt = xsltXPathGetTransformContext(ctxt);
if (nargs == 0) {
cur = ctxt->context->node;
@@ -694,16 +699,15 @@ xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
ctxt->error = XPATH_INVALID_TYPE;
- xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ xsltTransformError(tctxt, NULL, NULL,
"generate-id() : invalid arg expecting a node-set\n");
- return;
+ goto out;
}
obj = valuePop(ctxt);
nodelist = obj->nodesetval;
if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
- xmlXPathFreeObject(obj);
valuePush(ctxt, xmlXPathNewCString(""));
- return;
+ goto out;
}
cur = nodelist->nodeTab[0];
for (i = 1;i < nodelist->nodeNr;i++) {
@@ -712,22 +716,93 @@ xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
cur = nodelist->nodeTab[i];
}
} else {
- xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ xsltTransformError(tctxt, NULL, NULL,
"generate-id() : invalid number of args %d\n", nargs);
ctxt->error = XPATH_INVALID_ARITY;
- return;
+ goto out;
+ }
+
+ size = 30; /* for "id%lu" */
+
+ if (cur->type == XML_NAMESPACE_DECL) {
+ xmlNsPtr ns = (xmlNsPtr) cur;
+
+ nsPrefix = ns->prefix;
+ if (nsPrefix == NULL)
+ nsPrefix = BAD_CAST "";
+ nsPrefixSize = xmlStrlen(nsPrefix);
+ /* For "ns" and hex-encoded string */
+ size += nsPrefixSize * 2 + 2;
+
+ /* Parent is stored in 'next'. */
+ cur = (xmlNodePtr) ns->next;
+ }
+
+ psviPtr = xsltGetPSVIPtr(cur);
+ if (psviPtr == NULL) {
+ xsltTransformError(tctxt, NULL, NULL,
+ "generate-id(): invalid node type %d\n", cur->type);
+ ctxt->error = XPATH_INVALID_TYPE;
+ goto out;
}
- if (obj)
- xmlXPathFreeObject(obj);
+ if (xsltGetSourceNodeFlags(cur) & XSLT_SOURCE_NODE_HAS_ID) {
+ id = (unsigned long) *psviPtr;
+ } 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");
+ ctxt->error = XPATH_MEMORY_ERROR;
+ goto out;
+ }
+
+ id = ++tctxt->currentId;
+ *psviPtr = (void *) id;
+ xsltSetSourceNodeFlags(tctxt, cur, XSLT_SOURCE_NODE_HAS_ID);
+ }
- val = (long)((char *)cur - (char *)&base_address);
- if (val >= 0) {
- snprintf((char *)str, sizeof(str), "idp%ld", val);
+ str = xmlMalloc(size);
+ if (str == NULL) {
+ xsltTransformError(tctxt, NULL, NULL,
+ "generate-id(): out of memory\n");
+ ctxt->error = XPATH_MEMORY_ERROR;
+ goto out;
+ }
+ if (nsPrefix == NULL) {
+ snprintf(str, size, "id%lu", id);
} else {
- snprintf((char *)str, sizeof(str), "idm%ld", -val);
+ size_t i, j;
+
+ snprintf(str, size, "id%luns", id);
+
+ /*
+ * Only ASCII alphanumerics are allowed, so we hex-encode the prefix.
+ */
+ j = strlen(str);
+ for (i = 0; i < nsPrefixSize; i++) {
+ int v;
+
+ v = nsPrefix[i] >> 4;
+ str[j++] = v < 10 ? '0' + v : 'A' + (v - 10);
+ v = nsPrefix[i] & 15;
+ str[j++] = v < 10 ? '0' + v : 'A' + (v - 10);
+ }
+ str[j] = '\0';
}
- valuePush(ctxt, xmlXPathNewString(str));
+ valuePush(ctxt, xmlXPathWrapString(BAD_CAST str));
+
+out:
+ xmlXPathFreeObject(obj);
}
/**
diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h
index e7a058cc..ae9b76af 100644
--- a/libxslt/xsltInternals.h
+++ b/libxslt/xsltInternals.h
@@ -1792,6 +1792,7 @@ struct _xsltTransformContext {
unsigned long opLimit;
unsigned long opCount;
int sourceDocDirty;
+ unsigned long currentId; /* For generate-id() */
};
/**
diff --git a/libxslt/xsltutils.h b/libxslt/xsltutils.h
index 80194891..bae9ad75 100644
--- a/libxslt/xsltutils.h
+++ b/libxslt/xsltutils.h
@@ -250,6 +250,7 @@ XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
#ifdef IN_LIBXSLT
#define XSLT_SOURCE_NODE_MASK 15
#define XSLT_SOURCE_NODE_HAS_KEY 1
+#define XSLT_SOURCE_NODE_HAS_ID 2
int
xsltGetSourceNodeFlags(xmlNodePtr node);
int
diff --git a/tests/REC/test-12.4-1.out b/tests/REC/test-12.4-1.out
new file mode 100644
index 00000000..237a9f27
--- /dev/null
+++ b/tests/REC/test-12.4-1.out
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<result>
+ <document>id1</document>
+ <element>id2</element>
+ <attribute>id3</attribute>
+ <namespace>id2ns</namespace>
+ <namespace>id2nsC3A4C3B6C3BC</namespace>
+ <text>id4</text>
+ <comment>id5</comment>
+ <processing-instruction>id6</processing-instruction>
+</result>
diff --git a/tests/REC/test-12.4-1.xml b/tests/REC/test-12.4-1.xml
new file mode 100644
index 00000000..84484f66
--- /dev/null
+++ b/tests/REC/test-12.4-1.xml
@@ -0,0 +1,6 @@
+<doc xmlns="s:def">
+ <elem attr="value" xmlns:äöü="uri"/>
+ <text>text</text>
+ <!-- comment -->
+ <?pi content?>
+</doc>
diff --git a/tests/REC/test-12.4-1.xsl b/tests/REC/test-12.4-1.xsl
new file mode 100644
index 00000000..5cf5dd33
--- /dev/null
+++ b/tests/REC/test-12.4-1.xsl
@@ -0,0 +1,38 @@
+<xsl:stylesheet
+ version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:d="s:def"
+ exclude-result-prefixes="d">
+
+<xsl:output indent="yes"/>
+
+<xsl:template match="/">
+ <result>
+ <document>
+ <xsl:value-of select="generate-id(/)"/>
+ </document>
+ <element>
+ <xsl:value-of select="generate-id(/d:doc/d:elem)"/>
+ </element>
+ <attribute>
+ <xsl:value-of select="generate-id(d:doc/d:elem/@attr)"/>
+ </attribute>
+ <namespace>
+ <xsl:value-of select="generate-id(d:doc/d:elem/namespace::*[local-name()=''])"/>
+ </namespace>
+ <namespace>
+ <xsl:value-of select="generate-id(d:doc/d:elem/namespace::äöü)"/>
+ </namespace>
+ <text>
+ <xsl:value-of select="generate-id(d:doc/d:text/text())"/>
+ </text>
+ <comment>
+ <xsl:value-of select="generate-id(d:doc/comment())"/>
+ </comment>
+ <processing-instruction>
+ <xsl:value-of select="generate-id(d:doc/processing-instruction())"/>
+ </processing-instruction>
+ </result>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/tests/exslt/common/dynamic-id.out b/tests/exslt/common/dynamic-id.out
new file mode 100644
index 00000000..1b7b7bad
--- /dev/null
+++ b/tests/exslt/common/dynamic-id.out
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<result xmlns:exsl="http://exslt.org/common">
+ <id>id1</id>
+ <id>id2</id>
+ <id>id3</id>
+ <id>id4</id>
+ <id>id5</id>
+ <id>id6</id>
+ <id>id7</id>
+ <id>id8</id>
+ <id>id9</id>
+ <id>id10</id>
+</result>
diff --git a/tests/exslt/common/dynamic-id.xml b/tests/exslt/common/dynamic-id.xml
new file mode 100644
index 00000000..69d62f2c
--- /dev/null
+++ b/tests/exslt/common/dynamic-id.xml
@@ -0,0 +1 @@
+<doc/>
diff --git a/tests/exslt/common/dynamic-id.xsl b/tests/exslt/common/dynamic-id.xsl
new file mode 100644
index 00000000..8478f6af
--- /dev/null
+++ b/tests/exslt/common/dynamic-id.xsl
@@ -0,0 +1,29 @@
+<xsl:stylesheet
+ version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:exsl="http://exslt.org/common">
+
+<xsl:output indent="yes"/>
+
+<xsl:template name="dynamic-id">
+ <id>
+ <xsl:value-of select="generate-id(exsl:node-set('string'))"/>
+ </id>
+</xsl:template>
+
+<xsl:template match="/">
+ <result>
+ <xsl:call-template name="dynamic-id"/>
+ <xsl:call-template name="dynamic-id"/>
+ <xsl:call-template name="dynamic-id"/>
+ <xsl:call-template name="dynamic-id"/>
+ <xsl:call-template name="dynamic-id"/>
+ <xsl:call-template name="dynamic-id"/>
+ <xsl:call-template name="dynamic-id"/>
+ <xsl:call-template name="dynamic-id"/>
+ <xsl:call-template name="dynamic-id"/>
+ <xsl:call-template name="dynamic-id"/>
+ </result>
+</xsl:template>
+
+</xsl:stylesheet>
--
2.49.0
From 7eff64281f0bcd4c6a452dc04f3250037d28786e Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <wellnhofer@aevum.de>
Date: Wed, 31 Aug 2022 21:37:44 +0200
Subject: [PATCH 5/5] Clean up attributes in source doc
Also make bit flag constants unsigned to avoid implicit-conversion
warnings.
---
libxslt/transform.c | 10 ++++++++++
libxslt/xsltutils.h | 6 +++---
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/libxslt/transform.c b/libxslt/transform.c
index a631529c..8a424773 100644
--- a/libxslt/transform.c
+++ b/libxslt/transform.c
@@ -5841,6 +5841,16 @@ xsltCleanupSourceDoc(xmlDocPtr doc) {
if (psviPtr)
*psviPtr = NULL;
+ if (cur->type == XML_ELEMENT_NODE) {
+ xmlAttrPtr prop = cur->properties;
+
+ while (prop) {
+ prop->atype &= ~(XSLT_SOURCE_NODE_MASK << 27);
+ prop->psvi = NULL;
+ prop = prop->next;
+ }
+ }
+
if (cur->children != NULL && cur->type != XML_ENTITY_REF_NODE) {
cur = cur->children;
} else {
diff --git a/libxslt/xsltutils.h b/libxslt/xsltutils.h
index bae9ad75..98ca83a1 100644
--- a/libxslt/xsltutils.h
+++ b/libxslt/xsltutils.h
@@ -248,9 +248,9 @@ XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
int flags);
#ifdef IN_LIBXSLT
-#define XSLT_SOURCE_NODE_MASK 15
-#define XSLT_SOURCE_NODE_HAS_KEY 1
-#define XSLT_SOURCE_NODE_HAS_ID 2
+#define XSLT_SOURCE_NODE_MASK 15u
+#define XSLT_SOURCE_NODE_HAS_KEY 1u
+#define XSLT_SOURCE_NODE_HAS_ID 2u
int
xsltGetSourceNodeFlags(xmlNodePtr node);
int
--
2.49.0