|
 |
9a491d |
From 7f70302bfa9faeac9c9f7be8adf96d32c16acb72 Mon Sep 17 00:00:00 2001
|
|
 |
9a491d |
From: Nick Wellnhofer <wellnhofer@aevum.de>
|
|
 |
9a491d |
Date: Tue, 8 Feb 2022 03:29:24 +0100
|
|
 |
9a491d |
Subject: [PATCH] [CVE-2022-23308] Use-after-free of ID and IDREF attributes
|
|
 |
9a491d |
|
|
 |
9a491d |
If a document is parsed with XML_PARSE_DTDVALID and without
|
|
 |
9a491d |
XML_PARSE_NOENT, the value of ID attributes has to be normalized after
|
|
 |
9a491d |
potentially expanding entities in xmlRemoveID. Otherwise, later calls
|
|
 |
9a491d |
to xmlGetID can return a pointer to previously freed memory.
|
|
 |
9a491d |
|
|
 |
9a491d |
ID attributes which are empty or contain only whitespace after
|
|
 |
9a491d |
entity expansion are affected in a similar way. This is fixed by
|
|
 |
9a491d |
not storing such attributes in the ID table.
|
|
 |
9a491d |
|
|
 |
9a491d |
The test to detect streaming mode when validating against a DTD was
|
|
 |
9a491d |
broken. In connection with the defects above, this could result in a
|
|
 |
9a491d |
use-after-free when using the xmlReader interface with validation.
|
|
 |
9a491d |
Fix detection of streaming mode to avoid similar issues. (This changes
|
|
 |
9a491d |
the expected result of a test case. But as far as I can tell, using the
|
|
 |
9a491d |
XML reader with XIncludes referencing the root document never worked
|
|
 |
9a491d |
properly, anyway.)
|
|
 |
9a491d |
|
|
 |
9a491d |
All of these issues can result in denial of service. Using xmlReader
|
|
 |
9a491d |
with validation could result in disclosure of memory via the error
|
|
 |
9a491d |
channel, typically stderr. The security impact of xmlGetID returning
|
|
 |
9a491d |
a pointer to freed memory depends on the application. The typical use
|
|
 |
9a491d |
case of calling xmlGetID on an unmodified document is not affected.
|
|
 |
9a491d |
---
|
|
 |
9a491d |
valid.c | 88 +++++++++++++++++++++++++++++++++++----------------------
|
|
 |
9a491d |
1 file changed, 55 insertions(+), 33 deletions(-)
|
|
 |
9a491d |
|
|
 |
9a491d |
diff --git a/valid.c b/valid.c
|
|
 |
9a491d |
index a64b96be..5b81059f 100644
|
|
 |
9a491d |
--- a/valid.c
|
|
 |
9a491d |
+++ b/valid.c
|
|
 |
9a491d |
@@ -479,6 +479,35 @@ nodeVPop(xmlValidCtxtPtr ctxt)
|
|
 |
9a491d |
return (ret);
|
|
 |
9a491d |
}
|
|
 |
9a491d |
|
|
 |
9a491d |
+/**
|
|
 |
9a491d |
+ * xmlValidNormalizeString:
|
|
 |
9a491d |
+ * @str: a string
|
|
 |
9a491d |
+ *
|
|
 |
9a491d |
+ * Normalize a string in-place.
|
|
 |
9a491d |
+ */
|
|
 |
9a491d |
+static void
|
|
 |
9a491d |
+xmlValidNormalizeString(xmlChar *str) {
|
|
 |
9a491d |
+ xmlChar *dst;
|
|
 |
9a491d |
+ const xmlChar *src;
|
|
 |
9a491d |
+
|
|
 |
9a491d |
+ if (str == NULL)
|
|
 |
9a491d |
+ return;
|
|
 |
9a491d |
+ src = str;
|
|
 |
9a491d |
+ dst = str;
|
|
 |
9a491d |
+
|
|
 |
9a491d |
+ while (*src == 0x20) src++;
|
|
 |
9a491d |
+ while (*src != 0) {
|
|
 |
9a491d |
+ if (*src == 0x20) {
|
|
 |
9a491d |
+ while (*src == 0x20) src++;
|
|
 |
9a491d |
+ if (*src != 0)
|
|
 |
9a491d |
+ *dst++ = 0x20;
|
|
 |
9a491d |
+ } else {
|
|
 |
9a491d |
+ *dst++ = *src++;
|
|
 |
9a491d |
+ }
|
|
 |
9a491d |
+ }
|
|
 |
9a491d |
+ *dst = 0;
|
|
 |
9a491d |
+}
|
|
 |
9a491d |
+
|
|
 |
9a491d |
#ifdef DEBUG_VALID_ALGO
|
|
 |
9a491d |
static void
|
|
 |
9a491d |
xmlValidPrintNode(xmlNodePtr cur) {
|
|
 |
9a491d |
@@ -2546,6 +2575,24 @@ xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
|
|
 |
9a491d |
(xmlDictOwns(dict, (const xmlChar *)(str)) == 0))) \
|
|
 |
9a491d |
xmlFree((char *)(str));
|
|
 |
9a491d |
|
|
 |
9a491d |
+static int
|
|
 |
9a491d |
+xmlIsStreaming(xmlValidCtxtPtr ctxt) {
|
|
 |
9a491d |
+ xmlParserCtxtPtr pctxt;
|
|
 |
9a491d |
+
|
|
 |
9a491d |
+ if (ctxt == NULL)
|
|
 |
9a491d |
+ return(0);
|
|
 |
9a491d |
+ /*
|
|
 |
9a491d |
+ * These magic values are also abused to detect whether we're validating
|
|
 |
9a491d |
+ * while parsing a document. In this case, userData points to the parser
|
|
 |
9a491d |
+ * context.
|
|
 |
9a491d |
+ */
|
|
 |
9a491d |
+ if ((ctxt->finishDtd != XML_CTXT_FINISH_DTD_0) &&
|
|
 |
9a491d |
+ (ctxt->finishDtd != XML_CTXT_FINISH_DTD_1))
|
|
 |
9a491d |
+ return(0);
|
|
 |
9a491d |
+ pctxt = ctxt->userData;
|
|
 |
9a491d |
+ return(pctxt->parseMode == XML_PARSE_READER);
|
|
 |
9a491d |
+}
|
|
 |
9a491d |
+
|
|
 |
9a491d |
/**
|
|
 |
9a491d |
* xmlFreeID:
|
|
 |
9a491d |
* @not: A id
|
|
 |
9a491d |
@@ -2589,7 +2636,7 @@ xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
|
|
 |
9a491d |
if (doc == NULL) {
|
|
 |
9a491d |
return(NULL);
|
|
 |
9a491d |
}
|
|
 |
9a491d |
- if (value == NULL) {
|
|
 |
9a491d |
+ if ((value == NULL) || (value[0] == 0)) {
|
|
 |
9a491d |
return(NULL);
|
|
 |
9a491d |
}
|
|
 |
9a491d |
if (attr == NULL) {
|
|
 |
9a491d |
@@ -2620,7 +2667,7 @@ xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
|
|
 |
9a491d |
*/
|
|
 |
9a491d |
ret->value = xmlStrdup(value);
|
|
 |
9a491d |
ret->doc = doc;
|
|
 |
9a491d |
- if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
|
|
 |
9a491d |
+ if (xmlIsStreaming(ctxt)) {
|
|
 |
9a491d |
/*
|
|
 |
9a491d |
* Operating in streaming mode, attr is gonna disapear
|
|
 |
9a491d |
*/
|
|
 |
9a491d |
@@ -2754,6 +2801,7 @@ xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
|
|
 |
9a491d |
ID = xmlNodeListGetString(doc, attr->children, 1);
|
|
 |
9a491d |
if (ID == NULL)
|
|
 |
9a491d |
return(-1);
|
|
 |
9a491d |
+ xmlValidNormalizeString(ID);
|
|
 |
9a491d |
|
|
 |
9a491d |
id = xmlHashLookup(table, ID);
|
|
 |
9a491d |
if (id == NULL || id->attr != attr) {
|
|
 |
9a491d |
@@ -2942,7 +2990,7 @@ xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
|
|
 |
9a491d |
* fill the structure.
|
|
 |
9a491d |
*/
|
|
 |
9a491d |
ret->value = xmlStrdup(value);
|
|
 |
9a491d |
- if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
|
|
 |
9a491d |
+ if (xmlIsStreaming(ctxt)) {
|
|
 |
9a491d |
/*
|
|
 |
9a491d |
* Operating in streaming mode, attr is gonna disapear
|
|
 |
9a491d |
*/
|
|
 |
9a491d |
@@ -3962,8 +4010,7 @@ xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
|
|
 |
9a491d |
xmlChar *
|
|
 |
9a491d |
xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
|
|
 |
9a491d |
xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
|
|
 |
9a491d |
- xmlChar *ret, *dst;
|
|
 |
9a491d |
- const xmlChar *src;
|
|
 |
9a491d |
+ xmlChar *ret;
|
|
 |
9a491d |
xmlAttributePtr attrDecl = NULL;
|
|
 |
9a491d |
int extsubset = 0;
|
|
 |
9a491d |
|
|
 |
9a491d |
@@ -4004,19 +4051,7 @@ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
|
|
 |
9a491d |
ret = xmlStrdup(value);
|
|
 |
9a491d |
if (ret == NULL)
|
|
 |
9a491d |
return(NULL);
|
|
 |
9a491d |
- src = value;
|
|
 |
9a491d |
- dst = ret;
|
|
 |
9a491d |
- while (*src == 0x20) src++;
|
|
 |
9a491d |
- while (*src != 0) {
|
|
 |
9a491d |
- if (*src == 0x20) {
|
|
 |
9a491d |
- while (*src == 0x20) src++;
|
|
 |
9a491d |
- if (*src != 0)
|
|
 |
9a491d |
- *dst++ = 0x20;
|
|
 |
9a491d |
- } else {
|
|
 |
9a491d |
- *dst++ = *src++;
|
|
 |
9a491d |
- }
|
|
 |
9a491d |
- }
|
|
 |
9a491d |
- *dst = 0;
|
|
 |
9a491d |
+ xmlValidNormalizeString(ret);
|
|
 |
9a491d |
if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
|
|
 |
9a491d |
xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE,
|
|
 |
9a491d |
"standalone: %s on %s value had to be normalized based on external subset declaration\n",
|
|
 |
9a491d |
@@ -4048,8 +4083,7 @@ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
|
|
 |
9a491d |
xmlChar *
|
|
 |
9a491d |
xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
|
|
 |
9a491d |
const xmlChar *name, const xmlChar *value) {
|
|
 |
9a491d |
- xmlChar *ret, *dst;
|
|
 |
9a491d |
- const xmlChar *src;
|
|
 |
9a491d |
+ xmlChar *ret;
|
|
 |
9a491d |
xmlAttributePtr attrDecl = NULL;
|
|
 |
9a491d |
|
|
 |
9a491d |
if (doc == NULL) return(NULL);
|
|
 |
9a491d |
@@ -4079,19 +4113,7 @@ xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
|
|
 |
9a491d |
ret = xmlStrdup(value);
|
|
 |
9a491d |
if (ret == NULL)
|
|
 |
9a491d |
return(NULL);
|
|
 |
9a491d |
- src = value;
|
|
 |
9a491d |
- dst = ret;
|
|
 |
9a491d |
- while (*src == 0x20) src++;
|
|
 |
9a491d |
- while (*src != 0) {
|
|
 |
9a491d |
- if (*src == 0x20) {
|
|
 |
9a491d |
- while (*src == 0x20) src++;
|
|
 |
9a491d |
- if (*src != 0)
|
|
 |
9a491d |
- *dst++ = 0x20;
|
|
 |
9a491d |
- } else {
|
|
 |
9a491d |
- *dst++ = *src++;
|
|
 |
9a491d |
- }
|
|
 |
9a491d |
- }
|
|
 |
9a491d |
- *dst = 0;
|
|
 |
9a491d |
+ xmlValidNormalizeString(ret);
|
|
 |
9a491d |
return(ret);
|
|
 |
9a491d |
}
|
|
 |
9a491d |
|
|
 |
9a491d |
--
|
|
 |
9a491d |
2.35.1
|
|
 |
9a491d |
|