[CVE-2022-23308] Use-after-free of ID and IDREF attributes 32/290632/2 accepted/tizen_8.0_base accepted/tizen_base accepted/tizen_base_riscv accepted/tizen_base_tool accepted/tizen_base_toolchain sandbox/backup/gettext_0.21.1_20231228 tizen_8.0_base tizen_base accepted/tizen/8.0/base/20231005.044717 accepted/tizen/base/20230714.003324 accepted/tizen/base/riscv/20231124.082758 accepted/tizen/base/tool/20230331.013427 accepted/tizen/base/toolchain/20240318.094201 accepted/tizen/base/toolchain/20240319.005200 submit/tizen_base/20230330.040513 tizen_8.0_m2_release
authorJinWang An <jinwang.an@samsung.com>
Thu, 30 Mar 2023 03:16:17 +0000 (12:16 +0900)
committerJinWang An <jinwang.an@samsung.com>
Thu, 30 Mar 2023 03:24:01 +0000 (12:24 +0900)
From 652dd12a858989b14eed4e84e453059cd3ba340e Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <wellnhofer@aevum.de>
Date: Tue, 8 Feb 2022 03:29:24 +0100
Subject: [PATCH 001/233] [CVE-2022-23308] Use-after-free of ID and IDREF
 attributes

 If a document is parsed with XML_PARSE_DTDVALID and without
 XML_PARSE_NOENT, the value of ID attributes has to be normalized after
 potentially expanding entities in xmlRemoveID. Otherwise, later calls
 to xmlGetID can return a pointer to previously freed memory.

 ID attributes which are empty or contain only whitespace after
 entity expansion are affected in a similar way. This is fixed by
 not storing such attributes in the ID table.

 The test to detect streaming mode when validating against a DTD was
 broken. In connection with the defects above, this could result in a
 use-after-free when using the xmlReader interface with validation.
 Fix detection of streaming mode to avoid similar issues. (This changes
 the expected result of a test case. But as far as I can tell, using the
 XML reader with XIncludes referencing the root document never worked
 properly, anyway.)

 All of these issues can result in denial of service. Using xmlReader
 with validation could result in disclosure of memory via the error
 channel, typically stderr. The security impact of xmlGetID returning
 a pointer to freed memory depends on the application. The typical use
 case of calling xmlGetID on an unmodified document is not affected.

Change-Id: I4ffa87a65f1a6e6c44de0168bf86143d09d63056
Signed-off-by: JinWang An <jinwang.an@samsung.com>
packaging/CVE-2022-23308.patch [new file with mode: 0644]
packaging/gettext.spec

