Skip to content

[CVE-2022-43680] XML_ParserFree may free parser->m_dtd memory in out-of-memory situations when it should not #649

Closed
@hartwork

Description

@hartwork

Pull request #616 titles "Bugfixes" contains two bug reports. This new ticket is about the "UAF due to DTD destruction" half of it, with the intention of making the issue more easily accessible.

If I squash together the two related commits…

…we get a single commit with this bug description…

If the allocation in parserInit fails:

parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem));

Then we'll call XML_ParserFree on the parser inside parserCreate:

if (encodingName && ! parser->m_protocolEncodingName) {
  XML_ParserFree(parser);
  return NULL;
}

If we're inside XML_ExternalEntityParserCreate, this can mean that XML_ParserFree is called on a parser whose m_dtd pointer is shared with the document's root parser. Since the flag m_isParamEntity is only set in XML_ExternalEntityParserCreate after parserCreate returns, this call to XML_ParserFree will incorrectly destroy the shared dtd.

This fix moves the setting of m_isParamEntity into parserCreate, since the dtd parameter is only non-NULL in this case.

and this candidate patch for review:

--- a/expat/lib/xmlparse.c
+++ b/expat/lib/xmlparse.c
@@ -1028,10 +1028,16 @@ parserCreate(const XML_Char *encodingName,
   }
   parser->m_dataBufEnd = parser->m_dataBuf + INIT_DATA_BUF_SIZE;
 
-  if (dtd)
+  if (dtd) {
     parser->m_dtd = dtd;
-  else {
+#ifdef XML_DTD
+    parser->m_isParamEntity = XML_TRUE;
+#endif
+  } else {
     parser->m_dtd = dtdCreate(&parser->m_mem);
+#ifdef XML_DTD
+    parser->m_isParamEntity = XML_FALSE;
+#endif
     if (parser->m_dtd == NULL) {
       FREE(parser, parser->m_dataBuf);
       FREE(parser, parser->m_atts);
@@ -1148,7 +1154,6 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
   parser->m_parentParser = NULL;
   parser->m_parsingStatus.parsing = XML_INITIALIZED;
 #ifdef XML_DTD
-  parser->m_isParamEntity = XML_FALSE;
   parser->m_useForeignDTD = XML_FALSE;
   parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
 #endif
@@ -1406,7 +1411,6 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
        pointers in parser->m_dtd with ones that get destroyed with the external
        PE parser. This would leave those prefixes with dangling pointers.
     */
-    parser->m_isParamEntity = XML_TRUE;
     XmlPrologStateInitExternalEntity(&parser->m_prologState);
     parser->m_processor = externalParEntInitProcessor;
   }

CC @c01db33f

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions