Vulnerability: In libexpat before 2.2.8, crafted XML input could fool the parser into changing from DTD parsing to document parsing too early; a consecutive call to XML_GetCurrentLineNumber (or XML_GetCurrentColumnNumber) then resulted in a heap-based buffer over-read.
Vulnerability ID: CVE-2019-15903
CWE: CWE-125
Severity score: 2.9
Project: https://github.com/libexpat/libexpat
Published: 2019-09-04
File: expat/lib/xmlparse.c
Commit message: xmlparse.c: Deny internal entities closing the doctype
Commit author: Sebastian Pipping

Vulnerable version (before fix):
 static enum XML_Error initializeEncoding(XML_Parser parser); static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc,                                const char *s, const char *end, int tok,                                const char *next, const char **nextPtr,                                XML_Bool haveMore); static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity,                                             XML_Bool betweenDecl); static enum XML_Error doContent(XML_Parser parser, int startTagLevel, externalParEntProcessor(XML_Parser parser, const char *s, const char *end,    parser->m_processor = prologProcessor;   return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,                   (XML_Bool)! parser->m_parsingStatus.finalBuffer); }  static enum XML_Error PTRCALL prologProcessor(XML_Parser parser, const char *s, const char *end,   const char *next = s;   int tok = XmlPrologTok(parser->m_encoding, s, end, &next);   return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,                   (XML_Bool)! parser->m_parsingStatus.finalBuffer); }  static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,          int tok, const char *next, const char **nextPtr, XML_Bool haveMore) { #ifdef XML_DTD   static const XML_Char externalSubsetName[] = {ASCII_HASH, '\0'}; #endif /* XML_DTD */ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,       }       break;     case XML_ROLE_DOCTYPE_CLOSE:       if (parser->m_doctypeName) {         parser->m_startDoctypeDeclHandler(             parser->m_handlerArg, parser->m_doctypeName, parser->m_doctypeSysid, processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {     int tok         = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);     result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,                       tok, next, &next, XML_FALSE);   } else #endif /* XML_DTD */     result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding, internalEntityProcessor(XML_Parser parser, const char *s, const char *end,     int tok         = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);     result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,                       tok, next, &next, XML_FALSE);   } else #endif /* XML_DTD */     result = doContent(parser, openEntity->startTagLevel, internalEntityProcessor(XML_Parser parser, const char *s, const char *end,     parser->m_processor = prologProcessor;     tok = XmlPrologTok(parser->m_encoding, s, end, &next);     return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,                     (XML_Bool)! parser->m_parsingStatus.finalBuffer);   } else #endif /* XML_DTD */   {

Fixed version (after fix):
 static enum XML_Error initializeEncoding(XML_Parser parser); static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc,                                const char *s, const char *end, int tok,                                const char *next, const char **nextPtr,                                XML_Bool haveMore, XML_Bool allowClosingDoctype); static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity,                                             XML_Bool betweenDecl); static enum XML_Error doContent(XML_Parser parser, int startTagLevel, externalParEntProcessor(XML_Parser parser, const char *s, const char *end,    parser->m_processor = prologProcessor;   return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,                   (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE); }  static enum XML_Error PTRCALL prologProcessor(XML_Parser parser, const char *s, const char *end,   const char *next = s;   int tok = XmlPrologTok(parser->m_encoding, s, end, &next);   return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,                   (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE); }  static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,          int tok, const char *next, const char **nextPtr, XML_Bool haveMore,          XML_Bool allowClosingDoctype) { #ifdef XML_DTD   static const XML_Char externalSubsetName[] = {ASCII_HASH, '\0'}; #endif /* XML_DTD */ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,       }       break;     case XML_ROLE_DOCTYPE_CLOSE:       if (allowClosingDoctype != XML_TRUE) {         /* Must not close doctype from within expanded parameter entities */         return XML_ERROR_INVALID_TOKEN;       }        if (parser->m_doctypeName) {         parser->m_startDoctypeDeclHandler(             parser->m_handlerArg, parser->m_doctypeName, parser->m_doctypeSysid, processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {     int tok         = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);     result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,                       tok, next, &next, XML_FALSE, XML_FALSE);   } else #endif /* XML_DTD */     result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding, internalEntityProcessor(XML_Parser parser, const char *s, const char *end,     int tok         = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);     result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,                       tok, next, &next, XML_FALSE, XML_TRUE);   } else #endif /* XML_DTD */     result = doContent(parser, openEntity->startTagLevel, internalEntityProcessor(XML_Parser parser, const char *s, const char *end,     parser->m_processor = prologProcessor;     tok = XmlPrologTok(parser->m_encoding, s, end, &next);     return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,                     (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);   } else #endif /* XML_DTD */   {

Patch:
patch: @@ -401,7 +401,7 @@ static enum XML_Error initializeEncoding(XML_Parser parser);
 static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc,
                                const char *s, const char *end, int tok,
                                const char *next, const char **nextPtr,
-                               XML_Bool haveMore);
+                               XML_Bool haveMore, XML_Bool allowClosingDoctype);
 static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity,
                                             XML_Bool betweenDecl);
 static enum XML_Error doContent(XML_Parser parser, int startTagLevel,
@@ -4046,7 +4046,7 @@ externalParEntProcessor(XML_Parser parser, const char *s, const char *end,
 
   parser->m_processor = prologProcessor;
   return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
-                  (XML_Bool)! parser->m_parsingStatus.finalBuffer);
+                  (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
 }
 
 static enum XML_Error PTRCALL
@@ -4090,12 +4090,13 @@ prologProcessor(XML_Parser parser, const char *s, const char *end,
   const char *next = s;
   int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
   return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
-                  (XML_Bool)! parser->m_parsingStatus.finalBuffer);
+                  (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
 }
 
 static enum XML_Error
 doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
-         int tok, const char *next, const char **nextPtr, XML_Bool haveMore) {
+         int tok, const char *next, const char **nextPtr, XML_Bool haveMore,
+         XML_Bool allowClosingDoctype) {
 #ifdef XML_DTD
   static const XML_Char externalSubsetName[] = {ASCII_HASH, '\0'};
 #endif /* XML_DTD */
@@ -4271,6 +4272,11 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
       }
       break;
     case XML_ROLE_DOCTYPE_CLOSE:
+      if (allowClosingDoctype != XML_TRUE) {
+        /* Must not close doctype from within expanded parameter entities */
+        return XML_ERROR_INVALID_TOKEN;
+      }
+
       if (parser->m_doctypeName) {
         parser->m_startDoctypeDeclHandler(
             parser->m_handlerArg, parser->m_doctypeName, parser->m_doctypeSysid,
@@ -5174,7 +5180,7 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
     int tok
         = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
     result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
-                      tok, next, &next, XML_FALSE);
+                      tok, next, &next, XML_FALSE, XML_FALSE);
   } else
 #endif /* XML_DTD */
     result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding,
@@ -5217,7 +5223,7 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
     int tok
         = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
     result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
-                      tok, next, &next, XML_FALSE);
+                      tok, next, &next, XML_FALSE, XML_TRUE);
   } else
 #endif /* XML_DTD */
     result = doContent(parser, openEntity->startTagLevel,
@@ -5244,7 +5250,7 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
     parser->m_processor = prologProcessor;
     tok = XmlPrologTok(parser->m_encoding, s, end, &next);
     return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
-                    (XML_Bool)! parser->m_parsingStatus.finalBuffer);
+                    (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
   } else
 #endif /* XML_DTD */
   {
path: expat/lib/xmlparse.c