diff --git a/packaging/CVE-2022-23308.patch b/packaging/CVE-2022-23308.patch
new file mode 100644 (file)
index 0000000..7c1863a
--- /dev/null
@@ -0,0 +1,488 @@
+diff --git a/gettext-tools/gnulib-lib/libxml/valid.c b/gettext-tools/gnulib-lib/libxml/valid.c
+index 22ade700..182c9d5c 100644
+--- a/gettext-tools/gnulib-lib/libxml/valid.c
++++ b/gettext-tools/gnulib-lib/libxml/valid.c
+@@ -936,6 +936,36 @@ xmlFreeValidCtxt(xmlValidCtxtPtr cur) {
+ #endif /* LIBXML_VALID_ENABLED */
++/**
++ * xmlValidNormalizeString:
++ * @str: a string
++ *
++ * Normalize a string in-place.
++ */
++static void
++xmlValidNormalizeString(xmlChar *str) {
++    xmlChar *dst;
++    const xmlChar *src;
++
++    if (str == NULL)
++        return;
++    src = str;
++    dst = str;
++
++    while (*src == 0x20) src++;
++    while (*src != 0) {
++      if (*src == 0x20) {
++          while (*src == 0x20) src++;
++          if (*src != 0)
++              *dst++ = 0x20;
++      } else {
++          *dst++ = *src++;
++      }
++    }
++    *dst = 0;
++}
++
++
+ /**
+  * xmlNewDocElementContent:
+  * @doc:  the document
+@@ -2593,6 +2623,24 @@ xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
+           (xmlDictOwns(dict, (const xmlChar *)(str)) == 0)))  \
+           xmlFree((char *)(str));
++static int
++xmlIsStreaming(xmlValidCtxtPtr ctxt) {
++    xmlParserCtxtPtr pctxt;
++
++    if (ctxt == NULL)
++        return(0);
++    /*
++     * These magic values are also abused to detect whether we're validating
++     * while parsing a document. In this case, userData points to the parser
++     * context.
++     */
++    if ((ctxt->finishDtd != XML_CTXT_FINISH_DTD_0) &&
++        (ctxt->finishDtd != XML_CTXT_FINISH_DTD_1))
++        return(0);
++    pctxt = ctxt->userData;
++    return(pctxt->parseMode == XML_PARSE_READER);
++}
++
+ /**
+  * xmlFreeID:
+  * @not:  A id
+@@ -2636,7 +2684,7 @@ xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
+     if (doc == NULL) {
+       return(NULL);
+     }
+-    if (value == NULL) {
++    if ((value == NULL) || (value[0] == 0)) {
+       return(NULL);
+     }
+     if (attr == NULL) {
+@@ -2667,7 +2715,7 @@ xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
+      */
+     ret->value = xmlStrdup(value);
+     ret->doc = doc;
+-    if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
++    if (xmlIsStreaming(ctxt)) {
+       /*
+        * Operating in streaming mode, attr is gonna disapear
+        */
+@@ -2806,6 +2854,7 @@ xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
+     ID = xmlNodeListGetString(doc, attr->children, 1);
+     if (ID == NULL)
+         return(-1);
++    xmlValidNormalizeString(ID);
+     id = xmlHashLookup(table, ID);
+     if (id == NULL || id->attr != attr) {
+@@ -2995,7 +3044,7 @@ xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
+      * fill the structure.
+      */
+     ret->value = xmlStrdup(value);
+-    if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
++    if (xmlIsStreaming(ctxt)) {
+       /*
+        * Operating in streaming mode, attr is gonna disapear
+        */
+@@ -4014,8 +4063,7 @@ xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
+ xmlChar *
+ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
+            xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
+-    xmlChar *ret, *dst;
+-    const xmlChar *src;
++    xmlChar *ret;
+     xmlAttributePtr attrDecl = NULL;
+     int extsubset = 0;
+@@ -4056,19 +4104,7 @@ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
+     ret = xmlStrdup(value);
+     if (ret == NULL)
+       return(NULL);
+-    src = value;
+-    dst = ret;
+-    while (*src == 0x20) src++;
+-    while (*src != 0) {
+-      if (*src == 0x20) {
+-          while (*src == 0x20) src++;
+-          if (*src != 0)
+-              *dst++ = 0x20;
+-      } else {
+-          *dst++ = *src++;
+-      }
+-    }
+-    *dst = 0;
++    xmlValidNormalizeString(ret);
+     if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
+       xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE,
+ "standalone: %s on %s value had to be normalized based on external subset declaration\n",
+@@ -4100,8 +4136,7 @@ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
+ xmlChar *
+ xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
+                               const xmlChar *name, const xmlChar *value) {
+-    xmlChar *ret, *dst;
+-    const xmlChar *src;
++    xmlChar *ret;
+     xmlAttributePtr attrDecl = NULL;
+     if (doc == NULL) return(NULL);
+@@ -4131,19 +4166,7 @@ xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
+     ret = xmlStrdup(value);
+     if (ret == NULL)
+       return(NULL);
+-    src = value;
+-    dst = ret;
+-    while (*src == 0x20) src++;
+-    while (*src != 0) {
+-      if (*src == 0x20) {
+-          while (*src == 0x20) src++;
+-          if (*src != 0)
+-              *dst++ = 0x20;
+-      } else {
+-          *dst++ = *src++;
+-      }
+-    }
+-    *dst = 0;
++    xmlValidNormalizeString(ret);
+     return(ret);
+ }
+diff --git a/gnulib-local/lib/libxml/valid.c b/gnulib-local/lib/libxml/valid.c
+index 22ade700..85b67bc1 100644
+--- a/gnulib-local/lib/libxml/valid.c
++++ b/gnulib-local/lib/libxml/valid.c
+@@ -936,6 +936,35 @@ xmlFreeValidCtxt(xmlValidCtxtPtr cur) {
+ #endif /* LIBXML_VALID_ENABLED */
++/**
++ * xmlValidNormalizeString:
++ * @str: a string
++ *
++ * Normalize a string in-place.
++ */
++static void
++xmlValidNormalizeString(xmlChar *str) {
++    xmlChar *dst;
++    const xmlChar *src;
++
++    if (str == NULL)
++        return;
++    src = str;
++    dst = str;
++
++    while (*src == 0x20) src++;
++    while (*src != 0) {
++      if (*src == 0x20) {
++          while (*src == 0x20) src++;
++          if (*src != 0)
++              *dst++ = 0x20;
++      } else {
++          *dst++ = *src++;
++      }
++    }
++    *dst = 0;
++}
++
+ /**
+  * xmlNewDocElementContent:
+  * @doc:  the document
+@@ -2593,6 +2622,24 @@ xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
+           (xmlDictOwns(dict, (const xmlChar *)(str)) == 0)))  \
+           xmlFree((char *)(str));
++static int
++xmlIsStreaming(xmlValidCtxtPtr ctxt) {
++    xmlParserCtxtPtr pctxt;
++
++    if (ctxt == NULL)
++        return(0);
++    /*
++     * These magic values are also abused to detect whether we're validating
++     * while parsing a document. In this case, userData points to the parser
++     * context.
++     */
++    if ((ctxt->finishDtd != XML_CTXT_FINISH_DTD_0) &&
++        (ctxt->finishDtd != XML_CTXT_FINISH_DTD_1))
++        return(0);
++    pctxt = ctxt->userData;
++    return(pctxt->parseMode == XML_PARSE_READER);
++}
++
+ /**
+  * xmlFreeID:
+  * @not:  A id
+@@ -2636,7 +2683,7 @@ xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
+     if (doc == NULL) {
+       return(NULL);
+     }
+-    if (value == NULL) {
++    if ((value == NULL) || (value[0] == 0)) {
+       return(NULL);
+     }
+     if (attr == NULL) {
+@@ -2667,7 +2714,7 @@ xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
+      */
+     ret->value = xmlStrdup(value);
+     ret->doc = doc;
+-    if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
++    if (xmlIsStreaming(ctxt)) {
+       /*
+        * Operating in streaming mode, attr is gonna disapear
+        */
+@@ -2806,6 +2853,7 @@ xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
+     ID = xmlNodeListGetString(doc, attr->children, 1);
+     if (ID == NULL)
+         return(-1);
++    xmlValidNormalizeString(ID);
+     id = xmlHashLookup(table, ID);
+     if (id == NULL || id->attr != attr) {
+@@ -2995,7 +3043,7 @@ xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
+      * fill the structure.
+      */
+     ret->value = xmlStrdup(value);
+-    if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
++    if (xmlIsStreaming(ctxt)) {
+       /*
+        * Operating in streaming mode, attr is gonna disapear
+        */
+@@ -4014,8 +4062,7 @@ xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
+ xmlChar *
+ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
+            xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
+-    xmlChar *ret, *dst;
+-    const xmlChar *src;
++    xmlChar *ret;
+     xmlAttributePtr attrDecl = NULL;
+     int extsubset = 0;
+@@ -4056,19 +4103,7 @@ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
+     ret = xmlStrdup(value);
+     if (ret == NULL)
+       return(NULL);
+-    src = value;
+-    dst = ret;
+-    while (*src == 0x20) src++;
+-    while (*src != 0) {
+-      if (*src == 0x20) {
+-          while (*src == 0x20) src++;
+-          if (*src != 0)
+-              *dst++ = 0x20;
+-      } else {
+-          *dst++ = *src++;
+-      }
+-    }
+-    *dst = 0;
++    xmlValidNormalizeString(ret);
+     if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
+       xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE,
+ "standalone: %s on %s value had to be normalized based on external subset declaration\n",
+@@ -4100,8 +4135,7 @@ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
+ xmlChar *
+ xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
+                               const xmlChar *name, const xmlChar *value) {
+-    xmlChar *ret, *dst;
+-    const xmlChar *src;
++    xmlChar *ret;
+     xmlAttributePtr attrDecl = NULL;
+     if (doc == NULL) return(NULL);
+@@ -4131,19 +4165,7 @@ xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
+     ret = xmlStrdup(value);
+     if (ret == NULL)
+       return(NULL);
+-    src = value;
+-    dst = ret;
+-    while (*src == 0x20) src++;
+-    while (*src != 0) {
+-      if (*src == 0x20) {
+-          while (*src == 0x20) src++;
+-          if (*src != 0)
+-              *dst++ = 0x20;
+-      } else {
+-          *dst++ = *src++;
+-      }
+-    }
+-    *dst = 0;
++    xmlValidNormalizeString(ret);
+     return(ret);
+ }
+diff --git a/libtextstyle/lib/libxml/valid.c b/libtextstyle/lib/libxml/valid.c
+index 22ade700..182c9d5c 100644
+--- a/libtextstyle/lib/libxml/valid.c
++++ b/libtextstyle/lib/libxml/valid.c
+@@ -936,6 +936,36 @@ xmlFreeValidCtxt(xmlValidCtxtPtr cur) {
+ #endif /* LIBXML_VALID_ENABLED */
++/**
++ * xmlValidNormalizeString:
++ * @str: a string
++ *
++ * Normalize a string in-place.
++ */
++static void
++xmlValidNormalizeString(xmlChar *str) {
++    xmlChar *dst;
++    const xmlChar *src;
++
++    if (str == NULL)
++        return;
++    src = str;
++    dst = str;
++
++    while (*src == 0x20) src++;
++    while (*src != 0) {
++      if (*src == 0x20) {
++          while (*src == 0x20) src++;
++          if (*src != 0)
++              *dst++ = 0x20;
++      } else {
++          *dst++ = *src++;
++      }
++    }
++    *dst = 0;
++}
++
++
+ /**
+  * xmlNewDocElementContent:
+  * @doc:  the document
+@@ -2593,6 +2623,24 @@ xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
+           (xmlDictOwns(dict, (const xmlChar *)(str)) == 0)))  \
+           xmlFree((char *)(str));
++static int
++xmlIsStreaming(xmlValidCtxtPtr ctxt) {
++    xmlParserCtxtPtr pctxt;
++
++    if (ctxt == NULL)
++        return(0);
++    /*
++     * These magic values are also abused to detect whether we're validating
++     * while parsing a document. In this case, userData points to the parser
++     * context.
++     */
++    if ((ctxt->finishDtd != XML_CTXT_FINISH_DTD_0) &&
++        (ctxt->finishDtd != XML_CTXT_FINISH_DTD_1))
++        return(0);
++    pctxt = ctxt->userData;
++    return(pctxt->parseMode == XML_PARSE_READER);
++}
++
+ /**
+  * xmlFreeID:
+  * @not:  A id
+@@ -2636,7 +2684,7 @@ xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
+     if (doc == NULL) {
+       return(NULL);
+     }
+-    if (value == NULL) {
++    if ((value == NULL) || (value[0] == 0)) {
+       return(NULL);
+     }
+     if (attr == NULL) {
+@@ -2667,7 +2715,7 @@ xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
+      */
+     ret->value = xmlStrdup(value);
+     ret->doc = doc;
+-    if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
++    if (xmlIsStreaming(ctxt)) {
+       /*
+        * Operating in streaming mode, attr is gonna disapear
+        */
+@@ -2806,6 +2854,7 @@ xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
+     ID = xmlNodeListGetString(doc, attr->children, 1);
+     if (ID == NULL)
+         return(-1);
++    xmlValidNormalizeString(ID);
+     id = xmlHashLookup(table, ID);
+     if (id == NULL || id->attr != attr) {
+@@ -2995,7 +3044,7 @@ xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
+      * fill the structure.
+      */
+     ret->value = xmlStrdup(value);
+-    if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
++    if (xmlIsStreaming(ctxt)) {
+       /*
+        * Operating in streaming mode, attr is gonna disapear
+        */
+@@ -4014,8 +4063,7 @@ xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
+ xmlChar *
+ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
+            xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
+-    xmlChar *ret, *dst;
+-    const xmlChar *src;
++    xmlChar *ret;
+     xmlAttributePtr attrDecl = NULL;
+     int extsubset = 0;
+@@ -4056,19 +4104,7 @@ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
+     ret = xmlStrdup(value);
+     if (ret == NULL)
+       return(NULL);
+-    src = value;
+-    dst = ret;
+-    while (*src == 0x20) src++;
+-    while (*src != 0) {
+-      if (*src == 0x20) {
+-          while (*src == 0x20) src++;
+-          if (*src != 0)
+-              *dst++ = 0x20;
+-      } else {
+-          *dst++ = *src++;
+-      }
+-    }
+-    *dst = 0;
++    xmlValidNormalizeString(ret);
+     if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
+       xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE,
+ "standalone: %s on %s value had to be normalized based on external subset declaration\n",
+@@ -4100,8 +4136,7 @@ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
+ xmlChar *
+ xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
+                               const xmlChar *name, const xmlChar *value) {
+-    xmlChar *ret, *dst;
+-    const xmlChar *src;
++    xmlChar *ret;
+     xmlAttributePtr attrDecl = NULL;
+     if (doc == NULL) return(NULL);
+@@ -4131,19 +4166,7 @@ xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
+     ret = xmlStrdup(value);
+     if (ret == NULL)
+       return(NULL);
+-    src = value;
+-    dst = ret;
+-    while (*src == 0x20) src++;
+-    while (*src != 0) {
+-      if (*src == 0x20) {
+-          while (*src == 0x20) src++;
+-          if (*src != 0)
+-              *dst++ = 0x20;
+-      } else {
+-          *dst++ = *src++;
+-      }
+-    }
+-    *dst = 0;
++    xmlValidNormalizeString(ret);
+     return(ret);
+ }
index aadc2f5..d7f0e51 100644 (file)
@@ -17,6 +17,7 @@ Group:          Development/Tools
 Source:         ftp://ftp.gnu.org/gnu/gettext/%{name}-%{version}.tar.gz
 Source2:        msghack.py
 Source3:        Add_pie_compile_option.patch
+Source4:        CVE-2022-23308.patch
 Source1001:     gettext.manifest
 
 BuildRequires:  bison
@@ -73,6 +74,7 @@ This package contains libraries used internationalization support.
 %prep
 %setup -q
 %{__patch} -p1 < %{SOURCE3}  
+%{__patch} -p1 < %{SOURCE4}  
 
 %build
 export CFLAGS+=" -fPIC"