diff --git a/.gitignore b/.gitignore
index 6b6e15c..7e115b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
SOURCES/expat-2.2.5.tar.gz
/expat-2.2.5.tar.gz
+/expat-2.5.0.tar.gz
diff --git a/expat-2.2.5-Add-missing-validation-of-encoding.patch b/expat-2.2.5-Add-missing-validation-of-encoding.patch
deleted file mode 100644
index 8876367..0000000
--- a/expat-2.2.5-Add-missing-validation-of-encoding.patch
+++ /dev/null
@@ -1,200 +0,0 @@
-commit e8f285b522a907603501329e5b4212755f525fdf
-Author: Tomas Korbar
The functions in this section configure the built-in +- protection against various forms of +- billion laughs attacks.
+++@@ -2188,6 +2185,27 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p, + + + +XML_SetReparseDeferralEnabled
++/* Added in Expat 2.6.0. */ @@ -55,10 +79,10 @@ index efc19f4..95c33c7 100644The functions in this section either obtain state information from diff --git a/expat/doc/xmlwf.xml b/expat/doc/xmlwf.xml -index 5e2a4ae..6b719eb 100644 +index 9603abf..3d35393 100644 --- a/expat/doc/xmlwf.xml +++ b/expat/doc/xmlwf.xml -@@ -246,6 +246,16 @@ supports both. +@@ -313,6 +313,16 @@ supports both. @@ -75,57 +99,21 @@ index 5e2a4ae..6b719eb 100644
-diff --git a/expat/lib/.gitignore b/expat/lib/.gitignore -index 9c9cf88..cd5b24f 100644 ---- a/expat/lib/.gitignore -+++ b/expat/lib/.gitignore -@@ -1,7 +1,6 @@ - Makefile - .libs - *.lo --expat.h - Debug - Debug-w - Release -diff --git a/expat/lib/Makefile.am b/expat/lib/Makefile.am -index 5f2b71e..cef6558 100644 ---- a/expat/lib/Makefile.am -+++ b/expat/lib/Makefile.am -@@ -34,18 +34,25 @@ include_HEADERS = \ - expat_external.h - - lib_LTLIBRARIES = libexpat.la -+noinst_LTLIBRARIES = libexpatinternal.la - - libexpat_la_LDFLAGS = \ - -no-undefined \ - -version-info @LIBCURRENT@:@LIBREVISION@:@LIBAGE@ \ - -rpath $(libdir) - --libexpat_la_SOURCES = \ -- loadlibrary.c \ -+libexpat_la_SOURCES = -+ -+# This layer of indirection allows -+# the test suite to access internal symbols -+# despite compiling with -fvisibility=hidden -+libexpatinternal_la_SOURCES = \ - xmlparse.c \ - xmltok.c \ - xmlrole.c - -+libexpat_la_LIBADD = libexpatinternal.la -+ - doc_DATA = \ - ../AUTHORS \ - ../Changes diff --git a/expat/lib/expat.h b/expat/lib/expat.h -index 1f608c0..afe12c5 100644 +index 1c83563..842dd70 100644 --- a/expat/lib/expat.h +++ b/expat/lib/expat.h -@@ -1071,6 +1071,10 @@ XMLPARSEAPI(const XML_Feature *) - XML_GetFeatureList(void); +@@ -16,6 +16,7 @@ + Copyright (c) 2016 Thomas Beutlich + Copyright (c) 2017 Rhodri James + Copyright (c) 2022 Thijs Schreijer ++ Copyright (c) 2023 Sony Corporation / Snild Dolkow + Licensed under the MIT license: + Permission is hereby granted, free of charge, to any person obtaining +@@ -1050,6 +1051,10 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes); + #endif +/* Added in Expat 2.6.0. */ +XMLPARSEAPI(XML_Bool) @@ -135,63 +123,60 @@ index 1f608c0..afe12c5 100644 See http://semver.org. */ diff --git a/expat/lib/internal.h b/expat/lib/internal.h -index e33fdcb..8e3d566 100644 +index e09f533..e2709c8 100644 --- a/expat/lib/internal.h +++ b/expat/lib/internal.h -@@ -109,6 +109,7 @@ - # endif +@@ -31,6 +31,7 @@ + Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2018 Yury Gribov + Copyright (c) 2019 David Loffredo ++ Copyright (c) 2023 Sony Corporation / Snild Dolkow + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining +@@ -160,6 +161,9 @@ unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser); + const char *unsignedCharToPrintable(unsigned char c); #endif -+#include "expat.h" - - #ifdef __cplusplus - extern "C" { -@@ -119,6 +120,9 @@ void - _INTERNAL_trim_to_complete_utf8_characters(const char * from, const char ** fromLimRef); - - -+extern __attribute__ ((visibility ("hidden"))) XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c -+extern __attribute__ ((visibility ("hidden"))) unsigned int g_parseAttempts; // used for testing only ++extern XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c ++extern unsigned int g_parseAttempts; // used for testing only + #ifdef __cplusplus } #endif -diff --git a/expat/lib/libexpat.def b/expat/lib/libexpat.def -index d08f5b7..163870b 100644 ---- a/expat/lib/libexpat.def -+++ b/expat/lib/libexpat.def -@@ -75,4 +75,5 @@ EXPORTS - ; XML_GetAttributeInfo @66 - XML_SetHashSalt @67@ - ; added with version 2.2.5 -- _INTERNAL_trim_to_complete_utf8_characters @68@ -\ No newline at end of file -+ _INTERNAL_trim_to_complete_utf8_characters @68@ -+ XML_SetReparseDeferralEnabled @69 +diff --git a/expat/lib/libexpat.def.cmake b/expat/lib/libexpat.def.cmake +index cf434a2..3ff4d55 100644 +--- a/expat/lib/libexpat.def.cmake ++++ b/expat/lib/libexpat.def.cmake +@@ -77,3 +77,4 @@ EXPORTS + ; added with version 2.4.0 + @_EXPAT_COMMENT_DTD@ XML_SetBillionLaughsAttackProtectionActivationThreshold @69 + @_EXPAT_COMMENT_DTD@ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70 ++XML_SetReparseDeferralEnabled @71 diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c -index 3f765f7..488f63f 100644 +index b6c2eca..2ae64e9 100644 --- a/expat/lib/xmlparse.c +++ b/expat/lib/xmlparse.c -@@ -34,6 +34,7 @@ - # define _GNU_SOURCE 1 /* syscall prototype */ +@@ -73,6 +73,7 @@ + # endif #endif +#include #include - #include /* memset(), memcpy() */ + #include /* memset(), memcpy() */ #include -@@ -173,6 +174,8 @@ typedef char ICHAR; - #endif /* HAVE_BCOPY */ - #endif /* HAVE_MEMMOVE */ +@@ -196,6 +197,8 @@ typedef char ICHAR; + /* Do safe (NULL-aware) pointer arithmetic */ + #define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0) +#define EXPAT_MIN(a, b) (((a) < (b)) ? (a) : (b)) + #include "internal.h" #include "xmltok.h" #include "xmlrole.h" -@@ -544,6 +547,9 @@ parserInit(XML_Parser parser, const XML_Char *encodingName); - ? 0 \ - : ((*((pool)->ptr)++ = c), 1)) +@@ -602,6 +605,9 @@ static unsigned long getDebugLevel(const char *variableName, + ? 0 \ + : ((*((pool)->ptr)++ = c), 1)) +XML_Bool g_reparseDeferralEnabledDefault = XML_TRUE; // write ONLY in runtests.c +unsigned int g_parseAttempts = 0; // used for testing only @@ -199,7 +184,7 @@ index 3f765f7..488f63f 100644 struct XML_ParserStruct { /* The first member must be m_userData so that the XML_GetUserData macro works. */ -@@ -559,6 +565,9 @@ struct XML_ParserStruct { +@@ -617,6 +623,9 @@ struct XML_ParserStruct { const char *m_bufferLim; XML_Index m_parseEndByteIndex; const char *m_parseEndPtr; @@ -209,7 +194,7 @@ index 3f765f7..488f63f 100644 XML_Char *m_dataBuf; XML_Char *m_dataBufEnd; XML_StartElementHandler m_startElementHandler; -@@ -892,6 +901,48 @@ get_hash_secret_salt(XML_Parser parser) { +@@ -948,6 +957,47 @@ get_hash_secret_salt(XML_Parser parser) { return parser->m_hash_secret_salt; } @@ -226,7 +211,7 @@ index 3f765f7..488f63f 100644 + // ...but *do* try anyway if we're close to causing a reallocation. + size_t available_buffer + = EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer); -+#ifdef XML_CONTEXT_BYTES ++#if XML_CONTEXT_BYTES > 0 + available_buffer -= EXPAT_MIN(available_buffer, XML_CONTEXT_BYTES); +#endif + available_buffer @@ -254,11 +239,10 @@ index 3f765f7..488f63f 100644 + return ret; +} + -+ - static XML_Bool /* only valid for root parser */ - startParsing(XML_Parser parser) - { -@@ -1078,6 +1129,9 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) + static XML_Bool /* only valid for root parser */ + startParsing(XML_Parser parser) { + /* hash functions must be initialized before setContext() is called */ +@@ -1129,6 +1179,9 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { parser->m_bufferEnd = parser->m_buffer; parser->m_parseEndByteIndex = 0; parser->m_parseEndPtr = NULL; @@ -268,7 +252,7 @@ index 3f765f7..488f63f 100644 parser->m_declElementType = NULL; parser->m_declAttributeId = NULL; parser->m_declEntity = NULL; -@@ -1239,6 +1293,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, +@@ -1298,6 +1351,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, to worry which hash secrets each table has. */ unsigned long oldhash_secret_salt; @@ -276,15 +260,15 @@ index 3f765f7..488f63f 100644 /* Validate the oldParser parameter before we pull everything out of it */ if (oldParser == NULL) -@@ -1283,6 +1338,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, +@@ -1342,6 +1396,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, to worry which hash secrets each table has. */ oldhash_secret_salt = parser->m_hash_secret_salt; + oldReparseDeferralEnabled = parser->m_reparseDeferralEnabled; #ifdef XML_DTD - if (!context) -@@ -1336,6 +1392,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, + if (! context) +@@ -1394,6 +1449,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, parser->m_defaultExpandInternalEntities = oldDefaultExpandInternalEntities; parser->m_ns_triplets = oldns_triplets; parser->m_hash_secret_salt = oldhash_secret_salt; @@ -292,13 +276,13 @@ index 3f765f7..488f63f 100644 parser->m_parentParser = oldParser; #ifdef XML_DTD parser->m_paramEntityParsing = oldParamEntityParsing; -@@ -1833,52 +1890,8 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +@@ -1848,55 +1904,8 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { parser->m_parsingStatus.parsing = XML_PARSING; } - if (len == 0) { - parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; -- if (!isFinal) +- if (! isFinal) - return XML_STATUS_OK; - parser->m_positionPtr = parser->m_bufferPtr; - parser->m_parseEndPtr = parser->m_bufferEnd; @@ -307,7 +291,9 @@ index 3f765f7..488f63f 100644 - data are the final chunk of input, then we have to check them again - to detect errors based on that fact. - */ -- parser->m_errorCode = parser->m_processor(parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr); +- parser->m_errorCode +- = parser->m_processor(parser, parser->m_bufferPtr, +- parser->m_parseEndPtr, &parser->m_bufferPtr); - - if (parser->m_errorCode == XML_ERROR_NONE) { - switch (parser->m_parsingStatus.parsing) { @@ -324,7 +310,8 @@ index 3f765f7..488f63f 100644 - * - * LCOV_EXCL_START - */ -- XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, parser->m_bufferPtr, &parser->m_position); +- XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, +- parser->m_bufferPtr, &parser->m_position); - parser->m_positionPtr = parser->m_bufferPtr; - return XML_STATUS_SUSPENDED; - /* LCOV_EXCL_STOP */ @@ -346,20 +333,29 @@ index 3f765f7..488f63f 100644 const char *end; int nLeftOver; enum XML_Status result; -@@ -1893,7 +1906,7 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +@@ -1907,12 +1916,15 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { + parser->m_processor = errorProcessor; + return XML_STATUS_ERROR; + } ++ // though this isn't a buffer request, we assume that `len` is the app's ++ // preferred buffer fill size, and therefore save it here. ++ parser->m_lastBufferRequestSize = len; + parser->m_parseEndByteIndex += len; parser->m_positionPtr = s; parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; -- parser->m_errorCode = parser->m_processor(parser, s, parser->m_parseEndPtr = s + len, &end); -+ parser->m_errorCode = callProcessor(parser, s, parser->m_parseEndPtr = s + len, &end); + parser->m_errorCode +- = parser->m_processor(parser, s, parser->m_parseEndPtr = s + len, &end); ++ = callProcessor(parser, s, parser->m_parseEndPtr = s + len, &end); if (parser->m_errorCode != XML_ERROR_NONE) { parser->m_eventEndPtr = parser->m_eventPtr; -@@ -1920,22 +1933,25 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) - XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, end, &parser->m_position); +@@ -1939,23 +1951,25 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { + &parser->m_position); nLeftOver = s + len - end; if (nLeftOver) { -- if (parser->m_buffer == NULL || nLeftOver > parser->m_bufferLim - parser->m_buffer) { +- if (parser->m_buffer == NULL +- || nLeftOver > parser->m_bufferLim - parser->m_buffer) { - /* avoid _signed_ integer overflow */ - char *temp = NULL; - const int bytesToAllocate = (int)((unsigned)len * 2U); @@ -396,10 +392,10 @@ index 3f765f7..488f63f 100644 memcpy(parser->m_buffer, end, nLeftOver); } parser->m_bufferPtr = parser->m_buffer; -@@ -1947,15 +1963,14 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +@@ -1967,15 +1981,14 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { return result; } - #endif /* not defined XML_CONTEXT_BYTES */ + #endif /* not defined XML_CONTEXT_BYTES */ - else { - void *buff = XML_GetBuffer(parser, len); - if (buff == NULL) @@ -419,45 +415,61 @@ index 3f765f7..488f63f 100644 } enum XML_Status XMLCALL -@@ -1989,7 +2004,8 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) +@@ -2015,8 +2028,8 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) { parser->m_parseEndByteIndex += len; parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; -- parser->m_errorCode = parser->m_processor(parser, start, parser->m_parseEndPtr, &parser->m_bufferPtr); +- parser->m_errorCode = parser->m_processor( +- parser, start, parser->m_parseEndPtr, &parser->m_bufferPtr); + parser->m_errorCode = callProcessor(parser, start, parser->m_parseEndPtr, + &parser->m_bufferPtr); if (parser->m_errorCode != XML_ERROR_NONE) { parser->m_eventEndPtr = parser->m_eventPtr; -@@ -2035,8 +2051,14 @@ XML_GetBuffer(XML_Parser parser, int len) - default: ; +@@ -2061,10 +2074,14 @@ XML_GetBuffer(XML_Parser parser, int len) { + default:; } - if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) { +-#ifdef XML_CONTEXT_BYTES + // whether or not the request succeeds, `len` seems to be the app's preferred + // buffer fill size; remember it. + parser->m_lastBufferRequestSize = len; + if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd) + || parser->m_buffer == NULL) { -+#ifdef XML_CONTEXT_BYTES ++#if XML_CONTEXT_BYTES > 0 int keep; +-#endif /* defined XML_CONTEXT_BYTES */ +#endif /* XML_CONTEXT_BYTES > 0 */ /* Do not invoke signed arithmetic overflow: */ int neededSize = (int)((unsigned)len + (unsigned)EXPAT_SAFE_PTR_DIFF( -@@ -2055,8 +2077,9 @@ XML_GetBuffer(XML_Parser parser, int len) +@@ -2073,7 +2090,7 @@ XML_GetBuffer(XML_Parser parser, int len) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } +-#ifdef XML_CONTEXT_BYTES ++#if XML_CONTEXT_BYTES > 0 + keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer); + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; +@@ -2083,10 +2100,11 @@ XML_GetBuffer(XML_Parser parser, int len) { return NULL; } neededSize += keep; +-#endif /* defined XML_CONTEXT_BYTES */ - if (neededSize - <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) { +-#ifdef XML_CONTEXT_BYTES ++#endif /* XML_CONTEXT_BYTES > 0 */ + if (parser->m_buffer && parser->m_bufferPtr + && neededSize + <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) { - #ifdef XML_CONTEXT_BYTES ++#if XML_CONTEXT_BYTES > 0 if (keep < EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)) { int offset -@@ -2070,19 +2093,17 @@ XML_GetBuffer(XML_Parser parser, int len) + = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer) +@@ -2099,19 +2117,17 @@ XML_GetBuffer(XML_Parser parser, int len) { parser->m_bufferPtr -= offset; } #else @@ -485,7 +497,7 @@ index 3f765f7..488f63f 100644 if (bufferSize == 0) bufferSize = INIT_BUFFER_SIZE; do { -@@ -2099,7 +2120,7 @@ XML_GetBuffer(XML_Parser parser, int len) +@@ -2128,7 +2144,7 @@ XML_GetBuffer(XML_Parser parser, int len) { return NULL; } parser->m_bufferLim = newBuf + bufferSize; @@ -494,18 +506,27 @@ index 3f765f7..488f63f 100644 if (parser->m_bufferPtr) { memcpy(newBuf, &parser->m_bufferPtr[-keep], EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) -@@ -2182,7 +2203,7 @@ XML_ResumeParser(XML_Parser parser) +@@ -2158,7 +2174,7 @@ XML_GetBuffer(XML_Parser parser, int len) { + parser->m_bufferEnd = newBuf; + } + parser->m_bufferPtr = parser->m_buffer = newBuf; +-#endif /* not defined XML_CONTEXT_BYTES */ ++#endif /* XML_CONTEXT_BYTES > 0 */ + } + parser->m_eventPtr = parser->m_eventEndPtr = NULL; + parser->m_positionPtr = NULL; +@@ -2208,7 +2224,7 @@ XML_ResumeParser(XML_Parser parser) { } parser->m_parsingStatus.parsing = XML_PARSING; -- parser->m_errorCode = parser->m_processor(parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr); -+ parser->m_errorCode = callProcessor(parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr); +- parser->m_errorCode = parser->m_processor( ++ parser->m_errorCode = callProcessor( + parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr); if (parser->m_errorCode != XML_ERROR_NONE) { - parser->m_eventEndPtr = parser->m_eventPtr; -@@ -2504,6 +2525,15 @@ XML_GetFeatureList(void) - return features; +@@ -2561,6 +2577,15 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold( } + #endif /* XML_DTD */ +XML_Bool XMLCALL +XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled) { @@ -519,7 +540,7 @@ index 3f765f7..488f63f 100644 /* Initially tag->rawName always points into the parse buffer; for those TAG instances opened while the current parse buffer was processed, and not yet closed, we need to store tag->rawName in a more -@@ -4440,16 +4470,17 @@ entityValueInitProcessor(XML_Parser parser, +@@ -4482,15 +4507,15 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, parser->m_processor = entityValueProcessor; return entityValueProcessor(parser, next, end, nextPtr); } @@ -529,8 +550,6 @@ index 3f765f7..488f63f 100644 - tokens, but not for the BOM - we would rather like to skip it; - then, when this routine is entered the next time, XmlPrologTok will - return XML_TOK_INVALID, since the BOM is still in the buffer -- */ -- else if (tok == XML_TOK_BOM && next == end && !parser->m_parsingStatus.finalBuffer) { + /* XmlPrologTok has now set the encoding based on the BOM it found, and we + must move s and nextPtr forward to consume the BOM. + @@ -538,35 +557,28 @@ index 3f765f7..488f63f 100644 + would leave the BOM in the buffer and return. On the next call to this + function, our XmlPrologTok call would return XML_TOK_INVALID, since it + is not valid to have multiple BOMs. -+ */ + */ +- else if (tok == XML_TOK_BOM && next == end +- && ! parser->m_parsingStatus.finalBuffer) { + else if (tok == XML_TOK_BOM) { + # ifdef XML_DTD + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { +@@ -4500,7 +4525,7 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, + # endif + *nextPtr = next; - return XML_ERROR_NONE; + s = next; } /* If we get this token, we have the start of what might be a normal tag, but not a declaration (i.e. it doesn't begin with -diff --git a/expat/tests/Makefile.am b/expat/tests/Makefile.am -index 742ed43..4fe0e23 100644 ---- a/expat/tests/Makefile.am -+++ b/expat/tests/Makefile.am -@@ -52,8 +52,8 @@ runtests_SOURCES = \ - runtestspp_SOURCES = \ - runtestspp.cpp - --runtests_LDADD = libruntests.a ../lib/libexpat.la --runtestspp_LDADD = libruntests.a ../lib/libexpat.la -+runtests_LDADD = libruntests.a ../lib/libexpatinternal.la -+runtestspp_LDADD = libruntests.a ../lib/libexpatinternal.la - - EXTRA_DIST = \ - chardata.h \ diff --git a/expat/tests/minicheck.c b/expat/tests/minicheck.c -index be1e37e..6c694a0 100644 +index 1c65748..f383380 100644 --- a/expat/tests/minicheck.c +++ b/expat/tests/minicheck.c -@@ -209,6 +209,21 @@ srunner_run_all(SRunner *runner, int verbosity) - } +@@ -208,6 +208,21 @@ srunner_run_all(SRunner *runner, int verbosity) { + } } +void @@ -585,17 +597,18 @@ index be1e37e..6c694a0 100644 +} + void - _fail_unless(int UNUSED_P(condition), const char *UNUSED_P(file), int UNUSED_P(line), const char *msg) - { + _fail_unless(int condition, const char *file, int line, const char *msg) { + /* Always print the error message so it isn't lost. In this case, diff --git a/expat/tests/minicheck.h b/expat/tests/minicheck.h -index a2f57dd..894895a 100644 +index cc1f835..032b54e 100644 --- a/expat/tests/minicheck.h +++ b/expat/tests/minicheck.h -@@ -60,7 +60,13 @@ extern "C" { - { - #define END_TEST } } +@@ -64,7 +64,14 @@ extern "C" { + } \ + } --#define fail(msg) _fail_unless(0, __FILE__, __LINE__, msg) +-#define fail(msg) _fail_unless(0, __FILE__, __LINE__, msg) ++ +# define fail(msg) _fail(__FILE__, __LINE__, msg) +# define assert_true(cond) \ + do { \ @@ -606,7 +619,7 @@ index a2f57dd..894895a 100644 typedef void (*tcase_setup_function)(void); typedef void (*tcase_teardown_function)(void); -@@ -101,6 +107,11 @@ void _check_set_test_info(char const *function, +@@ -103,6 +110,11 @@ void _check_set_test_info(char const *function, char const *filename, * Prototypes for the actual implementation. */ @@ -619,48 +632,48 @@ index a2f57dd..894895a 100644 Suite *suite_create(const char *name); TCase *tcase_create(const char *name); diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c -index f58f794..486073f 100644 +index 915fa52..941f61d 100644 --- a/expat/tests/runtests.c +++ b/expat/tests/runtests.c -@@ -46,6 +46,7 @@ - #include /* ptrdiff_t */ +@@ -54,6 +54,7 @@ #include #include + #include /* intptr_t uint64_t */ +#include #if ! defined(__cplusplus) - # if defined(_MSC_VER) && (_MSC_VER <= 1700) -@@ -1112,7 +1113,7 @@ START_TEST(test_column_number_after_parse) - const char *text = " "; - XML_Size colno; + # include +@@ -1071,7 +1072,7 @@ START_TEST(test_column_number_after_parse) { + const char *text = " "; + XML_Size colno; -- if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) -+ if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); - colno = XML_GetCurrentColumnNumber(parser); - if (colno != 11) { -@@ -2769,7 +2770,7 @@ START_TEST(test_default_current) - if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), - XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -- CharData_CheckXMLChars(&storage, XCS("DCDCDCDCDCDD")); -+ CharData_CheckXMLChars(&storage, XCS("DCDCDCDD")); +- if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE) ++ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(g_parser); + colno = XML_GetCurrentColumnNumber(g_parser); +@@ -2582,7 +2583,7 @@ START_TEST(test_default_current) { + if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(g_parser); +- CharData_CheckXMLChars(&storage, XCS("DCDCDCDCDCDD")); ++ CharData_CheckXMLChars(&storage, XCS("DCDCDCDD")); - /* Again, without the defaulting */ - XML_ParserReset(parser, NULL); -@@ -2780,7 +2781,7 @@ START_TEST(test_default_current) - if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), - XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -- CharData_CheckXMLChars(&storage, XCS("DcccccD")); -+ CharData_CheckXMLChars(&storage, XCS("DcccD")); + /* Again, without the defaulting */ + XML_ParserReset(g_parser, NULL); +@@ -2593,7 +2594,7 @@ START_TEST(test_default_current) { + if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(g_parser); +- CharData_CheckXMLChars(&storage, XCS("DcccccD")); ++ CharData_CheckXMLChars(&storage, XCS("DcccD")); - /* Now with an internal entity to complicate matters */ - XML_ParserReset(parser, NULL); -@@ -4216,6 +4217,19 @@ END_TEST + /* Now with an internal entity to complicate matters */ + XML_ParserReset(g_parser, NULL); +@@ -3946,6 +3947,19 @@ START_TEST(test_get_buffer_3_overflow) { + END_TEST #endif // defined(XML_CONTEXT_BYTES) - +START_TEST(test_getbuffer_allocates_on_zero_len) { + for (int first_len = 1; first_len >= 0; first_len--) { + XML_Parser parser = XML_ParserCreate(NULL); @@ -675,9 +688,9 @@ index f58f794..486073f 100644 +END_TEST + /* Test position information macros */ - START_TEST(test_byte_info_at_end) - { -@@ -6747,6 +6761,12 @@ START_TEST(test_utf8_in_start_tags) { + START_TEST(test_byte_info_at_end) { + const char *text = " "; +@@ -6205,6 +6219,12 @@ START_TEST(test_utf8_in_start_tags) { char doc[1024]; size_t failCount = 0; @@ -690,7 +703,7 @@ index f58f794..486073f 100644 for (; i < sizeof(cases) / sizeof(cases[0]); i++) { size_t j = 0; for (; j < sizeof(atNameStart) / sizeof(atNameStart[0]); j++) { -@@ -7352,6 +7372,609 @@ START_TEST(test_empty_element_abort) +@@ -6830,6 +6850,613 @@ START_TEST(test_nested_entity_suspend) { } END_TEST @@ -871,7 +884,8 @@ index f58f794..486073f 100644 +}; + +static void -+element_decl_counter(void *userData, const XML_Char *UNUSED_P(name), XML_Content *model) { ++element_decl_counter(void *userData, const XML_Char *name, XML_Content *model) { ++ UNUSED_P(name); + struct element_decl_data *testdata = (struct element_decl_data *)userData; + testdata->count += 1; + XML_FreeContentModel(testdata->parser, model); @@ -879,8 +893,11 @@ index f58f794..486073f 100644 + +static int +external_inherited_parser(XML_Parser p, const XML_Char *context, -+ const XML_Char *UNUSED_P(base), const XML_Char *UNUSED_P(systemId), -+ const XML_Char *UNUSED_P(publicId)) { ++ const XML_Char *base, const XML_Char *systemId, ++ const XML_Char *publicId) { ++ UNUSED_P(base); ++ UNUSED_P(systemId); ++ UNUSED_P(publicId); + const char *const pre = "\n"; + const char *const start = "\n"; @@ -1300,79 +1317,73 @@ index f58f794..486073f 100644 /* * Namespaces tests. */ -@@ -7435,13 +8058,13 @@ START_TEST(test_return_ns_triplet) - if (_XML_Parse_SINGLE_BYTES(parser, text, strlen(text), - XML_FALSE) == XML_STATUS_ERROR) - xml_failure(parser); -- if (!triplet_start_flag) -- fail("triplet_start_checker not invoked"); - /* Check that unsetting "return triplets" fails while still parsing */ - XML_SetReturnNSTriplet(parser, XML_FALSE); - if (_XML_Parse_SINGLE_BYTES(parser, epilog, strlen(epilog), - XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -+ if (!triplet_start_flag) -+ fail("triplet_start_checker not invoked"); - if (!triplet_end_flag) - fail("triplet_end_checker not invoked"); - if (dummy_handler_flags != (DUMMY_START_NS_DECL_HANDLER_FLAG | -@@ -12476,6 +13099,7 @@ make_suite(void) +@@ -6902,13 +7529,13 @@ START_TEST(test_return_ns_triplet) { + if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE) + == XML_STATUS_ERROR) + xml_failure(g_parser); +- if (! triplet_start_flag) +- fail("triplet_start_checker not invoked"); + /* Check that unsetting "return triplets" fails while still parsing */ + XML_SetReturnNSTriplet(g_parser, XML_FALSE); + if (_XML_Parse_SINGLE_BYTES(g_parser, epilog, (int)strlen(epilog), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(g_parser); ++ if (! triplet_start_flag) ++ fail("triplet_start_checker not invoked"); + if (! triplet_end_flag) + fail("triplet_end_checker not invoked"); + if (dummy_handler_flags +@@ -12219,6 +12846,7 @@ make_suite(void) { #if defined(XML_CONTEXT_BYTES) - tcase_add_test(tc_basic, test_get_buffer_3_overflow); + tcase_add_test(tc_basic, test_get_buffer_3_overflow); #endif -+ tcase_add_test(tc_basic, test_getbuffer_allocates_on_zero_len); - tcase_add_test(tc_basic, test_byte_info_at_end); - tcase_add_test(tc_basic, test_byte_info_at_error); - tcase_add_test(tc_basic, test_byte_info_at_cdata); -@@ -12588,6 +13212,14 @@ make_suite(void) - tcase_add_test(tc_basic, test_bad_notation); - tcase_add_test(tc_basic, test_default_doctype_handler); - tcase_add_test(tc_basic, test_empty_element_abort); -+ tcase_add_test(tc_basic, test_big_tokens_take_linear_time); -+ tcase_add_test(tc_basic, test_set_reparse_deferral); -+ tcase_add_test(tc_basic, test_reparse_deferral_is_inherited); -+ tcase_add_test(tc_basic, test_set_reparse_deferral_on_null_parser); -+ tcase_add_test(tc_basic, test_set_reparse_deferral_on_the_fly); -+ tcase_add_test(tc_basic, test_set_bad_reparse_option); -+ tcase_add_test(tc_basic, test_bypass_heuristic_when_close_to_bufsize); -+ tcase_add_test(tc_basic, test_varying_buffer_fills); - - suite_add_tcase(s, tc_namespace); - tcase_add_checked_fixture(tc_namespace, ++ tcase_add_test(tc_basic, test_getbuffer_allocates_on_zero_len); + tcase_add_test(tc_basic, test_byte_info_at_end); + tcase_add_test(tc_basic, test_byte_info_at_error); + tcase_add_test(tc_basic, test_byte_info_at_cdata); +@@ -12337,7 +12965,14 @@ make_suite(void) { + tcase_add_test__ifdef_xml_dtd(tc_basic, + test_pool_integrity_with_unfinished_attr); + tcase_add_test(tc_basic, test_nested_entity_suspend); +- ++ tcase_add_test(tc_basic, test_big_tokens_take_linear_time); ++ tcase_add_test(tc_basic, test_set_reparse_deferral); ++ tcase_add_test(tc_basic, test_reparse_deferral_is_inherited); ++ tcase_add_test(tc_basic, test_set_reparse_deferral_on_null_parser); ++ tcase_add_test(tc_basic, test_set_reparse_deferral_on_the_fly); ++ tcase_add_test(tc_basic, test_set_bad_reparse_option); ++ tcase_add_test(tc_basic, test_bypass_heuristic_when_close_to_bufsize); ++ tcase_add_test(tc_basic, test_varying_buffer_fills); + suite_add_tcase(s, tc_namespace); + tcase_add_checked_fixture(tc_namespace, namespace_setup, namespace_teardown); + tcase_add_test(tc_namespace, test_return_ns_triplet); diff --git a/expat/xmlwf/xmlwf.c b/expat/xmlwf/xmlwf.c -index 82d028e..cd26919 100644 +index 471f2a2..7c62919 100644 --- a/expat/xmlwf/xmlwf.c +++ b/expat/xmlwf/xmlwf.c -@@ -35,6 +35,7 @@ - #include - #include - #include -+#include +@@ -914,6 +914,9 @@ usage(const XML_Char *prog, int rc) { + T(" -a FACTOR set maximum tolerated [a]mplification factor (default: 100.0)\n") + T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB)\n") + T("\n") ++ T("reparse deferral:\n") ++ T(" -q disable reparse deferral, and allow [q]uadratic parse runtime with large tokens\n") ++ T("\n") + T("info arguments:\n") + T(" -h show this [h]elp message and exit\n") + T(" -v show program's [v]ersion number and exit\n") +@@ -967,6 +970,8 @@ tmain(int argc, XML_Char **argv) { + unsigned long long attackThresholdBytes; + XML_Bool attackThresholdGiven = XML_FALSE; - #include "expat.h" - #include "codepage.h" -@@ -892,7 +893,7 @@ static void - usage(const XML_Char *prog, int rc) - { - ftprintf(stderr, -- T("usage: %s [-s] [-n] [-p] [-x] [-e encoding] [-w] [-d output-dir] [-c] [-m] [-r] [-t] [-N] [file ...]\n"), prog); -+ T("usage: %s [-s] [-n] [-p] [-x] [-e encoding] [-w] [-d output-dir] [-c] [-m] [-r] [-t] [-N] [-q] [file ...]\n"), prog); - exit(rc); - } - -@@ -917,6 +918,8 @@ tmain(int argc, XML_Char **argv) - XML_PARAM_ENTITY_PARSING_NEVER; - int useStdin = 0; - XmlwfUserData userData = { NULL, NULL, NULL }; + XML_Bool disableDeferral = XML_FALSE; + - - #ifdef _MSC_VER - _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF); -@@ -1003,6 +1006,11 @@ tmain(int argc, XML_Char **argv) - case T('v'): - showVersion(argv[0]); - return 0; + int exitCode = XMLWF_EXIT_SUCCESS; + enum XML_ParamEntityParsing paramEntityParsing + = XML_PARAM_ENTITY_PARSING_NEVER; +@@ -1089,6 +1094,11 @@ tmain(int argc, XML_Char **argv) { + #endif + break; + } + case T('q'): { + disableDeferral = XML_TRUE; + j++; @@ -1381,8 +1392,8 @@ index 82d028e..cd26919 100644 case T('\0'): if (j > 1) { i++; -@@ -1033,6 +1041,16 @@ tmain(int argc, XML_Char **argv) - exit(1); +@@ -1134,6 +1144,16 @@ tmain(int argc, XML_Char **argv) { + #endif } + if (disableDeferral) { @@ -1391,13 +1402,28 @@ index 82d028e..cd26919 100644 + // This prevents tperror(..) from reporting misleading "[..]: Success" + errno = EINVAL; + tperror(T("Failed to disable reparse deferral")); -+ exit(1); ++ exit(XMLWF_EXIT_INTERNAL_ERROR); + } + } + if (requireStandalone) XML_SetNotStandaloneHandler(parser, notStandalone); XML_SetParamEntityParsing(parser, paramEntityParsing); +diff --git a/expat/xmlwf/xmlwf_helpgen.py b/expat/xmlwf/xmlwf_helpgen.py +index c2a527f..1bd0a0a 100755 +--- a/expat/xmlwf/xmlwf_helpgen.py ++++ b/expat/xmlwf/xmlwf_helpgen.py +@@ -81,6 +81,10 @@ billion_laughs.add_argument('-a', metavar='FACTOR', + help='set maximum tolerated [a]mplification factor (default: 100.0)') + billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB)') + ++reparse_deferral = parser.add_argument_group('reparse deferral') ++reparse_deferral.add_argument('-q', metavar='FACTOR', ++ help='disable reparse deferral, and allow [q]uadratic parse runtime with large tokens') ++ + parser.add_argument('files', metavar='FILE', nargs='*', help='file to process (default: STDIN)') + + info = parser.add_argument_group('info arguments') diff --git a/testdata/largefiles/aaaaaa_attr.xml b/testdata/largefiles/aaaaaa_attr.xml new file mode 100644 index 0000000..66e3d25 diff --git a/expat-2.5.0-CVE-2024-28757.patch b/expat-2.5.0-CVE-2024-28757.patch new file mode 100644 index 0000000..0ac0f9e --- /dev/null +++ b/expat-2.5.0-CVE-2024-28757.patch @@ -0,0 +1,172 @@ +commit cd3b344e0dbd19a812d0b4f34f9d089ed7c5c411 +Author: Tomas Korbar +Date: Tue Mar 19 15:12:18 2024 +0100 + + Fix CVE-2024-28757 + + Upstream PRs #841 and #842 + +diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c +index 2ae64e9..0896b16 100644 +--- a/expat/lib/xmlparse.c ++++ b/expat/lib/xmlparse.c +@@ -6164,7 +6164,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, + dtd->keepProcessing = dtd->standalone; + goto endEntityValue; + } +- if (entity->open) { ++ if (entity->open || (entity == parser->m_declEntity)) { + if (enc == parser->m_encoding) + parser->m_eventPtr = entityTextPtr; + result = XML_ERROR_RECURSIVE_ENTITY_REF; +@@ -7680,6 +7680,8 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { + + static float + accountingGetCurrentAmplification(XML_Parser rootParser) { ++ // 1.........1.........12 => 22 ++ const size_t lenOfShortestInclude = sizeof("") - 1; + const XmlBigCount countBytesOutput + = rootParser->m_accounting.countBytesDirect + + rootParser->m_accounting.countBytesIndirect; +@@ -7687,7 +7689,9 @@ accountingGetCurrentAmplification(XML_Parser rootParser) { + = rootParser->m_accounting.countBytesDirect + ? (countBytesOutput + / (float)(rootParser->m_accounting.countBytesDirect)) +- : 1.0f; ++ : ((lenOfShortestInclude ++ + rootParser->m_accounting.countBytesIndirect) ++ / (float)lenOfShortestInclude); + assert(! rootParser->m_parentParser); + return amplificationFactor; + } +diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c +index 941f61d..93adc45 100644 +--- a/expat/tests/runtests.c ++++ b/expat/tests/runtests.c +@@ -1788,6 +1788,48 @@ START_TEST(test_wfc_no_recursive_entity_refs) { + } + END_TEST + ++START_TEST(test_recursive_external_parameter_entity_2) { ++ struct TestCase { ++ const char *doc; ++ enum XML_Status expectedStatus; ++ }; ++ ++ struct TestCase cases[] = { ++ {"", XML_STATUS_ERROR}, ++ {"" ++ "", ++ XML_STATUS_ERROR}, ++ {"" ++ "", ++ XML_STATUS_OK}, ++ {"", XML_STATUS_OK}, ++ }; ++ ++ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { ++ const char *const doc = cases[i].doc; ++ const enum XML_Status expectedStatus = cases[i].expectedStatus; ++ ++ XML_Parser parser = XML_ParserCreate(NULL); ++ assert_true(parser != NULL); ++ ++ XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL); ++ assert_true(ext_parser != NULL); ++ ++ const enum XML_Status actualStatus ++ = _XML_Parse_SINGLE_BYTES(ext_parser, doc, (int)strlen(doc), XML_TRUE); ++ ++ assert_true(actualStatus == expectedStatus); ++ if (actualStatus != XML_STATUS_OK) { ++ assert_true(XML_GetErrorCode(ext_parser) ++ == XML_ERROR_RECURSIVE_ENTITY_REF); ++ } ++ ++ XML_ParserFree(ext_parser); ++ XML_ParserFree(parser); ++ } ++} ++END_TEST ++ + /* Test incomplete external entities are faulted */ + START_TEST(test_ext_entity_invalid_parse) { + const char *text = "" ++ // (22) that is used in function accountingGetCurrentAmplification in ++ // xmlparse.c. ++ // 1.........1.........1.........1.........1..4 => 44 ++ const char doc[] = ""; ++ const int docLen = (int)sizeof(doc) - 1; ++ const float maximumToleratedAmplification = 2.0f; ++ ++ struct TestCase { ++ int offsetOfThreshold; ++ enum XML_Status expectedStatus; ++ }; ++ ++ struct TestCase cases[] = { ++ {-2, XML_STATUS_ERROR}, {-1, XML_STATUS_ERROR}, {0, XML_STATUS_ERROR}, ++ {+1, XML_STATUS_OK}, {+2, XML_STATUS_OK}, ++ }; ++ ++ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { ++ const int offsetOfThreshold = cases[i].offsetOfThreshold; ++ const enum XML_Status expectedStatus = cases[i].expectedStatus; ++ const unsigned long long activationThresholdBytes ++ = docLen + offsetOfThreshold; ++ ++ XML_Parser parser = XML_ParserCreate(NULL); ++ assert_true(parser != NULL); ++ ++ assert_true(XML_SetBillionLaughsAttackProtectionMaximumAmplification( ++ parser, maximumToleratedAmplification) ++ == XML_TRUE); ++ assert_true(XML_SetBillionLaughsAttackProtectionActivationThreshold( ++ parser, activationThresholdBytes) ++ == XML_TRUE); ++ ++ XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL); ++ assert_true(ext_parser != NULL); ++ ++ const enum XML_Status actualStatus ++ = _XML_Parse_SINGLE_BYTES(ext_parser, doc, docLen, XML_TRUE); ++ ++ assert_true(actualStatus == expectedStatus); ++ if (actualStatus != XML_STATUS_OK) { ++ assert_true(XML_GetErrorCode(ext_parser) ++ == XML_ERROR_AMPLIFICATION_LIMIT_BREACH); ++ } ++ ++ XML_ParserFree(ext_parser); ++ XML_ParserFree(parser); ++ } ++} ++END_TEST ++ + #endif // defined(XML_DTD) + + static Suite * +@@ -12871,6 +12967,8 @@ make_suite(void) { + tcase_add_test__ifdef_xml_dtd(tc_basic, test_skipped_parameter_entity); + tcase_add_test__ifdef_xml_dtd(tc_basic, + test_recursive_external_parameter_entity); ++ tcase_add_test__ifdef_xml_dtd(tc_basic, ++ test_recursive_external_parameter_entity_2); + tcase_add_test(tc_basic, test_undefined_ext_entity_in_external_dtd); + tcase_add_test(tc_basic, test_suspend_xdecl); + tcase_add_test(tc_basic, test_abort_epilog); +@@ -13120,6 +13218,7 @@ make_suite(void) { + tcase_add_test(tc_accounting, test_accounting_precision); + tcase_add_test(tc_accounting, test_billion_laughs_attack_protection_api); + tcase_add_test(tc_accounting, test_helper_unsigned_char_to_printable); ++ tcase_add_test(tc_accounting, test_amplification_isolated_external_parser); + #endif + + return s; diff --git a/expat-2.2.5-CVE-2024-45490.patch b/expat-2.5.0-CVE-2024-45490.patch similarity index 78% rename from expat-2.2.5-CVE-2024-45490.patch rename to expat-2.5.0-CVE-2024-45490.patch index 384797b..044f6c5 100644 --- a/expat-2.2.5-CVE-2024-45490.patch +++ b/expat-2.5.0-CVE-2024-45490.patch @@ -1,16 +1,16 @@ -commit 3c1a64705b5662c5b78f4aa5a5acc7a59c477094 +commit 05d87eb116ddde35bfa4e4c1d2ec7bcbda38c09b Author: Tomas Korbar -Date: Wed Sep 11 15:03:05 2024 +0200 +Date: Wed Sep 11 13:48:58 2024 +0200 Fix CVE-2024-45490 https://github.com/libexpat/libexpat/pull/890 diff --git a/expat/doc/reference.html b/expat/doc/reference.html -index 95c33c7..08cf9b0 100644 +index a10f3cb..d618bd8 100644 --- a/expat/doc/reference.html +++ b/expat/doc/reference.html -@@ -1039,7 +1039,9 @@ containing part (or perhaps all) of the document. The number of bytes of s +@@ -1098,7 +1098,9 @@ containing part (or perhaps all) of the document. The number of bytes of s that are part of the document is indicated by len. This means thatsdoesn't have to be null terminated. It also means that iflenis larger than the number of bytes in the block of @@ -21,7 +21,7 @@ index 95c33c7..08cf9b0 100644isFinalparameter informs the parser that this is the last piece of the document. Frequently, the last piece is empty (i.e.lenis zero.) -@@ -1054,11 +1056,17 @@ XML_ParseBuffer(XML_Parser p, +@@ -1114,11 +1116,17 @@ XML_ParseBuffer(XML_Parser p, int isFinal);@@ -38,12 +38,12 @@ index 95c33c7..08cf9b0 100644 +-+XML_GetBuffer
diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c -index 488f63f..c3c1af9 100644 +index 0896b16..f54e258 100644 --- a/expat/lib/xmlparse.c +++ b/expat/lib/xmlparse.c -@@ -1981,6 +1981,12 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) +@@ -1998,6 +1998,12 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) { if (parser == NULL) return XML_STATUS_ERROR; @@ -57,10 +57,10 @@ index 488f63f..c3c1af9 100644 case XML_SUSPENDED: parser->m_errorCode = XML_ERROR_SUSPENDED; diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c -index 486073f..6a3e09a 100644 +index 93adc45..ed88f9f 100644 --- a/expat/tests/runtests.c +++ b/expat/tests/runtests.c -@@ -4083,6 +4083,57 @@ START_TEST(test_empty_parse) +@@ -3856,6 +3856,57 @@ START_TEST(test_empty_parse) { } END_TEST @@ -117,13 +117,13 @@ index 486073f..6a3e09a 100644 + /* Test odd corners of the XML_GetBuffer interface */ static enum XML_Status - get_feature(enum XML_FeatureEnum feature_id, long *presult) -@@ -13094,6 +13145,8 @@ make_suite(void) - tcase_add_test(tc_basic, test_user_parameters); - tcase_add_test(tc_basic, test_ext_entity_ref_parameter); - tcase_add_test(tc_basic, test_empty_parse); -+ tcase_add_test(tc_basic, test_negative_len_parse); -+ tcase_add_test(tc_basic, test_negative_len_parse_buffer); - tcase_add_test(tc_basic, test_get_buffer_1); - tcase_add_test(tc_basic, test_get_buffer_2); + get_feature(enum XML_FeatureEnum feature_id, long *presult) { +@@ -12937,6 +12988,8 @@ make_suite(void) { + tcase_add_test__ifdef_xml_dtd(tc_basic, test_user_parameters); + tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_ref_parameter); + tcase_add_test(tc_basic, test_empty_parse); ++ tcase_add_test(tc_basic, test_negative_len_parse); ++ tcase_add_test(tc_basic, test_negative_len_parse_buffer); + tcase_add_test(tc_basic, test_get_buffer_1); + tcase_add_test(tc_basic, test_get_buffer_2); #if defined(XML_CONTEXT_BYTES) diff --git a/expat-2.5.0-CVE-2024-45491.patch b/expat-2.5.0-CVE-2024-45491.patch new file mode 100644 index 0000000..0f69dd7 --- /dev/null +++ b/expat-2.5.0-CVE-2024-45491.patch @@ -0,0 +1,31 @@ +From 8e439a9947e9dc80a395c0c7456545d8d9d9e421 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping+Date: Mon, 19 Aug 2024 22:34:13 +0200 +Subject: [PATCH] lib: Detect integer overflow in dtdCopy + +Reported by TaiYou +--- + expat/lib/xmlparse.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c +index 91682c188..e2327bdcf 100644 +--- a/expat/lib/xmlparse.c ++++ b/expat/lib/xmlparse.c +@@ -7016,6 +7016,16 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, + if (! newE) + return 0; + if (oldE->nDefaultAtts) { ++ /* Detect and prevent integer overflow. ++ * The preprocessor guard addresses the "always false" warning ++ * from -Wtype-limits on platforms where ++ * sizeof(int) < sizeof(size_t), e.g. on x86_64. */ ++#if UINT_MAX >= SIZE_MAX ++ if ((size_t)oldE->nDefaultAtts ++ > ((size_t)(-1) / sizeof(DEFAULT_ATTRIBUTE))) { ++ return 0; ++ } ++#endif + newE->defaultAtts + = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + if (! newE->defaultAtts) { diff --git a/expat-2.2.5-CVE-2024-45492.patch b/expat-2.5.0-CVE-2024-45492.patch similarity index 54% rename from expat-2.2.5-CVE-2024-45492.patch rename to expat-2.5.0-CVE-2024-45492.patch index 137911f..8950cdf 100644 --- a/expat-2.2.5-CVE-2024-45492.patch +++ b/expat-2.5.0-CVE-2024-45492.patch @@ -1,19 +1,21 @@ -commit 6fd04be3c2f7a2730c85b0eaf061549953161da3 -Author: Tomas Korbar -Date: Wed Sep 11 15:12:38 2024 +0200 +From 9bf0f2c16ee86f644dd1432507edff94c08dc232 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Mon, 19 Aug 2024 22:37:16 +0200 +Subject: [PATCH] lib: Detect integer overflow in function nextScaffoldPart - Fix CVE-2024-45492 - - https://github.com/libexpat/libexpat/pull/892 +Reported by TaiYou +--- + expat/lib/xmlparse.c | 9 +++++++++ + 1 file changed, 9 insertions(+) diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c -index 6818c4e..698e907 100644 +index 91682c188..f737575ea 100644 --- a/expat/lib/xmlparse.c +++ b/expat/lib/xmlparse.c -@@ -7426,6 +7426,15 @@ nextScaffoldPart(XML_Parser parser) +@@ -7558,6 +7558,15 @@ nextScaffoldPart(XML_Parser parser) { int next; - if (!dtd->scaffIndex) { + if (! dtd->scaffIndex) { + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning + * from -Wtype-limits on platforms where @@ -24,5 +26,5 @@ index 6818c4e..698e907 100644 + } +#endif dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int)); - if (!dtd->scaffIndex) + if (! dtd->scaffIndex) return -1; diff --git a/expat-2.2.5-CVE-2024-50602.patch b/expat-2.5.0-CVE-2024-50602.patch similarity index 57% rename from expat-2.2.5-CVE-2024-50602.patch rename to expat-2.5.0-CVE-2024-50602.patch index 3886c7d..1a06e2e 100644 --- a/expat-2.2.5-CVE-2024-50602.patch +++ b/expat-2.5.0-CVE-2024-50602.patch @@ -1,31 +1,31 @@ -commit c84ad1507fa42c25937af06e349c8f2f9bc34c11 +commit 38905b99bb78a6a691ed8358f30030116783656c Author: Tomas Korbar -Date: Fri Nov 8 11:18:42 2024 +0100 +Date: Thu Nov 7 15:00:46 2024 +0100 Fix CVE-2024-50602 See https://github.com/libexpat/libexpat/pull/915 diff --git a/expat/lib/expat.h b/expat/lib/expat.h -index afe12c5..157953c 100644 +index 842dd70..69b0ba1 100644 --- a/expat/lib/expat.h +++ b/expat/lib/expat.h -@@ -124,7 +124,9 @@ enum XML_Error { - XML_ERROR_RESERVED_PREFIX_XMLNS, - XML_ERROR_RESERVED_NAMESPACE_URI, - /* Added in 2.2.1. */ -- XML_ERROR_INVALID_ARGUMENT -+ XML_ERROR_INVALID_ARGUMENT, +@@ -128,7 +128,9 @@ enum XML_Error { + /* Added in 2.3.0. */ + XML_ERROR_NO_BUFFER, + /* Added in 2.4.0. */ +- XML_ERROR_AMPLIFICATION_LIMIT_BREACH ++ XML_ERROR_AMPLIFICATION_LIMIT_BREACH, + /* Added in 2.6.4. */ -+ XML_ERROR_NOT_STARTED ++ XML_ERROR_NOT_STARTED, }; enum XML_Content_Type { diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c -index 698e907..ed079a5 100644 +index e0c2873..8b2af91 100644 --- a/expat/lib/xmlparse.c +++ b/expat/lib/xmlparse.c -@@ -2170,6 +2170,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) +@@ -2193,6 +2193,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) { if (parser == NULL) return XML_STATUS_ERROR; switch (parser->m_parsingStatus.parsing) { @@ -35,7 +35,7 @@ index 698e907..ed079a5 100644 case XML_SUSPENDED: if (resumable) { parser->m_errorCode = XML_ERROR_SUSPENDED; -@@ -2180,7 +2183,7 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) +@@ -2203,7 +2206,7 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) { case XML_FINISHED: parser->m_errorCode = XML_ERROR_FINISHED; return XML_STATUS_ERROR; @@ -44,9 +44,9 @@ index 698e907..ed079a5 100644 if (resumable) { #ifdef XML_DTD if (parser->m_isParamEntity) { -@@ -2192,6 +2195,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) - } - else +@@ -2214,6 +2217,9 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable) { + parser->m_parsingStatus.parsing = XML_SUSPENDED; + } else parser->m_parsingStatus.parsing = XML_FINISHED; + break; + default: @@ -54,10 +54,10 @@ index 698e907..ed079a5 100644 } return XML_STATUS_OK; } -@@ -2456,6 +2462,9 @@ XML_ErrorString(enum XML_Error code) - /* Added in 2.2.5. */ - case XML_ERROR_INVALID_ARGUMENT: /* Constant added in 2.2.1, already */ - return XML_L("invalid argument"); +@@ -2478,6 +2484,9 @@ XML_ErrorString(enum XML_Error code) { + case XML_ERROR_AMPLIFICATION_LIMIT_BREACH: + return XML_L( + "limit on input amplification factor (from DTD and entities) breached"); + /* Added in 2.6.4. */ + case XML_ERROR_NOT_STARTED: + return XML_L("parser not started"); @@ -65,13 +65,13 @@ index 698e907..ed079a5 100644 return NULL; } diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c -index 6a3e09a..7b6d9fb 100644 +index ed88f9f..5769aa0 100644 --- a/expat/tests/runtests.c +++ b/expat/tests/runtests.c -@@ -9162,6 +9162,28 @@ START_TEST(test_misc_utf16le) +@@ -8711,6 +8711,28 @@ START_TEST(test_misc_tag_mismatch_reset_leak) { + } END_TEST - +START_TEST(test_misc_resumeparser_not_crashing) { + XML_Parser parser = XML_ParserCreate(NULL); + XML_GetBuffer(parser, 1); @@ -95,14 +95,14 @@ index 6a3e09a..7b6d9fb 100644 +END_TEST + static void - alloc_setup(void) - { -@@ -13325,6 +13347,8 @@ make_suite(void) - tcase_add_test(tc_misc, - test_misc_deny_internal_entity_closing_doctype_issue_317); - #endif -+ tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing); -+ tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser); + alloc_setup(void) { + XML_Memory_Handling_Suite memsuite = {duff_allocator, duff_reallocator, free}; +@@ -13176,6 +13198,8 @@ make_suite(void) { + tcase_add_test__ifdef_xml_dtd( + tc_misc, test_misc_deny_internal_entity_closing_doctype_issue_317); + tcase_add_test(tc_misc, test_misc_tag_mismatch_reset_leak); ++ tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing); ++ tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser); - suite_add_tcase(s, tc_alloc); - tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown); + suite_add_tcase(s, tc_alloc); + tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown); diff --git a/expat-2.2.5-CVE-2024-8176.patch b/expat-2.5.0-CVE-2024-8176.patch similarity index 60% rename from expat-2.2.5-CVE-2024-8176.patch rename to expat-2.5.0-CVE-2024-8176.patch index 44e1af8..9006d10 100644 --- a/expat-2.2.5-CVE-2024-8176.patch +++ b/expat-2.5.0-CVE-2024-8176.patch @@ -1,17 +1,14 @@ -commit 9f90e0bc7b061a12dbe8b5d1c6ef389cf61e0614 +commit c0de4903900004dd3ca91f246e5f6489a49a132b Author: Tomas Korbar -Date: Fri Apr 4 10:19:58 2025 +0200 +Date: Mon Mar 24 10:04:33 2025 +0100 Fix CVE-2024-8176 - - Backport of upstream commit e113a4667258fd2328659f35ed9c29b64731edab - Pull Request #298 diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c -index ed079a5..d00d23a 100644 +index 8b2af91..d68d2c8 100644 --- a/expat/lib/xmlparse.c +++ b/expat/lib/xmlparse.c -@@ -284,6 +284,10 @@ typedef struct { +@@ -305,6 +305,10 @@ typedef struct { const XML_Char *publicId; const XML_Char *notation; XML_Bool open; @@ -22,7 +19,7 @@ index ed079a5..d00d23a 100644 XML_Bool is_param; XML_Bool is_internal; /* true if declared in internal subset outside PE */ } ENTITY; -@@ -374,6 +378,12 @@ typedef struct { +@@ -395,6 +399,12 @@ typedef struct { int *scaffIndex; } DTD; @@ -35,51 +32,53 @@ index ed079a5..d00d23a 100644 typedef struct open_internal_entity { const char *internalEventPtr; const char *internalEventEndPtr; -@@ -381,6 +391,7 @@ typedef struct open_internal_entity { +@@ -402,6 +412,7 @@ typedef struct open_internal_entity { ENTITY *entity; int startTagLevel; XML_Bool betweenDecl; /* WFC: PE Between Declarations */ + enum EntityType type; } OPEN_INTERNAL_ENTITY; - typedef enum XML_Error PTRCALL Processor(XML_Parser parser, -@@ -418,9 +429,8 @@ 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); + enum XML_Account { +@@ -461,8 +472,8 @@ static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc, + const char *next, const char **nextPtr, + XML_Bool haveMore, XML_Bool allowClosingDoctype, + enum XML_Account account); +-static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity, +- XML_Bool betweenDecl); +static enum XML_Error processEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl, enum EntityType type); - static enum XML_Error - doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, - const char *start, const char *end, const char **endPtr, -@@ -449,8 +459,9 @@ static enum XML_Error - storeAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, - const char *, const char *, STRING_POOL *); - static enum XML_Error --appendAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, -- const char *, const char *, STRING_POOL *); + static enum XML_Error doContent(XML_Parser parser, int startTagLevel, + const ENCODING *enc, const char *start, + const char *end, const char **endPtr, +@@ -492,16 +503,21 @@ static enum XML_Error storeAttributeValue(XML_Parser parser, const ENCODING *, + XML_Bool isCdata, const char *, + const char *, STRING_POOL *, + enum XML_Account account); +-static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *, +- XML_Bool isCdata, const char *, +- const char *, STRING_POOL *, +- enum XML_Account account); ++static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, -+ const char *ptr, const char *end, STRING_POOL *pool, -+ const char **nextPtr); - static ATTRIBUTE_ID * - getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, - const char *end); -@@ -458,7 +469,10 @@ static int - setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); - static enum XML_Error - storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start, -- const char *end); -+ const char *end, const char **nextPtr); ++ const char *ptr, const char *end, STRING_POOL *pool, ++ enum XML_Account account, const char **nextPtr); + static ATTRIBUTE_ID *getAttributeId(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); + static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); + static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end, +- enum XML_Account account); ++ enum XML_Account account, ++ const char **nextPtr); +static enum XML_Error callStoreEntityValue(XML_Parser parser, + const ENCODING *enc, -+ const char *start, const char *end); - static int - reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, - const char *start, const char *end); -@@ -611,6 +625,10 @@ struct XML_ParserStruct { ++ const char *start, const char *end, ++ enum XML_Account account); + static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); + static int reportComment(XML_Parser parser, const ENCODING *enc, +@@ -669,6 +685,10 @@ struct XML_ParserStruct { const char *m_positionPtr; OPEN_INTERNAL_ENTITY *m_openInternalEntities; OPEN_INTERNAL_ENTITY *m_freeInternalEntities; @@ -90,15 +89,15 @@ index ed079a5..d00d23a 100644 XML_Bool m_defaultExpandInternalEntities; int m_tagLevel; ENTITY *m_declEntity; -@@ -654,6 +672,7 @@ struct XML_ParserStruct { - enum XML_ParamEntityParsing m_paramEntityParsing; +@@ -716,6 +736,7 @@ struct XML_ParserStruct { + ACCOUNTING m_accounting; + ENTITY_STATS m_entity_stats; #endif - unsigned long m_hash_secret_salt; + XML_Bool m_reenter; }; - #define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) -@@ -930,7 +949,29 @@ callProcessor(XML_Parser parser, const char *start, const char *end, + #define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) +@@ -986,7 +1007,29 @@ callProcessor(XML_Parser parser, const char *start, const char *end, } } g_parseAttempts += 1; @@ -129,7 +128,7 @@ index ed079a5..d00d23a 100644 if (ret == XML_ERROR_NONE) { // if we consumed nothing, remember what we had on this parse attempt. if (*endPtr == start) { -@@ -1045,6 +1086,8 @@ parserCreate(const XML_Char *encodingName, +@@ -1097,6 +1140,8 @@ parserCreate(const XML_Char *encodingName, parser->m_freeBindingList = NULL; parser->m_freeTagList = NULL; parser->m_freeInternalEntities = NULL; @@ -138,7 +137,7 @@ index ed079a5..d00d23a 100644 parser->m_groupSize = 0; parser->m_groupConnector = NULL; -@@ -1149,6 +1192,8 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) +@@ -1199,6 +1244,8 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { parser->m_eventEndPtr = NULL; parser->m_positionPtr = NULL; parser->m_openInternalEntities = NULL; @@ -147,7 +146,7 @@ index ed079a5..d00d23a 100644 parser->m_defaultExpandInternalEntities = XML_TRUE; parser->m_tagLevel = 0; parser->m_tagStack = NULL; -@@ -1159,6 +1204,8 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) +@@ -1209,6 +1256,8 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { parser->m_unknownEncodingData = NULL; parser->m_parentParser = NULL; parser->m_parsingStatus.parsing = XML_INITIALIZED; @@ -156,7 +155,7 @@ index ed079a5..d00d23a 100644 #ifdef XML_DTD parser->m_isParamEntity = XML_FALSE; parser->m_useForeignDTD = XML_FALSE; -@@ -1208,6 +1255,24 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) +@@ -1268,6 +1317,24 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) { openEntity->next = parser->m_freeInternalEntities; parser->m_freeInternalEntities = openEntity; } @@ -181,7 +180,7 @@ index ed079a5..d00d23a 100644 moveToFreeBindingList(parser, parser->m_inheritedBindings); FREE(parser, parser->m_unknownEncodingMem); if (parser->m_unknownEncodingRelease) -@@ -1221,6 +1286,19 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) +@@ -1281,6 +1348,19 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) { return XML_TRUE; } @@ -199,18 +198,19 @@ index ed079a5..d00d23a 100644 +} + enum XML_Status XMLCALL - XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) - { -@@ -1230,7 +1308,7 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) + XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) { + if (parser == NULL) +@@ -1289,8 +1369,7 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) { XXX There's no way for the caller to determine which of the XXX possible error cases caused the XML_STATUS_ERROR return. */ -- if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) +- if (parser->m_parsingStatus.parsing == XML_PARSING +- || parser->m_parsingStatus.parsing == XML_SUSPENDED) + if (parserBusy(parser)) return XML_STATUS_ERROR; /* Get rid of any previous encoding name */ -@@ -1473,7 +1551,34 @@ XML_ParserFree(XML_Parser parser) +@@ -1527,7 +1606,34 @@ XML_ParserFree(XML_Parser parser) { entityList = entityList->next; FREE(parser, openEntity); } @@ -246,43 +246,47 @@ index ed079a5..d00d23a 100644 destroyBindings(parser->m_freeBindingList, parser); destroyBindings(parser->m_inheritedBindings, parser); poolDestroy(&parser->m_tempPool); -@@ -1516,7 +1621,7 @@ XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) +@@ -1569,8 +1675,7 @@ XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) { return XML_ERROR_INVALID_ARGUMENT; #ifdef XML_DTD /* block after XML_Parse()/XML_ParseBuffer() has been called */ -- if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) +- if (parser->m_parsingStatus.parsing == XML_PARSING +- || parser->m_parsingStatus.parsing == XML_SUSPENDED) + if (parserBusy(parser)) return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING; parser->m_useForeignDTD = useDTD; return XML_ERROR_NONE; -@@ -1531,7 +1636,7 @@ XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) +@@ -1585,8 +1690,7 @@ XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) { if (parser == NULL) return; /* block after XML_Parse()/XML_ParseBuffer() has been called */ -- if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) +- if (parser->m_parsingStatus.parsing == XML_PARSING +- || parser->m_parsingStatus.parsing == XML_SUSPENDED) + if (parserBusy(parser)) return; parser->m_ns_triplets = do_nst ? XML_TRUE : XML_FALSE; } -@@ -1841,7 +1946,7 @@ XML_SetParamEntityParsing(XML_Parser parser, +@@ -1855,8 +1959,7 @@ XML_SetParamEntityParsing(XML_Parser parser, if (parser == NULL) return 0; /* block after XML_Parse()/XML_ParseBuffer() has been called */ -- if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) +- if (parser->m_parsingStatus.parsing == XML_PARSING +- || parser->m_parsingStatus.parsing == XML_SUSPENDED) + if (parserBusy(parser)) return 0; #ifdef XML_DTD parser->m_paramEntityParsing = peParsing; -@@ -1860,7 +1965,7 @@ XML_SetHashSalt(XML_Parser parser, +@@ -1873,8 +1976,7 @@ XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt) { if (parser->m_parentParser) return XML_SetHashSalt(parser->m_parentParser, hash_salt); /* block after XML_Parse()/XML_ParseBuffer() has been called */ -- if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED) +- if (parser->m_parsingStatus.parsing == XML_PARSING +- || parser->m_parsingStatus.parsing == XML_SUSPENDED) + if (parserBusy(parser)) return 0; parser->m_hash_secret_salt = hash_salt; return 1; -@@ -2164,6 +2269,11 @@ XML_GetBuffer(XML_Parser parser, int len) +@@ -2188,6 +2290,11 @@ XML_GetBuffer(XML_Parser parser, int len) { return parser->m_bufferEnd; } @@ -292,76 +296,64 @@ index ed079a5..d00d23a 100644 +} + enum XML_Status XMLCALL - XML_StopParser(XML_Parser parser, XML_Bool resumable) - { -@@ -2610,8 +2720,8 @@ contentProcessor(XML_Parser parser, - const char *end, - const char **endPtr) - { -- enum XML_Error result = doContent(parser, 0, parser->m_encoding, start, end, -- endPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer); -+ enum XML_Error result = doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding, start, end, -+ endPtr, (XML_Bool)! parser->m_parsingStatus.finalBuffer); + XML_StopParser(XML_Parser parser, XML_Bool resumable) { + if (parser == NULL) +@@ -2659,8 +2766,9 @@ static enum XML_Error PTRCALL + contentProcessor(XML_Parser parser, const char *start, const char *end, + const char **endPtr) { + enum XML_Error result = doContent( +- parser, 0, parser->m_encoding, start, end, endPtr, +- (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT); ++ parser, parser->m_parentParser ? 1 : 0, parser->m_encoding, start, end, ++ endPtr, (XML_Bool)! parser->m_parsingStatus.finalBuffer, ++ XML_ACCOUNT_DIRECT); if (result == XML_ERROR_NONE) { - if (!storeRawNames(parser)) + if (! storeRawNames(parser)) return XML_ERROR_NO_MEMORY; -@@ -2697,6 +2807,11 @@ externalEntityInitProcessor3(XML_Parser parser, - return XML_ERROR_NONE; - case XML_FINISHED: - return XML_ERROR_ABORTED; -+ case XML_PARSING: -+ if (parser->m_reenter) { -+ return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE -+ } -+ /* Fall through */ - default: - start = next; - } -@@ -2863,7 +2978,7 @@ doContent(XML_Parser parser, - reportDefault(parser, enc, s, next); - break; - } -- result = processInternalEntity(parser, entity, XML_FALSE); -+ result = processEntity(parser, entity, XML_FALSE, ENTITY_INTERNAL); - if (result != XML_ERROR_NONE) - return result; +@@ -2748,6 +2856,11 @@ externalEntityInitProcessor3(XML_Parser parser, const char *start, + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; ++ case XML_PARSING: ++ if (parser->m_reenter) { ++ return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE ++ } ++ /* Fall through */ + default: + start = next; + } +@@ -2921,7 +3034,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + reportDefault(parser, enc, s, next); + break; } -@@ -2990,9 +3105,13 @@ doContent(XML_Parser parser, - poolClear(&parser->m_tempPool); - freeBindings(parser, bindings); - } -- if ((parser->m_tagLevel == 0) && -- !((parser->m_parsingStatus.parsing == XML_FINISHED) || (parser->m_parsingStatus.parsing == XML_SUSPENDED))) { -- return epilogProcessor(parser, next, end, nextPtr); -+ if ((parser->m_tagLevel == 0) && (parser->m_parsingStatus.parsing != XML_FINISHED)) { +- result = processInternalEntity(parser, entity, XML_FALSE); ++ result = processEntity(parser, entity, XML_FALSE, ENTITY_INTERNAL); + if (result != XML_ERROR_NONE) + return result; + } else if (parser->m_externalEntityRefHandler) { +@@ -3047,7 +3160,9 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + } + if ((parser->m_tagLevel == 0) + && (parser->m_parsingStatus.parsing != XML_FINISHED)) { +- if (parser->m_parsingStatus.parsing == XML_SUSPENDED) ++ if (parser->m_parsingStatus.parsing == XML_SUSPENDED ++ || (parser->m_parsingStatus.parsing == XML_PARSING ++ && parser->m_reenter)) + parser->m_processor = epilogProcessor; + else + return epilogProcessor(parser, next, end, nextPtr); +@@ -3108,7 +3223,9 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + } + if ((parser->m_tagLevel == 0) + && (parser->m_parsingStatus.parsing != XML_FINISHED)) { +- if (parser->m_parsingStatus.parsing == XML_SUSPENDED) + if (parser->m_parsingStatus.parsing == XML_SUSPENDED + || (parser->m_parsingStatus.parsing == XML_PARSING + && parser->m_reenter)) -+ parser->m_processor = epilogProcessor; -+ else -+ return epilogProcessor(parser, next, end, nextPtr); - } - break; - case XML_TOK_END_TAG: -@@ -3046,8 +3165,15 @@ doContent(XML_Parser parser, - parser->m_freeBindingList = b; - b->prefix->binding = b->prevPrefixBinding; - } -- if (parser->m_tagLevel == 0) -- return epilogProcessor(parser, next, end, nextPtr); -+ if ((parser->m_tagLevel == 0) -+ && (parser->m_parsingStatus.parsing != XML_FINISHED)) { -+ if (parser->m_parsingStatus.parsing == XML_SUSPENDED -+ || (parser->m_parsingStatus.parsing == XML_PARSING -+ && parser->m_reenter)) -+ parser->m_processor = epilogProcessor; -+ else -+ return epilogProcessor(parser, next, end, nextPtr); -+ } - } - break; - case XML_TOK_CHAR_REF: -@@ -3183,14 +3309,22 @@ doContent(XML_Parser parser, + parser->m_processor = epilogProcessor; + else + return epilogProcessor(parser, next, end, nextPtr); +@@ -3241,14 +3358,22 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, break; /* LCOV_EXCL_STOP */ } @@ -380,12 +372,12 @@ index ed079a5..d00d23a 100644 + return XML_ERROR_NONE; + } + /* Fall through */ - default: ; + default:; + *eventPP = s = next; } } /* not reached */ -@@ -4112,14 +4246,21 @@ doCdataSection(XML_Parser parser, +@@ -4165,14 +4290,21 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, /* LCOV_EXCL_STOP */ } @@ -403,51 +395,52 @@ index ed079a5..d00d23a 100644 + return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE + } + /* Fall through */ - default: ; + default:; + *eventPP = s = next; } } /* not reached */ -@@ -4466,7 +4607,7 @@ entityValueInitProcessor(XML_Parser parser, - break; +@@ -4504,7 +4636,7 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, } /* found end of entity value - can store it now */ -- return storeEntityValue(parser, parser->m_encoding, s, end); -+ return storeEntityValue(parser, parser->m_encoding, s, end, NULL); - } - else if (tok == XML_TOK_XML_DECL) { + return storeEntityValue(parser, parser->m_encoding, s, end, +- XML_ACCOUNT_DIRECT); ++ XML_ACCOUNT_DIRECT, NULL); + } else if (tok == XML_TOK_XML_DECL) { enum XML_Error result; -@@ -4581,7 +4722,7 @@ entityValueProcessor(XML_Parser parser, + result = processXmlDecl(parser, 0, start, next); +@@ -4631,7 +4763,7 @@ entityValueProcessor(XML_Parser parser, const char *s, const char *end, break; } /* found end of entity value - can store it now */ -- return storeEntityValue(parser, enc, s, end); -+ return storeEntityValue(parser, enc, s, end, NULL); +- return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT); ++ return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT, NULL); } start = next; } -@@ -5012,9 +5153,8 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, +@@ -5069,9 +5201,9 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, break; case XML_ROLE_ENTITY_VALUE: if (dtd->keepProcessing) { -- enum XML_Error result = storeEntityValue(parser, enc, -- s + enc->minBytesPerChar, -- next - enc->minBytesPerChar); +- enum XML_Error result +- = storeEntityValue(parser, enc, s + enc->minBytesPerChar, +- next - enc->minBytesPerChar, XML_ACCOUNT_NONE); + enum XML_Error result = callStoreEntityValue( -+ parser, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar); ++ parser, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar, ++ XML_ACCOUNT_NONE); if (parser->m_declEntity) { parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool); - parser->m_declEntity->textLen = (int)(poolLength(&dtd->entityValuePool)); -@@ -5429,7 +5569,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + parser->m_declEntity->textLen +@@ -5467,7 +5599,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, enum XML_Error result; - XML_Bool betweenDecl = - (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); + XML_Bool betweenDecl + = (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); - result = processInternalEntity(parser, entity, betweenDecl); + result = processEntity(parser, entity, betweenDecl, ENTITY_INTERNAL); if (result != XML_ERROR_NONE) return result; handleDefault = XML_FALSE; -@@ -5627,6 +5767,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, +@@ -5672,6 +5804,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, return XML_ERROR_NONE; case XML_FINISHED: return XML_ERROR_ABORTED; @@ -460,7 +453,7 @@ index ed079a5..d00d23a 100644 default: s = next; tok = XmlPrologTok(enc, s, end, &next); -@@ -5690,75 +5836,80 @@ epilogProcessor(XML_Parser parser, +@@ -5739,28 +5877,58 @@ epilogProcessor(XML_Parser parser, const char *s, const char *end, default: return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; } @@ -478,64 +471,64 @@ index ed079a5..d00d23a 100644 + return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE + } + /* Fall through */ - default: ; + default:; + parser->m_eventPtr = s = next; } } } static enum XML_Error --processInternalEntity(XML_Parser parser, ENTITY *entity, -- XML_Bool betweenDecl) --{ +-processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) { - const char *textStart, *textEnd; - const char *next; - enum XML_Error result; - OPEN_INTERNAL_ENTITY *openEntity; -- ++processEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl, ++ enum EntityType type) { ++ OPEN_INTERNAL_ENTITY *openEntity, **openEntityList, **freeEntityList; ++ switch (type) { ++ case ENTITY_INTERNAL: ++ parser->m_processor = internalEntityProcessor; ++ openEntityList = &parser->m_openInternalEntities; ++ freeEntityList = &parser->m_freeInternalEntities; ++ break; ++ case ENTITY_ATTRIBUTE: ++ openEntityList = &parser->m_openAttributeEntities; ++ freeEntityList = &parser->m_freeAttributeEntities; ++ break; ++ case ENTITY_VALUE: ++ openEntityList = &parser->m_openValueEntities; ++ freeEntityList = &parser->m_freeValueEntities; ++ break; ++ /* default case serves merely as a safety net in case of a ++ * wrong entityType. Therefore we exclude the following lines ++ * from the test coverage. ++ * ++ * LCOV_EXCL_START ++ */ ++ default: ++ // Should not reach here ++ assert(0); ++ /* LCOV_EXCL_STOP */ ++ } + - if (parser->m_freeInternalEntities) { - openEntity = parser->m_freeInternalEntities; - parser->m_freeInternalEntities = openEntity->next; -+processEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl, -+ enum EntityType type) { -+ OPEN_INTERNAL_ENTITY *openEntity, **openEntityList, **freeEntityList; -+ switch (type) { -+ case ENTITY_INTERNAL: -+ parser->m_processor = internalEntityProcessor; -+ openEntityList = &parser->m_openInternalEntities; -+ freeEntityList = &parser->m_freeInternalEntities; -+ break; -+ case ENTITY_ATTRIBUTE: -+ openEntityList = &parser->m_openAttributeEntities; -+ freeEntityList = &parser->m_freeAttributeEntities; -+ break; -+ case ENTITY_VALUE: -+ openEntityList = &parser->m_openValueEntities; -+ freeEntityList = &parser->m_freeValueEntities; -+ break; -+ /* default case serves merely as a safety net in case of a -+ * wrong entityType. Therefore we exclude the following lines -+ * from the test coverage. -+ * -+ * LCOV_EXCL_START -+ */ -+ default: -+ // Should not reach here -+ assert(0); -+ /* LCOV_EXCL_STOP */ - } -- else { -+ + if (*freeEntityList) { -+ openEntity = *freeEntityList; -+ *freeEntityList = openEntity->next; -+ } else { - openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY)); - if (!openEntity) ++ openEntity = *freeEntityList; ++ *freeEntityList = openEntity->next; + } else { + openEntity + = (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY)); +@@ -5768,56 +5936,33 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) { return XML_ERROR_NO_MEMORY; } entity->open = XML_TRUE; + entity->hasMore = XML_TRUE; + #ifdef XML_DTD + entityTrackingOnOpen(parser, entity, __LINE__); + #endif entity->processed = 0; - openEntity->next = parser->m_openInternalEntities; - parser->m_openInternalEntities = openEntity; @@ -547,28 +540,32 @@ index ed079a5..d00d23a 100644 openEntity->betweenDecl = betweenDecl; openEntity->internalEventPtr = NULL; openEntity->internalEventEndPtr = NULL; -- textStart = (char *)entity->textPtr; -- textEnd = (char *)(entity->textPtr + entity->textLen); +- textStart = (const char *)entity->textPtr; +- textEnd = (const char *)(entity->textPtr + entity->textLen); - /* Set a safe default value in case 'next' does not get set */ - next = textStart; - -#ifdef XML_DTD - if (entity->is_param) { -- 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 +- int tok +- = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next); +- result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd, +- tok, next, &next, XML_FALSE, XML_FALSE, +- XML_ACCOUNT_ENTITY_EXPANSION); +- } else -#endif /* XML_DTD */ -- result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding, textStart, -- textEnd, &next, XML_FALSE); +- result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding, +- textStart, textEnd, &next, XML_FALSE, +- XML_ACCOUNT_ENTITY_EXPANSION); - - if (result == XML_ERROR_NONE) { - if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) { - entity->processed = (int)(next - textStart); - parser->m_processor = internalEntityProcessor; -- } -- else { +- } else { +-#ifdef XML_DTD +- entityTrackingOnClose(parser, entity, __LINE__); +-#endif /* XML_DTD */ - entity->open = XML_FALSE; - parser->m_openInternalEntities = openEntity->next; - /* put openEntity back in list of free instances */ @@ -585,128 +582,147 @@ index ed079a5..d00d23a 100644 } static enum XML_Error PTRCALL -@@ -5776,58 +5927,59 @@ internalEntityProcessor(XML_Parser parser, + internalEntityProcessor(XML_Parser parser, const char *s, const char *end, + const char **nextPtr) { ++ UNUSED_P(s); ++ UNUSED_P(end); ++ UNUSED_P(nextPtr); + ENTITY *entity; + const char *textStart, *textEnd; + const char *next; +@@ -5827,72 +5972,63 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end, return XML_ERROR_UNEXPECTED_STATE; entity = openEntity->entity; -- textStart = ((char *)entity->textPtr) + entity->processed; -- textEnd = (char *)(entity->textPtr + entity->textLen); +- textStart = ((const char *)entity->textPtr) + entity->processed; +- textEnd = (const char *)(entity->textPtr + entity->textLen); - /* Set a safe default value in case 'next' does not get set */ - next = textStart; - -#ifdef XML_DTD - if (entity->is_param) { -- 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 +- int tok +- = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next); +- result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd, +- tok, next, &next, XML_FALSE, XML_TRUE, +- XML_ACCOUNT_ENTITY_EXPANSION); +- } else -#endif /* XML_DTD */ -- result = doContent(parser, openEntity->startTagLevel, parser->m_internalEncoding, -- textStart, textEnd, &next, XML_FALSE); +- result = doContent(parser, openEntity->startTagLevel, +- parser->m_internalEncoding, textStart, textEnd, &next, +- XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION); - - if (result != XML_ERROR_NONE) - return result; -- else if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) { -- entity->processed = (int)(next - (char *)entity->textPtr); -- return result; -- } -- else { -- entity->open = XML_FALSE; -- parser->m_openInternalEntities = openEntity->next; -- /* put openEntity back in list of free instances */ -- openEntity->next = parser->m_freeInternalEntities; -- parser->m_freeInternalEntities = openEntity; -- } +- +- if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) { +- entity->processed = (int)(next - (const char *)entity->textPtr); + // This will return early + if (entity->hasMore) { -+ textStart = ((const char *)entity->textPtr) + entity->processed; -+ textEnd = (const char *)(entity->textPtr + entity->textLen); -+ /* Set a safe default value in case 'next' does not get set */ -+ next = textStart; -+ -+ if (entity->is_param) { -+ 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 { -+ result = doContent(parser, openEntity->startTagLevel, -+ parser->m_internalEncoding, textStart, textEnd, &next, -+ XML_FALSE); -+ } ++ textStart = ((const char *)entity->textPtr) + entity->processed; ++ textEnd = (const char *)(entity->textPtr + entity->textLen); ++ /* Set a safe default value in case 'next' does not get set */ ++ next = textStart; ++ ++ if (entity->is_param) { ++ int tok ++ = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next); ++ result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd, ++ tok, next, &next, XML_FALSE, XML_FALSE, ++ XML_ACCOUNT_ENTITY_EXPANSION); ++ } else { ++ result = doContent(parser, openEntity->startTagLevel, ++ parser->m_internalEncoding, textStart, textEnd, &next, ++ XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION); ++ } ++ ++ if (result != XML_ERROR_NONE) ++ return result; ++ // Check if entity is complete, if not, mark down how much of it is ++ // processed ++ if (textEnd != next ++ && (parser->m_parsingStatus.parsing == XML_SUSPENDED ++ || (parser->m_parsingStatus.parsing == XML_PARSING ++ && parser->m_reenter))) { ++ entity->processed = (int)(next - (const char *)entity->textPtr); ++ return result; ++ } ++ // Entity is complete. We cannot close it here since we need to first ++ // process its possible inner entities (which are added to the ++ // m_openInternalEntities during doProlog or doContent calls above) ++ entity->hasMore = XML_FALSE; ++ triggerReenter(parser); + return result; +- } +- ++ } // End of entity processing, "if" block will return here ++ // Remove fully processed openEntity from open entity list. + #ifdef XML_DTD + entityTrackingOnClose(parser, entity, __LINE__); + #endif ++ // openEntity is m_openInternalEntities' head, as we set it at the start of ++ // this function and we skipped doProlog and doContent calls with hasMore set ++ // to false. This means we can directly remove the head of ++ // m_openInternalEntities ++ assert(parser->m_openInternalEntities == openEntity); + entity->open = XML_FALSE; +- parser->m_openInternalEntities = openEntity->next; ++ parser->m_openInternalEntities = parser->m_openInternalEntities->next; + /* put openEntity back in list of free instances */ + openEntity->next = parser->m_freeInternalEntities; + parser->m_freeInternalEntities = openEntity; +- // If there are more open entities we want to stop right here and have the +- // upcoming call to XML_ResumeParser continue with entity content, or it would +- // be ignored altogether. +- if (parser->m_openInternalEntities != NULL +- && parser->m_parsingStatus.parsing == XML_SUSPENDED) { +- return XML_ERROR_NONE; +- } +- -#ifdef XML_DTD - if (entity->is_param) { - int tok; - 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 +- (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE, +- XML_ACCOUNT_DIRECT); +- } else -#endif /* XML_DTD */ - { - parser->m_processor = contentProcessor; - /* see externalEntityContentProcessor vs contentProcessor */ -- result = doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding, -- s, end, nextPtr, -- (XML_Bool)! parser->m_parsingStatus.finalBuffer); +- result = doContent(parser, parser->m_parentParser ? 1 : 0, +- parser->m_encoding, s, end, nextPtr, +- (XML_Bool)! parser->m_parsingStatus.finalBuffer, +- XML_ACCOUNT_DIRECT); - if (result == XML_ERROR_NONE) { - if (! storeRawNames(parser)) - return XML_ERROR_NO_MEMORY; -+ if (result != XML_ERROR_NONE) -+ return result; -+ // Check if entity is complete, if not, mark down how much of it is -+ // processed -+ if (textEnd != next -+ && (parser->m_parsingStatus.parsing == XML_SUSPENDED -+ || (parser->m_parsingStatus.parsing == XML_PARSING -+ && parser->m_reenter))) { -+ entity->processed = (int)(next - (const char *)entity->textPtr); -+ return result; - } -+ // Entity is complete. We cannot close it here since we need to first -+ // process its possible inner entities (which are added to the -+ // m_openInternalEntities during doProlog or doContent calls above) -+ entity->hasMore = XML_FALSE; -+ triggerReenter(parser); - return result; -- } -+ } // End of entity processing, "if" block will return here -+ // Remove fully processed openEntity from open entity list. -+ -+ // openEntity is m_openInternalEntities' head, as we set it at the start of -+ // this function and we skipped doProlog and doContent calls with hasMore set -+ // to false. This means we can directly remove the head of -+ // m_openInternalEntities -+ assert(parser->m_openInternalEntities == openEntity); -+ entity->open = XML_FALSE; -+ parser->m_openInternalEntities = parser->m_openInternalEntities->next; -+ /* put openEntity back in list of free instances */ -+ openEntity->next = parser->m_freeInternalEntities; -+ parser->m_freeInternalEntities = openEntity; +- } +- return result; + if (parser->m_openInternalEntities == NULL) { + parser->m_processor = entity->is_param ? prologProcessor : contentProcessor; -+ } + } + triggerReenter(parser); + return XML_ERROR_NONE; } static enum XML_Error PTRCALL -@@ -5844,8 +5996,66 @@ storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, - const char *ptr, const char *end, - STRING_POOL *pool) - { -- enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, -- end, pool); +@@ -5908,8 +6044,70 @@ static enum XML_Error + storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, STRING_POOL *pool, + enum XML_Account account) { +- enum XML_Error result +- = appendAttributeValue(parser, enc, isCdata, ptr, end, pool, account); + const char *next = ptr; + enum XML_Error result = XML_ERROR_NONE; + + while (1) { + if (! parser->m_openAttributeEntities) { + result = appendAttributeValue(parser, enc, isCdata, next, end, pool, -+ &next); ++ account, &next); + } else { + OPEN_INTERNAL_ENTITY *const openEntity = parser->m_openAttributeEntities; + if (! openEntity) @@ -722,7 +738,7 @@ index ed079a5..d00d23a 100644 + if (entity->hasMore) { + result = appendAttributeValue( + parser, parser->m_internalEncoding, isCdata, textStart, textEnd, -+ pool, &nextInEntity); ++ pool, XML_ACCOUNT_ENTITY_EXPANSION, &nextInEntity); + if (result != XML_ERROR_NONE) + break; + // Check if entity is complete, if not, mark down how much of it is @@ -741,6 +757,10 @@ index ed079a5..d00d23a 100644 + continue; + } // End of entity processing, "if" block skips the rest + ++ // Remove fully processed openEntity from open entity list. ++#if XML_DTD == 1 ++ entityTrackingOnClose(parser, entity, __LINE__); ++#endif + // openEntity is m_openAttributeEntities' head, since we set it at the + // start of this function and because we skipped appendAttributeValue call + // with hasMore set to false. This means we can directly remove the head @@ -759,22 +779,21 @@ index ed079a5..d00d23a 100644 + break; + } + } -+ ++ if (result) return result; - if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) -@@ -5858,14 +6068,17 @@ storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + if (! isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) +@@ -5922,7 +6120,7 @@ storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, - const char *ptr, const char *end, -- STRING_POOL *pool) -+ STRING_POOL *pool, const char **nextPtr) - { - DTD * const dtd = parser->m_dtd; /* save one level of indirection */ - for (;;) { -- const char *next; -+ const char *next = ptr; /* XmlAttributeValueTok doesn't always set the last arg */ - int tok = XmlAttributeValueTok(enc, ptr, end, &next); + const char *ptr, const char *end, STRING_POOL *pool, +- enum XML_Account account) { ++ enum XML_Account account, const char **nextPtr) { + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ + #ifndef XML_DTD + UNUSED_P(account); +@@ -5940,6 +6138,9 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + #endif switch (tok) { case XML_TOK_NONE: + if (nextPtr) { @@ -783,57 +802,64 @@ index ed079a5..d00d23a 100644 return XML_ERROR_NONE; case XML_TOK_INVALID: if (enc == parser->m_encoding) -@@ -6006,14 +6219,11 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, - } - else { - enum XML_Error result; -- const XML_Char *textEnd = entity->textPtr + entity->textLen; -- entity->open = XML_TRUE; -- result = appendAttributeValue(parser, parser->m_internalEncoding, isCdata, -- (char *)entity->textPtr, -- (char *)textEnd, pool); -- entity->open = XML_FALSE; -- if (result) -- return result; -+ result = processEntity(parser, entity, XML_FALSE, ENTITY_ATTRIBUTE); -+ if ((result == XML_ERROR_NONE) && (nextPtr != NULL)) { -+ *nextPtr = next; -+ } -+ return result; - } +@@ -6080,21 +6281,11 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; + } else { + enum XML_Error result; +- const XML_Char *textEnd = entity->textPtr + entity->textLen; +- entity->open = XML_TRUE; +-#ifdef XML_DTD +- entityTrackingOnOpen(parser, entity, __LINE__); +-#endif +- result = appendAttributeValue(parser, parser->m_internalEncoding, +- isCdata, (const char *)entity->textPtr, +- (const char *)textEnd, pool, +- XML_ACCOUNT_ENTITY_EXPANSION); +-#ifdef XML_DTD +- entityTrackingOnClose(parser, entity, __LINE__); +-#endif +- entity->open = XML_FALSE; +- if (result) +- return result; ++ result = processEntity(parser, entity, XML_FALSE, ENTITY_ATTRIBUTE); ++ if ((result == XML_ERROR_NONE) && (nextPtr != NULL)) { ++ *nextPtr = next; ++ } ++ return result; } - break; -@@ -6043,7 +6253,8 @@ static enum XML_Error - storeEntityValue(XML_Parser parser, - const ENCODING *enc, - const char *entityTextPtr, -- const char *entityTextEnd) -+ const char *entityTextEnd, -+ const char **nextPtr) - { - DTD * const dtd = parser->m_dtd; /* save one level of indirection */ + } break; + default: +@@ -6122,7 +6313,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + static enum XML_Error + storeEntityValue(XML_Parser parser, const ENCODING *enc, + const char *entityTextPtr, const char *entityTextEnd, +- enum XML_Account account) { ++ enum XML_Account account, const char **nextPtr) { + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ STRING_POOL *pool = &(dtd->entityValuePool); -@@ -6060,8 +6271,9 @@ storeEntityValue(XML_Parser parser, + enum XML_Error result = XML_ERROR_NONE; +@@ -6140,8 +6331,9 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, return XML_ERROR_NO_MEMORY; } + const char *next; for (;;) { -- const char *next; -+ next = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */ +- const char *next ++ next + = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */ int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); - switch (tok) { - case XML_TOK_PARAM_ENTITY_REF: -@@ -6115,15 +6327,8 @@ storeEntityValue(XML_Parser parser, + +@@ -6205,16 +6397,8 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, + } else dtd->keepProcessing = dtd->standalone; - } - else { + } else { - entity->open = XML_TRUE; -- result = storeEntityValue(parser, -- parser->m_internalEncoding, -- (char *)entity->textPtr, -- (char *)(entity->textPtr -- + entity->textLen)); +- entityTrackingOnOpen(parser, entity, __LINE__); +- result = storeEntityValue( +- parser, parser->m_internalEncoding, (const char *)entity->textPtr, +- (const char *)(entity->textPtr + entity->textLen), +- XML_ACCOUNT_ENTITY_EXPANSION); +- entityTrackingOnClose(parser, entity, __LINE__); - entity->open = XML_FALSE; - if (result) - goto endEntityValue; @@ -842,7 +868,7 @@ index ed079a5..d00d23a 100644 } break; } -@@ -6213,9 +6418,79 @@ endEntityValue: +@@ -6302,9 +6486,84 @@ endEntityValue: #ifdef XML_DTD parser->m_prologState.inEntityValue = oldInEntityValue; #endif /* XML_DTD */ @@ -855,13 +881,14 @@ index ed079a5..d00d23a 100644 +static enum XML_Error +callStoreEntityValue(XML_Parser parser, const ENCODING *enc, -+ const char *entityTextPtr, const char *entityTextEnd) { ++ const char *entityTextPtr, const char *entityTextEnd, ++ enum XML_Account account) { + const char *next = entityTextPtr; + enum XML_Error result = XML_ERROR_NONE; + while (1) { + if (! parser->m_openValueEntities) { + result -+ = storeEntityValue(parser, enc, next, entityTextEnd, &next); ++ = storeEntityValue(parser, enc, next, entityTextEnd, account, &next); + } else { + OPEN_INTERNAL_ENTITY *const openEntity = parser->m_openValueEntities; + if (! openEntity) @@ -876,7 +903,8 @@ index ed079a5..d00d23a 100644 + const char *nextInEntity = textStart; + if (entity->hasMore) { + result = storeEntityValue(parser, parser->m_internalEncoding, textStart, -+ textEnd, &nextInEntity); ++ textEnd, XML_ACCOUNT_ENTITY_EXPANSION, ++ &nextInEntity); + if (result != XML_ERROR_NONE) + break; + // Check if entity is complete, if not, mark down how much of it is @@ -895,6 +923,10 @@ index ed079a5..d00d23a 100644 + continue; + } // End of entity processing, "if" block skips the rest + ++ // Remove fully processed openEntity from open entity list. ++# if XML_DTD == 1 ++ entityTrackingOnClose(parser, entity, __LINE__); ++# endif + // openEntity is m_openValueEntities' head, since we set it at the + // start of this function and because we skipped storeEntityValue call + // with hasMore set to false. This means we can directly remove the head @@ -917,16 +949,15 @@ index ed079a5..d00d23a 100644 + + return result; +} -+ + static void FASTCALL - normalizeLines(XML_Char *s) - { + normalizeLines(XML_Char *s) { + XML_Char *p; diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c -index 7b6d9fb..0b5bcf1 100644 +index 5769aa0..5db384d 100644 --- a/expat/tests/runtests.c +++ b/expat/tests/runtests.c -@@ -39,6 +39,8 @@ +@@ -47,6 +47,8 @@ #endif #include @@ -935,7 +966,7 @@ index 7b6d9fb..0b5bcf1 100644 #include #include #include -@@ -1915,6 +1917,87 @@ START_TEST(test_wfc_no_recursive_entity_refs) +@@ -1788,6 +1790,89 @@ START_TEST(test_wfc_no_recursive_entity_refs) { } END_TEST @@ -993,6 +1024,8 @@ index 7b6d9fb..0b5bcf1 100644 + XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS) + == 1); + } ++#else ++ UNUSED_P(usesParameterEntities); +#endif // XML_DTD + + const enum XML_Status status @@ -1020,72 +1053,50 @@ index 7b6d9fb..0b5bcf1 100644 +} +END_TEST + - /* Test incomplete external entities are faulted */ - START_TEST(test_ext_entity_invalid_parse) - { -@@ -5352,6 +5435,34 @@ START_TEST(test_suspend_epilog) - } - END_TEST + START_TEST(test_recursive_external_parameter_entity_2) { + struct TestCase { + const char *doc; +@@ -2958,6 +3043,12 @@ typedef struct elementInfo { + AttrInfo *attributes; + } ElementInfo; -+static void XMLCALL -+suspending_end_handler(void *userData, -+ const XML_Char *UNUSED_P(s)) -+{ -+ XML_StopParser((XML_Parser)userData, 1); -+} + -+START_TEST(test_suspend_in_sole_empty_tag) -+{ -+ const char *text = " "; -+ enum XML_Status rc; ++typedef struct StructParserAndElementInfo { ++ XML_Parser parser; ++ ElementInfo *info; ++} ParserAndElementInfo; + -+ XML_SetEndElementHandler(parser, suspending_end_handler); -+ XML_SetUserData(parser, parser); -+ rc = _XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), -+ XML_TRUE); -+ if (rc == XML_STATUS_ERROR) -+ xml_failure(parser); -+ else if (rc != XML_STATUS_SUSPENDED) -+ fail("Suspend not triggered"); -+ rc = XML_ResumeParser(parser); -+ if (rc == XML_STATUS_ERROR) -+ xml_failure(parser); -+ else if (rc != XML_STATUS_OK) -+ fail("Resume failed"); -+} -+END_TEST -+ - START_TEST(test_unfinished_epilog) - { - const char *text = " <"; -@@ -5671,6 +5782,7 @@ END_TEST + static void XMLCALL + counting_start_element_handler(void *userData, const XML_Char *name, + const XML_Char **atts) { +@@ -5306,6 +5397,7 @@ END_TEST typedef struct ext_hdlr_data { - const char *parse_text; - XML_ExternalEntityRefHandler handler; -+ CharData *storage; + const char *parse_text; + XML_ExternalEntityRefHandler handler; ++ CharData *storage; } ExtHdlrData; static int XMLCALL -@@ -5708,7 +5820,8 @@ START_TEST(test_skipped_null_loaded_ext_entity) - "\n" - "\n" - "%pe2;\n", -- external_entity_null_loader -+ external_entity_null_loader, -+ NULL - }; +@@ -5340,7 +5432,7 @@ START_TEST(test_skipped_null_loaded_ext_entity) { + = {"\n" + "\n" + "%pe2;\n", +- external_entity_null_loader}; ++ external_entity_null_loader, NULL}; - XML_SetUserData(parser, &test_data); -@@ -5729,6 +5842,7 @@ START_TEST(test_skipped_unloaded_ext_entity) - "\n" - "\n" - "%pe2;\n", -+ NULL, - NULL - }; + XML_SetUserData(g_parser, &test_data); + XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); +@@ -5358,7 +5450,7 @@ START_TEST(test_skipped_unloaded_ext_entity) { + = {"\n" + "\n" + "%pe2;\n", +- NULL}; ++ NULL, NULL}; -@@ -6957,6 +7071,24 @@ accumulate_entity_decl(void *userData, - CharData_AppendXMLChars(storage, XCS("\n"), 1); + XML_SetUserData(g_parser, &test_data); + XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); +@@ -6448,6 +6540,25 @@ accumulate_entity_decl(void *userData, const XML_Char *entityName, + CharData_AppendXMLChars(storage, XCS("\n"), 1); } +typedef struct { @@ -1106,13 +1117,26 @@ index 7b6d9fb..0b5bcf1 100644 + } + } +} - - START_TEST(test_utf16_pe) - { -@@ -7423,6 +7555,176 @@ START_TEST(test_empty_element_abort) ++ + START_TEST(test_utf16_pe) { + /* '> +@@ -6901,11 +7012,6 @@ START_TEST(test_pool_integrity_with_unfinished_attr) { } END_TEST +-typedef struct { +- XML_Parser parser; +- CharData *storage; +-} ParserPlusStorage; +- + static void XMLCALL + accumulate_and_suspend_comment_handler(void *userData, const XML_Char *data) { + ParserPlusStorage *const parserPlusStorage = (ParserPlusStorage *)userData; +@@ -6913,6 +7019,147 @@ accumulate_and_suspend_comment_handler(void *userData, const XML_Char *data) { + XML_StopParser(parserPlusStorage->parser, XML_TRUE); + } + +/* Test a possible early return location in internalEntityProcessor */ +START_TEST(test_entity_ref_no_elements) { + const char *const text = "'>\n" +@@ -6943,6 +7190,35 @@ START_TEST(test_nested_entity_suspend) { + } + END_TEST + +START_TEST(test_nested_entity_suspend_2) { + const char *const text = "\n" @@ -1286,78 +1317,8 @@ index 7b6d9fb..0b5bcf1 100644 /* Regression test for quadratic parsing on large tokens */ START_TEST(test_big_tokens_take_linear_time) { const char *const too_slow_failure_message -@@ -7599,6 +7901,69 @@ struct element_decl_data { - int count; - }; - -+typedef struct { -+ XML_Parser parser; -+ int deep; -+} DataIssue240; -+ -+static void -+start_element_issue_240(void *userData, const XML_Char *UNUSED_P(name), -+ const XML_Char **UNUSED_P(atts)) { -+ DataIssue240 *mydata = (DataIssue240 *)userData; -+ mydata->deep++; -+} -+ -+static void -+end_element_issue_240(void *userData, const XML_Char *UNUSED_P(name)) { -+ DataIssue240 *mydata = (DataIssue240 *)userData; -+ mydata->deep--; -+ if (mydata->deep == 0) { -+ XML_StopParser(mydata->parser, 0); -+ } -+} -+ -+START_TEST(test_misc_stop_during_end_handler_issue_240_1) { -+ XML_Parser parser; -+ DataIssue240 *mydata; -+ enum XML_Status result; -+ const char *const doc1 = " "; -+ -+ parser = XML_ParserCreate(NULL); -+ XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240); -+ mydata = (DataIssue240 *)malloc(sizeof(DataIssue240)); -+ mydata->parser = parser; -+ mydata->deep = 0; -+ XML_SetUserData(parser, mydata); -+ -+ result = XML_Parse(parser, doc1, (int)strlen(doc1), 1); -+ XML_ParserFree(parser); -+ free(mydata); -+ if (result != XML_STATUS_ERROR) -+ fail("Stopping the parser did not work as expected"); -+} -+END_TEST -+ -+START_TEST(test_misc_stop_during_end_handler_issue_240_2) { -+ XML_Parser parser; -+ DataIssue240 *mydata; -+ enum XML_Status result; -+ const char *const doc2 = " "; -+ -+ parser = XML_ParserCreate(NULL); -+ XML_SetElementHandler(parser, start_element_issue_240, end_element_issue_240); -+ mydata = (DataIssue240 *)malloc(sizeof(DataIssue240)); -+ mydata->parser = parser; -+ mydata->deep = 0; -+ XML_SetUserData(parser, mydata); -+ -+ result = XML_Parse(parser, doc2, (int)strlen(doc2), 1); -+ XML_ParserFree(parser); -+ free(mydata); -+ if (result != XML_STATUS_ERROR) -+ fail("Stopping the parser did not work as expected"); -+} -+END_TEST -+ - static void - element_decl_counter(void *userData, const XML_Char *UNUSED_P(name), XML_Content *model) { - struct element_decl_data *testdata = (struct element_decl_data *)userData; -@@ -8939,6 +9304,29 @@ static void *duff_reallocator(void *ptr, size_t size) - return realloc(ptr, size); +@@ -8349,6 +8625,29 @@ duff_reallocator(void *ptr, size_t size) { + return realloc(ptr, size); } +// Portable remake of strndup(3) for C99; does not care about space efficiency @@ -1384,9 +1345,9 @@ index 7b6d9fb..0b5bcf1 100644 +} + /* Test that a failure to allocate the parser structure fails gracefully */ - START_TEST(test_misc_alloc_create_parser) - { -@@ -9174,7 +9562,7 @@ END_TEST + START_TEST(test_misc_alloc_create_parser) { + XML_Memory_Handling_Suite memsuite = {duff_allocator, realloc, free}; +@@ -8723,7 +9022,7 @@ END_TEST START_TEST(test_misc_stopparser_rejects_unstarted_parser) { const XML_Bool cases[] = {XML_TRUE, XML_FALSE}; for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { @@ -1395,7 +1356,7 @@ index 7b6d9fb..0b5bcf1 100644 XML_Parser parser = XML_ParserCreate(NULL); assert_true(XML_GetErrorCode(parser) == XML_ERROR_NONE); assert_true(XML_StopParser(parser, resumable) == XML_STATUS_ERROR); -@@ -9184,6 +9572,105 @@ START_TEST(test_misc_stopparser_rejects_unstarted_parser) { +@@ -8733,6 +9032,105 @@ START_TEST(test_misc_stopparser_rejects_unstarted_parser) { } END_TEST @@ -1499,12 +1460,12 @@ index 7b6d9fb..0b5bcf1 100644 +END_TEST + static void - alloc_setup(void) - { -@@ -9809,6 +10296,31 @@ START_TEST(test_alloc_internal_entity) + alloc_setup(void) { + XML_Memory_Handling_Suite memsuite = {duff_allocator, duff_reallocator, free}; +@@ -9311,6 +9709,31 @@ START_TEST(test_alloc_internal_entity) { + } END_TEST - +START_TEST(test_alloc_parameter_entity) { + const char *text = "\">" @@ -1515,8 +1476,8 @@ index 7b6d9fb..0b5bcf1 100644 + + for (i = 0; i < alloc_test_max_repeats; i++) { + allocation_count = i; -+ XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); -+ if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) ++ XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); ++ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + != XML_STATUS_ERROR) + break; + alloc_teardown(); @@ -1533,63 +1494,42 @@ index 7b6d9fb..0b5bcf1 100644 /* Test the robustness against allocation failure of element handling * Based on test_dtd_default_handling(). */ -@@ -13114,6 +13626,7 @@ make_suite(void) - tcase_add_test(tc_basic, - test_entity_with_external_subset_unless_standalone); - tcase_add_test(tc_basic, test_wfc_no_recursive_entity_refs); -+ tcase_add_test(tc_basic, test_no_indirectly_recursive_entity_refs); - tcase_add_test(tc_basic, test_ext_entity_set_encoding); - tcase_add_test(tc_basic, test_ext_entity_no_handler); - tcase_add_test(tc_basic, test_ext_entity_set_bom); -@@ -13203,6 +13716,7 @@ make_suite(void) - tcase_add_test(tc_basic, test_abort_epilog); - tcase_add_test(tc_basic, test_abort_epilog_2); - tcase_add_test(tc_basic, test_suspend_epilog); -+ tcase_add_test(tc_basic, test_suspend_in_sole_empty_tag); - tcase_add_test(tc_basic, test_unfinished_epilog); - tcase_add_test(tc_basic, test_partial_char_in_epilog); - tcase_add_test(tc_basic, test_hash_collision); -@@ -13287,6 +13801,14 @@ make_suite(void) - tcase_add_test(tc_basic, test_bad_notation); - tcase_add_test(tc_basic, test_default_doctype_handler); - tcase_add_test(tc_basic, test_empty_element_abort); -+#ifdef XML_DTD -+ tcase_add_test(tc_basic, test_entity_ref_no_elements); -+ tcase_add_test(tc_basic, test_deep_nested_entity); -+ tcase_add_test(tc_basic, test_deep_nested_attribute_entity); -+ tcase_add_test(tc_basic, -+ test_deep_nested_entity_delayed_interpretation); -+ tcase_add_test(tc_basic, test_nested_entity_suspend_2); -+#endif - tcase_add_test(tc_basic, test_big_tokens_take_linear_time); - tcase_add_test(tc_basic, test_set_reparse_deferral); - tcase_add_test(tc_basic, test_reparse_deferral_is_inherited); -@@ -13343,12 +13865,18 @@ make_suite(void) - tcase_add_test(tc_misc, test_misc_features); - tcase_add_test(tc_misc, test_misc_attribute_leak); - tcase_add_test(tc_misc, test_misc_utf16le); -+ tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_1); -+ tcase_add_test(tc_misc, test_misc_stop_during_end_handler_issue_240_2); - #ifdef XML_DTD - tcase_add_test(tc_misc, - test_misc_deny_internal_entity_closing_doctype_issue_317); - #endif - tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing); - tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser); -+#ifdef XML_DTD -+ tcase_add_test(tc_misc, test_renter_loop_finite_content); -+#endif -+ tcase_add_test(tc_misc, test_misc_expected_event_ptr_issue_980); +@@ -12955,6 +13378,7 @@ make_suite(void) { + test_wfc_undeclared_entity_with_external_subset_standalone); + tcase_add_test(tc_basic, test_entity_with_external_subset_unless_standalone); + tcase_add_test(tc_basic, test_wfc_no_recursive_entity_refs); ++ tcase_add_test(tc_basic, test_no_indirectly_recursive_entity_refs); + tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_set_encoding); + tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_no_handler); + tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_set_bom); +@@ -13137,7 +13561,13 @@ make_suite(void) { + tcase_add_test(tc_basic, test_empty_element_abort); + tcase_add_test__ifdef_xml_dtd(tc_basic, + test_pool_integrity_with_unfinished_attr); ++ tcase_add_test__ifdef_xml_dtd(tc_basic, test_entity_ref_no_elements); ++ tcase_add_test__ifdef_xml_dtd(tc_basic, test_deep_nested_entity); ++ tcase_add_test__ifdef_xml_dtd(tc_basic, test_deep_nested_attribute_entity); ++ tcase_add_test__ifdef_xml_dtd(tc_basic, ++ test_deep_nested_entity_delayed_interpretation); + tcase_add_test(tc_basic, test_nested_entity_suspend); ++ tcase_add_test__ifdef_xml_dtd(tc_basic, test_nested_entity_suspend_2); + tcase_add_test(tc_basic, test_big_tokens_take_linear_time); + tcase_add_test(tc_basic, test_set_reparse_deferral); + tcase_add_test(tc_basic, test_reparse_deferral_is_inherited); +@@ -13200,6 +13630,8 @@ make_suite(void) { + tcase_add_test(tc_misc, test_misc_tag_mismatch_reset_leak); + tcase_add_test(tc_misc, test_misc_resumeparser_not_crashing); + tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser); ++ tcase_add_test__ifdef_xml_dtd(tc_misc, test_renter_loop_finite_content); ++ tcase_add_test(tc_misc, test_misc_expected_event_ptr_issue_980); - suite_add_tcase(s, tc_alloc); - tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown); -@@ -13365,6 +13893,9 @@ make_suite(void) - tcase_add_test(tc_alloc, test_alloc_external_entity); - tcase_add_test(tc_alloc, test_alloc_ext_entity_set_encoding); - tcase_add_test(tc_alloc, test_alloc_internal_entity); -+#ifdef XML_DTD -+ tcase_add_test(tc_alloc, test_alloc_parameter_entity); -+#endif - tcase_add_test(tc_alloc, test_alloc_dtd_default_handling); - tcase_add_test(tc_alloc, test_alloc_explicit_encoding); - tcase_add_test(tc_alloc, test_alloc_set_base); + suite_add_tcase(s, tc_alloc); + tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown); +@@ -13216,6 +13648,7 @@ make_suite(void) { + tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_external_entity); + tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_ext_entity_set_encoding); + tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_internal_entity); ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_parameter_entity); + tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_dtd_default_handling); + tcase_add_test(tc_alloc, test_alloc_explicit_encoding); + tcase_add_test(tc_alloc, test_alloc_set_base); diff --git a/expat-2.5.0-CVE-2025-59375.patch b/expat-2.5.0-CVE-2025-59375.patch new file mode 100644 index 0000000..644cd09 --- /dev/null +++ b/expat-2.5.0-CVE-2025-59375.patch @@ -0,0 +1,1526 @@ +commit e16f2e3d9c3fa181dcaeed312ea9bc7de8257942 +Author: Tomas Korbar +Date: Tue Nov 18 13:01:31 2025 +0100 + + Fix CVE-2025-59375 + +diff --git a/expat/doc/reference.html b/expat/doc/reference.html +index d618bd8..3358ecc 100644 +--- a/expat/doc/reference.html ++++ b/expat/doc/reference.html +@@ -155,6 +155,8 @@ interface. + +
Sets a handler for element declarations in a DTD. The handler gets
+ called with the name of the element in the declaration and a pointer
+ to a structure that contains the element model. It's the user code's
+-responsibility to free model when finished with it. See
++responsibility to free model when finished with via a call to
+ XML_FreeContentModel.
+ There is no need to free the model from the handler, it can be kept
+ around and freed at a later stage.
++/* Added in Expat 2.7.2. */ ++XML_Bool ++XML_SetAllocTrackerMaximumAmplification(XML_Parser p, ++ float maximumAmplificationFactor); ++++
++ Sets the maximum tolerated amplification factor
++ between direct input and bytes of dynamic memory allocated
++ (default: 100.0)
++ of parser p to maximumAmplificationFactor, and
++ returns XML_TRUE upon success and XML_FALSE upon error.
++
++ Note: ++ There are three types of allocations that intentionally bypass tracking and limiting: ++
++XML_MemMalloc
++ and
++ XML_MemRealloc
++ —
++ healthy use of these two functions continues to be a responsibility
++ of the application using Expat
++ —,
++ XML_GetBuffer
++ and
++ XML_ParseBuffer
++ (and thus also by plain
++ XML_Parse), and
++ XML_FreeContentModel).
++ The amplification factor is calculated as ..
++amplification := allocated / direct++
++ .. while parsing, whereas
++ direct is the number of bytes read from the primary document in parsing and
++ allocated is the number of bytes of dynamic memory allocated in the parser hierarchy.
++
For a call to XML_SetAllocTrackerMaximumAmplification to succeed:
p must be a non-NULL root parser (without any parent parsers) andmaximumAmplificationFactor must be non-NaN and greater than or equal to 1.0.++ Note: ++ If you ever need to increase this value for non-attack payload, ++ please file a bug report. ++
++ ++++ Note: ++ Amplifications factors greater than 100 can been observed near the start of parsing ++ even with benign files in practice. ++ ++ So if you do reduce the maximum allowed amplification, ++ please make sure that the activation threshold is still big enough ++ to not end up with undesired false positives (i.e. benign files being rejected). ++
++++/* Added in Expat 2.7.2. */ ++XML_Bool ++XML_SetAllocTrackerActivationThreshold(XML_Parser p, ++ unsigned long long activationThresholdBytes); ++++
++ Sets number of allocated bytes of dynamic memory
++ needed to activate protection against disproportionate use of RAM
++ (default: 64 MiB)
++ of parser p to activationThresholdBytes, and
++ returns XML_TRUE upon success and XML_FALSE upon error.
++
++ Note:
++ For types of allocations that intentionally bypass tracking and limiting, please see
++ XML_SetAllocTrackerMaximumAmplification
++ above.
++
For a call to XML_SetAllocTrackerActivationThreshold to succeed:
p must be a non-NULL root parser (without any parent parsers).++ Note: ++ If you ever need to increase this value for non-attack payload, ++ please file a bug report. ++
+++ /* Added in Expat 2.6.0. */ +diff --git a/expat/doc/xmlwf.xml b/expat/doc/xmlwf.xml +index 3d35393..aeb2dc0 100644 +--- a/expat/doc/xmlwf.xml ++++ b/expat/doc/xmlwf.xml +@@ -152,19 +152,31 @@ supports both. ++ + Sets the maximum tolerated amplification factor +- for protection against billion laughs attacks (default: 100.0). ++ for protection against amplification attacks ++ like the billion laughs attack ++ (default: 100.0 ++ for the sum of direct and indirect output and also ++ for allocations of dynamic memory). + The amplification factor is calculated as .. + ++ amplification := (direct + indirect) / direct + ++- .. while parsing, whereas ++ .. with regard to use of entities and .. ++ ++++ amplification := allocated / direct ++ ++++ .. with regard to dynamic memory while parsing. + <direct> is the number of bytes read +- from the primary document in parsing and ++ from the primary document in parsing, + <indirect> is the number of bytes + added by expanding entities and reading of external DTD files, +- combined. ++ combined, and ++ <allocated> is the total number of bytes of dynamic memory ++ allocated (and not freed) per hierarchy of parsers. + ++ NOTE : +@@ -179,8 +191,10 @@ supports both. ++ + Sets the number of output bytes (including amplification) +- needed to activate protection against billion laughs attacks +- (default: 8 MiB). ++ needed to activate protection against amplification attacks ++ like billion laughs ++ (default: 8 MiB for the sum of direct and indirect output, ++ and 64 MiB for allocations of dynamic memory). + This can be thought of as an "activation threshold". + ++diff --git a/expat/lib/expat.h b/expat/lib/expat.h +index 69b0ba1..6eae1d6 100644 +--- a/expat/lib/expat.h ++++ b/expat/lib/expat.h +@@ -1028,8 +1028,11 @@ enum XML_FeatureEnum { + XML_FEATURE_ATTR_INFO, + /* Added in Expat 2.4.0. */ + XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, +- XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT ++ XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, + /* Additional features must be added to the end of this enum. */ ++ /* Added in Expat 2.7.2. */ ++ XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, ++ XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, + }; + + typedef struct { +@@ -1051,6 +1054,16 @@ XML_SetBillionLaughsAttackProtectionMaximumAmplification( + XMLPARSEAPI(XML_Bool) + XML_SetBillionLaughsAttackProtectionActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes); ++ ++/* Added in Expat 2.7.2. */ ++XMLPARSEAPI(XML_Bool) ++XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, ++ float maximumAmplificationFactor); ++ ++/* Added in Expat 2.7.2. */ ++XMLPARSEAPI(XML_Bool) ++XML_SetAllocTrackerActivationThreshold( ++ XML_Parser parser, unsigned long long activationThresholdBytes); + #endif + + /* Added in Expat 2.6.0. */ +diff --git a/expat/lib/internal.h b/expat/lib/internal.h +index e2709c8..1d1e7da 100644 +--- a/expat/lib/internal.h ++++ b/expat/lib/internal.h +@@ -107,6 +107,7 @@ + #endif + + #include // ULONG_MAX ++#include // size_t + + #if defined(_WIN32) \ + && (! defined(__USE_MINGW_ANSI_STDIO) \ +@@ -144,6 +145,16 @@ + 100.0f + #define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \ + 8388608 // 8 MiB, 2^23 ++ ++#define EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT 100.0f ++#define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT \ ++ 67108864 // 64 MiB, 2^26 ++ ++// NOTE: If function expat_alloc was user facing, EXPAT_MALLOC_ALIGNMENT would ++// have to take sizeof(long double) into account ++#define EXPAT_MALLOC_ALIGNMENT sizeof(long long) // largest parser (sub)member ++#define EXPAT_MALLOC_PADDING ((EXPAT_MALLOC_ALIGNMENT) - sizeof(size_t)) ++ + /* NOTE END */ + + #include "expat.h" // so we can use type XML_Parser below +@@ -163,6 +174,9 @@ const char *unsignedCharToPrintable(unsigned char c); + + extern XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c + extern unsigned int g_parseAttempts; // used for testing only ++void *expat_malloc(XML_Parser parser, size_t size, int sourceLine); ++void expat_free(XML_Parser parser, void *ptr, int sourceLine); ++void *expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine); + + #ifdef __cplusplus + } +diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c +index d68d2c8..cba41f4 100644 +--- a/expat/lib/xmlparse.c ++++ b/expat/lib/xmlparse.c +@@ -214,7 +214,7 @@ typedef struct { + unsigned char power; + size_t size; + size_t used; +- const XML_Memory_Handling_Suite *mem; ++ XML_Parser parser; + } HASH_TABLE; + + static size_t keylen(KEY s); +@@ -337,7 +337,7 @@ typedef struct { + const XML_Char *end; + XML_Char *ptr; + XML_Char *start; +- const XML_Memory_Handling_Suite *mem; ++ XML_Parser parser; + } STRING_POOL; + + /* The XML_Char before the name is used to determine whether +@@ -432,6 +432,14 @@ typedef struct accounting { + unsigned long long activationThresholdBytes; + } ACCOUNTING; + ++typedef struct MALLOC_TRACKER { ++ XmlBigCount bytesAllocated; ++ XmlBigCount peakBytesAllocated; // updated live only for debug level >=2 ++ unsigned long debugLevel; ++ float maximumAmplificationFactor; // >=1.0 ++ XmlBigCount activationThresholdBytes; ++} MALLOC_TRACKER; ++ + typedef struct entity_stats { + unsigned int countEverOpened; + unsigned int currentDepth; +@@ -530,26 +538,23 @@ static XML_Bool setContext(XML_Parser parser, const XML_Char *context); + + static void FASTCALL normalizePublicId(XML_Char *s); + +-static DTD *dtdCreate(const XML_Memory_Handling_Suite *ms); ++static DTD *dtdCreate(XML_Parser parser); + /* do not call if m_parentParser != NULL */ +-static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); +-static void dtdDestroy(DTD *p, XML_Bool isDocEntity, +- const XML_Memory_Handling_Suite *ms); ++static void dtdReset(DTD *p, XML_Parser parser); ++static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser); + static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, +- const XML_Memory_Handling_Suite *ms); ++ XML_Parser parser); + static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *, STRING_POOL *, + const HASH_TABLE *); + static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name, + size_t createSize); +-static void FASTCALL hashTableInit(HASH_TABLE *, +- const XML_Memory_Handling_Suite *ms); ++static void FASTCALL hashTableInit(HASH_TABLE *table, XML_Parser parser); + static void FASTCALL hashTableClear(HASH_TABLE *); + static void FASTCALL hashTableDestroy(HASH_TABLE *); + static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); + static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *); + +-static void FASTCALL poolInit(STRING_POOL *, +- const XML_Memory_Handling_Suite *ms); ++static void FASTCALL poolInit(STRING_POOL *pool, XML_Parser parser); + static void FASTCALL poolClear(STRING_POOL *); + static void FASTCALL poolDestroy(STRING_POOL *); + static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, +@@ -569,15 +574,15 @@ static XML_Content *build_model(XML_Parser parser); + static ELEMENT_TYPE *getElementType(XML_Parser parser, const ENCODING *enc, + const char *ptr, const char *end); + +-static XML_Char *copyString(const XML_Char *s, +- const XML_Memory_Handling_Suite *memsuite); ++static XML_Char *copyString(const XML_Char *s, XML_Parser parser); + + static unsigned long generate_hash_secret_salt(XML_Parser parser); + static XML_Bool startParsing(XML_Parser parser); + + static XML_Parser parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, +- const XML_Char *nameSep, DTD *dtd); ++ const XML_Char *nameSep, DTD *dtd, ++ XML_Parser parentParser); + + static void parserInit(XML_Parser parser, const XML_Char *encodingName); + +@@ -734,14 +739,215 @@ struct XML_ParserStruct { + unsigned long m_hash_secret_salt; + #ifdef XML_DTD + ACCOUNTING m_accounting; ++ MALLOC_TRACKER m_alloc_tracker; + ENTITY_STATS m_entity_stats; + #endif + XML_Bool m_reenter; + }; + +-#define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) +-#define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s))) +-#define FREE(parser, p) (parser->m_mem.free_fcn((p))) ++# define MALLOC(parser, s) (expat_malloc((parser), (s), __LINE__)) ++# define REALLOC(parser, p, s) (expat_realloc((parser), (p), (s), __LINE__)) ++# define FREE(parser, p) (expat_free((parser), (p), __LINE__)) ++ ++static void ++expat_heap_stat(XML_Parser rootParser, char operator, XmlBigCount absDiff, ++ XmlBigCount newTotal, XmlBigCount peakTotal, int sourceLine) { ++ // NOTE: This can be +infinity or -nan ++ const float amplification ++ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; ++ fprintf( ++ stderr, ++ "expat: Allocations(%p): Direct " EXPAT_FMT_ULL("10") ", allocated %c" EXPAT_FMT_ULL( ++ "10") " to " EXPAT_FMT_ULL("10") " (" EXPAT_FMT_ULL("10") " peak), amplification %8.2f (xmlparse.c:%d)\n", ++ (void *)rootParser, rootParser->m_accounting.countBytesDirect, operator, ++ absDiff, newTotal, peakTotal, (double)amplification, sourceLine); ++} ++ ++static bool ++expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase, ++ int sourceLine) { ++ assert(rootParser != NULL); ++ assert(increase > 0); ++ ++ XmlBigCount newTotal = 0; ++ bool tolerable = true; ++ ++ // Detect integer overflow ++ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated < increase) { ++ tolerable = false; ++ } else { ++ newTotal = rootParser->m_alloc_tracker.bytesAllocated + increase; ++ ++ if (newTotal >= rootParser->m_alloc_tracker.activationThresholdBytes) { ++ assert(newTotal > 0); ++ // NOTE: This can be +infinity when dividing by zero but not -nan ++ const float amplification ++ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; ++ if (amplification ++ > rootParser->m_alloc_tracker.maximumAmplificationFactor) { ++ tolerable = false; ++ } ++ } ++ } ++ ++ if (! tolerable && (rootParser->m_alloc_tracker.debugLevel >= 1)) { ++ expat_heap_stat(rootParser, '+', increase, newTotal, newTotal, sourceLine); ++ } ++ ++ return tolerable; ++} ++ ++void *expat_malloc(XML_Parser parser, size_t size, int sourceLine) { ++ // Detect integer overflow ++ if (SIZE_MAX - size < sizeof(size_t) + EXPAT_MALLOC_PADDING) { ++ return NULL; ++ } ++ ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); ++ assert(rootParser->m_parentParser == NULL); ++ ++ const size_t bytesToAllocate = sizeof(size_t) + EXPAT_MALLOC_PADDING + size; ++ ++ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated ++ < bytesToAllocate) { ++ return NULL; // i.e. signal integer overflow as out-of-memory ++ } ++ ++ if (! expat_heap_increase_tolerable(rootParser, bytesToAllocate, ++ sourceLine)) { ++ return NULL; // i.e. signal violation as out-of-memory ++ } ++ ++ // Actually allocate ++ void *const mallocedPtr = parser->m_mem.malloc_fcn(bytesToAllocate); ++ ++ if (mallocedPtr == NULL) { ++ return NULL; ++ } ++ ++ // Update in-block recorded size ++ *(size_t *)mallocedPtr = size; ++ ++ // Update accounting ++ rootParser->m_alloc_tracker.bytesAllocated += bytesToAllocate; ++ ++ // Report as needed ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { ++ if (rootParser->m_alloc_tracker.bytesAllocated ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { ++ rootParser->m_alloc_tracker.peakBytesAllocated ++ = rootParser->m_alloc_tracker.bytesAllocated; ++ } ++ expat_heap_stat(rootParser, '+', bytesToAllocate, ++ rootParser->m_alloc_tracker.bytesAllocated, ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); ++ } ++ ++ return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING; ++} ++ ++void expat_free(XML_Parser parser, void *ptr, int sourceLine) { ++ assert(parser != NULL); ++ ++ if (ptr == NULL) { ++ return; ++ } ++ ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); ++ assert(rootParser->m_parentParser == NULL); ++ ++ // Extract size (to the eyes of malloc_fcn/realloc_fcn) and ++ // the original pointer returned by malloc/realloc ++ void *const mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t); ++ const size_t bytesAllocated ++ = sizeof(size_t) + EXPAT_MALLOC_PADDING + *(size_t *)mallocedPtr; ++ ++ // Update accounting ++ assert(rootParser->m_alloc_tracker.bytesAllocated >= bytesAllocated); ++ rootParser->m_alloc_tracker.bytesAllocated -= bytesAllocated; ++ ++ // Report as needed ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { ++ expat_heap_stat(rootParser, '-', bytesAllocated, ++ rootParser->m_alloc_tracker.bytesAllocated, ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); ++ } ++ ++ // NOTE: This may be freeing rootParser, so freeing has to come last ++ parser->m_mem.free_fcn(mallocedPtr); ++} ++ ++void *expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) { ++ assert(parser != NULL); ++ ++ if (ptr == NULL) { ++ return expat_malloc(parser, size, sourceLine); ++ } ++ ++ if (size == 0) { ++ expat_free(parser, ptr, sourceLine); ++ return NULL; ++ } ++ ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); ++ assert(rootParser->m_parentParser == NULL); ++ ++ // Extract original size (to the eyes of the caller) and the original ++ // pointer returned by malloc/realloc ++ void *mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t); ++ const size_t prevSize = *(size_t *)mallocedPtr; ++ ++ // Classify upcoming change ++ const bool isIncrease = (size > prevSize); ++ const size_t absDiff ++ = (size > prevSize) ? (size - prevSize) : (prevSize - size); ++ ++ // Ask for permission from accounting ++ if (isIncrease) { ++ if (! expat_heap_increase_tolerable(rootParser, absDiff, sourceLine)) { ++ return NULL; // i.e. signal violation as out-of-memory ++ } ++ } ++ ++ // NOTE: Integer overflow detection has already been done for us ++ // by expat_heap_increase_tolerable(..) above ++ assert(SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING >= size); ++ ++ // Actually allocate ++ mallocedPtr = parser->m_mem.realloc_fcn( ++ mallocedPtr, sizeof(size_t) + EXPAT_MALLOC_PADDING + size); ++ ++ if (mallocedPtr == NULL) { ++ return NULL; ++ } ++ ++ // Update accounting ++ if (isIncrease) { ++ assert((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated ++ >= absDiff); ++ rootParser->m_alloc_tracker.bytesAllocated += absDiff; ++ } else { // i.e. decrease ++ assert(rootParser->m_alloc_tracker.bytesAllocated >= absDiff); ++ rootParser->m_alloc_tracker.bytesAllocated -= absDiff; ++ } ++ ++ // Report as needed ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { ++ if (rootParser->m_alloc_tracker.bytesAllocated ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { ++ rootParser->m_alloc_tracker.peakBytesAllocated ++ = rootParser->m_alloc_tracker.bytesAllocated; ++ } ++ expat_heap_stat(rootParser, isIncrease ? '+' : '-', absDiff, ++ rootParser->m_alloc_tracker.bytesAllocated, ++ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); ++ } ++ ++ // Update in-block recorded size ++ *(size_t *)mallocedPtr = size; ++ ++ return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING; ++} + + XML_Parser XMLCALL + XML_ParserCreate(const XML_Char *encodingName) { +@@ -1059,19 +1265,34 @@ XML_Parser XMLCALL + XML_ParserCreate_MM(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep) { +- return parserCreate(encodingName, memsuite, nameSep, NULL); ++ return parserCreate(encodingName, memsuite, nameSep, NULL, NULL); + } + + static XML_Parser + parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep, +- DTD *dtd) { +- XML_Parser parser; ++ DTD *dtd, XML_Parser parentParser) { ++ XML_Parser parser = NULL; ++ ++ const size_t increase ++ = sizeof(size_t) + EXPAT_MALLOC_PADDING + sizeof(struct XML_ParserStruct); ++ ++ if (parentParser != NULL) { ++ const XML_Parser rootParser = getRootParserOf(parentParser, NULL); ++ if (! expat_heap_increase_tolerable(rootParser, increase, __LINE__)) { ++ return NULL; ++ } ++ } + + if (memsuite) { + XML_Memory_Handling_Suite *mtemp; +- parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); +- if (parser != NULL) { ++ void *const sizeAndParser ++ = memsuite->malloc_fcn(sizeof(size_t) + EXPAT_MALLOC_PADDING ++ + sizeof(struct XML_ParserStruct)); ++ if (sizeAndParser != NULL) { ++ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct); ++ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t) ++ + EXPAT_MALLOC_PADDING); + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = memsuite->malloc_fcn; + mtemp->realloc_fcn = memsuite->realloc_fcn; +@@ -1079,18 +1300,59 @@ parserCreate(const XML_Char *encodingName, + } + } else { + XML_Memory_Handling_Suite *mtemp; +- parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); +- if (parser != NULL) { ++ void *const sizeAndParser = malloc(sizeof(size_t) + EXPAT_MALLOC_PADDING ++ + sizeof(struct XML_ParserStruct)); ++ if (sizeAndParser != NULL) { ++ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct); ++ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t) ++ + EXPAT_MALLOC_PADDING); + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = malloc; + mtemp->realloc_fcn = realloc; + mtemp->free_fcn = free; + } +- } ++ } // cppcheck-suppress[memleak symbolName=sizeAndParser] // Cppcheck >=2.18.0 + + if (! parser) + return parser; + ++ // Initialize .m_alloc_tracker ++ memset(&parser->m_alloc_tracker, 0, sizeof(MALLOC_TRACKER)); ++ if (parentParser == NULL) { ++ parser->m_alloc_tracker.debugLevel ++ = getDebugLevel("EXPAT_MALLOC_DEBUG", 0u); ++ parser->m_alloc_tracker.maximumAmplificationFactor ++ = EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT; ++ parser->m_alloc_tracker.activationThresholdBytes ++ = EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT; ++ ++ // NOTE: This initialization needs to come this early because these fields ++ // are read by allocation tracking code ++ parser->m_parentParser = NULL; ++ parser->m_accounting.countBytesDirect = 0; ++ } else { ++ parser->m_parentParser = parentParser; ++ } ++ ++ // Record XML_ParserStruct allocation we did a few lines up before ++ const XML_Parser rootParser = getRootParserOf(parser, NULL); ++ assert(rootParser->m_parentParser == NULL); ++ assert(SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated >= increase); ++ rootParser->m_alloc_tracker.bytesAllocated += increase; ++ ++ // Report on allocation ++ if (rootParser->m_alloc_tracker.debugLevel >= 2) { ++ if (rootParser->m_alloc_tracker.bytesAllocated ++ > rootParser->m_alloc_tracker.peakBytesAllocated) { ++ rootParser->m_alloc_tracker.peakBytesAllocated ++ = rootParser->m_alloc_tracker.bytesAllocated; ++ } ++ ++ expat_heap_stat(rootParser, '+', increase, ++ rootParser->m_alloc_tracker.bytesAllocated, ++ rootParser->m_alloc_tracker.peakBytesAllocated, __LINE__); ++ } ++ + parser->m_buffer = NULL; + parser->m_bufferLim = NULL; + +@@ -1125,7 +1387,7 @@ parserCreate(const XML_Char *encodingName, + if (dtd) + parser->m_dtd = dtd; + else { +- parser->m_dtd = dtdCreate(&parser->m_mem); ++ parser->m_dtd = dtdCreate(parser); + if (parser->m_dtd == NULL) { + FREE(parser, parser->m_dataBuf); + FREE(parser, parser->m_atts); +@@ -1159,8 +1421,8 @@ parserCreate(const XML_Char *encodingName, + + parser->m_protocolEncodingName = NULL; + +- poolInit(&parser->m_tempPool, &(parser->m_mem)); +- poolInit(&parser->m_temp2Pool, &(parser->m_mem)); ++ poolInit(&parser->m_tempPool, parser); ++ poolInit(&parser->m_temp2Pool, parser); + parserInit(parser, encodingName); + + if (encodingName && ! parser->m_protocolEncodingName) { +@@ -1192,7 +1454,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { + parser->m_processor = prologInitProcessor; + XmlPrologStateInit(&parser->m_prologState); + if (encodingName != NULL) { +- parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem)); ++ parser->m_protocolEncodingName = copyString(encodingName, parser); + } + parser->m_curBase = NULL; + XmlInitEncoding(&parser->m_initEncoding, &parser->m_encoding, 0); +@@ -1254,7 +1516,6 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { + parser->m_unknownEncodingMem = NULL; + parser->m_unknownEncodingRelease = NULL; + parser->m_unknownEncodingData = NULL; +- parser->m_parentParser = NULL; + parser->m_parsingStatus.parsing = XML_INITIALIZED; + // Reentry can only be triggered inside m_processor calls + parser->m_reenter = XML_FALSE; +@@ -1344,7 +1605,7 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) { + FREE(parser, (void *)parser->m_protocolEncodingName); + parser->m_protocolEncodingName = NULL; + parserInit(parser, encodingName); +- dtdReset(parser->m_dtd, &parser->m_mem); ++ dtdReset(parser->m_dtd, parser); + return XML_TRUE; + } + +@@ -1380,7 +1641,7 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) { + parser->m_protocolEncodingName = NULL; + else { + /* Copy the new encoding name into allocated memory */ +- parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem)); ++ parser->m_protocolEncodingName = copyString(encodingName, parser); + if (! parser->m_protocolEncodingName) + return XML_STATUS_ERROR; + } +@@ -1489,9 +1750,10 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, + */ + if (parser->m_ns) { + XML_Char tmp[2] = {parser->m_namespaceSeparator, 0}; +- parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); ++ parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd, oldParser); + } else { +- parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); ++ parser ++ = parserCreate(encodingName, &parser->m_mem, NULL, newDtd, oldParser); + } + + if (! parser) +@@ -1535,7 +1797,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, + parser->m_prologState.inEntityValue = oldInEntityValue; + if (context) { + #endif /* XML_DTD */ +- if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, &parser->m_mem) ++ if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, parser) + || ! setContext(parser, context)) { + XML_ParserFree(parser); + return NULL; +@@ -1647,14 +1909,16 @@ XML_ParserFree(XML_Parser parser) { + #else + if (parser->m_dtd) + #endif /* XML_DTD */ +- dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, +- &parser->m_mem); ++ dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, parser); + FREE(parser, (void *)parser->m_atts); + #ifdef XML_ATTR_INFO + FREE(parser, (void *)parser->m_attInfo); + #endif + FREE(parser, parser->m_groupConnector); +- FREE(parser, parser->m_buffer); ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer ++ // is not being allocated with MALLOC(..) but with plain ++ // .malloc_fcn(..). ++ parser->m_mem.free_fcn(parser->m_buffer); + FREE(parser, parser->m_dataBuf); + FREE(parser, parser->m_nsAtts); + FREE(parser, parser->m_unknownEncodingMem); +@@ -2246,7 +2510,9 @@ XML_GetBuffer(XML_Parser parser, int len) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } +- newBuf = (char *)MALLOC(parser, bufferSize); ++ // NOTE: We are avoiding MALLOC(..) here to leave limiting ++ // the input size to the application using Expat. ++ newBuf = (char *)parser->m_mem.malloc_fcn(bufferSize); + if (newBuf == 0) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return NULL; +@@ -2257,7 +2523,10 @@ XML_GetBuffer(XML_Parser parser, int len) { + memcpy(newBuf, &parser->m_bufferPtr[-keep], + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) + + keep); +- FREE(parser, parser->m_buffer); ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer ++ // is not being allocated with MALLOC(..) but with plain ++ // .malloc_fcn(..). ++ parser->m_mem.free_fcn(parser->m_buffer); + parser->m_buffer = newBuf; + parser->m_bufferEnd + = parser->m_buffer +@@ -2273,7 +2542,10 @@ XML_GetBuffer(XML_Parser parser, int len) { + if (parser->m_bufferPtr) { + memcpy(newBuf, parser->m_bufferPtr, + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)); +- FREE(parser, parser->m_buffer); ++ // NOTE: We are avoiding FREE(..) here because parser->m_buffer ++ // is not being allocated with MALLOC(..) but with plain ++ // .malloc_fcn(..). ++ parser->m_mem.free_fcn(parser->m_buffer); + parser->m_bufferEnd + = newBuf + + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr); +@@ -2451,28 +2723,43 @@ XML_GetCurrentColumnNumber(XML_Parser parser) { + + void XMLCALL + XML_FreeContentModel(XML_Parser parser, XML_Content *model) { +- if (parser != NULL) +- FREE(parser, model); ++ if (parser == NULL) ++ return; ++ ++ // NOTE: We are avoiding FREE(..) here because the content model ++ // has been created using plain .malloc_fcn(..) rather than MALLOC(..). ++ parser->m_mem.free_fcn(model); + } + + void *XMLCALL + XML_MemMalloc(XML_Parser parser, size_t size) { + if (parser == NULL) + return NULL; +- return MALLOC(parser, size); ++ ++ // NOTE: We are avoiding MALLOC(..) here to not include ++ // user allocations with allocation tracking and limiting. ++ return parser->m_mem.malloc_fcn(size); + } + + void *XMLCALL + XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) { + if (parser == NULL) + return NULL; +- return REALLOC(parser, ptr, size); ++ ++ // NOTE: We are avoiding REALLOC(..) here to not include ++ // user allocations with allocation tracking and limiting. ++ return parser->m_mem.realloc_fcn(ptr, size); + } + + void XMLCALL + XML_MemFree(XML_Parser parser, void *ptr) { +- if (parser != NULL) +- FREE(parser, ptr); ++ if (parser == NULL) ++ return; ++ ++ // NOTE: We are avoiding FREE(..) here because XML_MemMalloc and ++ // XML_MemRealloc are not using MALLOC(..) and REALLOC(..) ++ // but plain .malloc_fcn(..) and .realloc_fcn(..), internally. ++ parser->m_mem.free_fcn(ptr); + } + + void XMLCALL +@@ -2669,6 +2956,13 @@ XML_GetFeatureList(void) { + {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, + XML_L("XML_BLAP_ACT_THRES"), + EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT}, ++ /* Added in Expat 2.7.2. */ ++ {XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, ++ XML_L("XML_AT_MAX_AMP"), ++ (long int)EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT}, ++ {XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, ++ XML_L("XML_AT_ACT_THRES"), ++ (long int)EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT}, + #endif + {XML_FEATURE_END, NULL, 0}}; + +@@ -2697,6 +2991,29 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold( + parser->m_accounting.activationThresholdBytes = activationThresholdBytes; + return XML_TRUE; + } ++ ++XML_Bool XMLCALL ++XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, ++ float maximumAmplificationFactor) { ++ if ((parser == NULL) || (parser->m_parentParser != NULL) ++ || isnan(maximumAmplificationFactor) ++ || (maximumAmplificationFactor < 1.0f)) { ++ return XML_FALSE; ++ } ++ parser->m_alloc_tracker.maximumAmplificationFactor ++ = maximumAmplificationFactor; ++ return XML_TRUE; ++} ++ ++XML_Bool XMLCALL ++XML_SetAllocTrackerActivationThreshold( ++ XML_Parser parser, unsigned long long activationThresholdBytes) { ++ if ((parser == NULL) || (parser->m_parentParser != NULL)) { ++ return XML_FALSE; ++ } ++ parser->m_alloc_tracker.activationThresholdBytes = activationThresholdBytes; ++ return XML_TRUE; ++} + #endif /* XML_DTD */ + + XML_Bool XMLCALL +@@ -5652,8 +5969,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + case XML_ROLE_CONTENT_EMPTY: + if (dtd->in_eldecl) { + if (parser->m_elementDeclHandler) { ++ // NOTE: We are avoiding MALLOC(..) here to so that ++ // applications that are not using XML_FreeContentModel but ++ // plain free(..) or .free_fcn() to free the content model's ++ // memory are safe. + XML_Content *content +- = (XML_Content *)MALLOC(parser, sizeof(XML_Content)); ++ = (XML_Content *)parser->m_mem.malloc_fcn(sizeof(XML_Content)); + if (! content) + return XML_ERROR_NO_MEMORY; + content->quant = XML_CQUANT_NONE; +@@ -7009,19 +7330,19 @@ normalizePublicId(XML_Char *publicId) { + } + + static DTD * +-dtdCreate(const XML_Memory_Handling_Suite *ms) { +- DTD *p = ms->malloc_fcn(sizeof(DTD)); ++dtdCreate(XML_Parser parser) { ++ DTD *p = MALLOC(parser, sizeof(DTD)); + if (p == NULL) + return p; +- poolInit(&(p->pool), ms); +- poolInit(&(p->entityValuePool), ms); +- hashTableInit(&(p->generalEntities), ms); +- hashTableInit(&(p->elementTypes), ms); +- hashTableInit(&(p->attributeIds), ms); +- hashTableInit(&(p->prefixes), ms); ++ poolInit(&(p->pool), parser); ++ poolInit(&(p->entityValuePool), parser); ++ hashTableInit(&(p->generalEntities), parser); ++ hashTableInit(&(p->elementTypes), parser); ++ hashTableInit(&(p->attributeIds), parser); ++ hashTableInit(&(p->prefixes), parser); + #ifdef XML_DTD + p->paramEntityRead = XML_FALSE; +- hashTableInit(&(p->paramEntities), ms); ++ hashTableInit(&(p->paramEntities), parser); + #endif /* XML_DTD */ + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; +@@ -7041,7 +7362,7 @@ dtdCreate(const XML_Memory_Handling_Suite *ms) { + } + + static void +-dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { ++dtdReset(DTD *p, XML_Parser parser) { + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { +@@ -7049,7 +7370,7 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { + if (! e) + break; + if (e->allocDefaultAtts != 0) +- ms->free_fcn(e->defaultAtts); ++ FREE(parser, e->defaultAtts); + } + hashTableClear(&(p->generalEntities)); + #ifdef XML_DTD +@@ -7066,9 +7387,9 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { + + p->in_eldecl = XML_FALSE; + +- ms->free_fcn(p->scaffIndex); ++ FREE(parser, p->scaffIndex); + p->scaffIndex = NULL; +- ms->free_fcn(p->scaffold); ++ FREE(parser, p->scaffold); + p->scaffold = NULL; + + p->scaffLevel = 0; +@@ -7082,7 +7403,7 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { + } + + static void +-dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { ++dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) { + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { +@@ -7090,7 +7411,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { + if (! e) + break; + if (e->allocDefaultAtts != 0) +- ms->free_fcn(e->defaultAtts); ++ FREE(parser, e->defaultAtts); + } + hashTableDestroy(&(p->generalEntities)); + #ifdef XML_DTD +@@ -7102,10 +7423,10 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { + poolDestroy(&(p->pool)); + poolDestroy(&(p->entityValuePool)); + if (isDocEntity) { +- ms->free_fcn(p->scaffIndex); +- ms->free_fcn(p->scaffold); ++ FREE(parser, p->scaffIndex); ++ FREE(parser, p->scaffold); + } +- ms->free_fcn(p); ++ FREE(parser, p); + } + + /* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. +@@ -7113,7 +7434,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { + */ + static int + dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, +- const XML_Memory_Handling_Suite *ms) { ++ XML_Parser parser) { + HASH_TABLE_ITER iter; + + /* Copy the prefix table. */ +@@ -7194,7 +7515,7 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, + } + #endif + newE->defaultAtts +- = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); ++ = MALLOC(parser, oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + if (! newE->defaultAtts) { + return 0; + } +@@ -7356,7 +7677,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { + /* table->size is a power of 2 */ + table->size = (size_t)1 << INIT_POWER; + tsize = table->size * sizeof(NAMED *); +- table->v = table->mem->malloc_fcn(tsize); ++ table->v = MALLOC(table->parser, tsize); + if (! table->v) { + table->size = 0; + return NULL; +@@ -7396,7 +7717,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { + } + + size_t tsize = newSize * sizeof(NAMED *); +- NAMED **newV = table->mem->malloc_fcn(tsize); ++ NAMED **newV = MALLOC(table->parser, tsize); + if (! newV) + return NULL; + memset(newV, 0, tsize); +@@ -7412,7 +7733,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { + } + newV[j] = table->v[i]; + } +- table->mem->free_fcn(table->v); ++ FREE(table->parser, table->v); + table->v = newV; + table->power = newPower; + table->size = newSize; +@@ -7425,7 +7746,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { + } + } + } +- table->v[i] = table->mem->malloc_fcn(createSize); ++ table->v[i] = MALLOC(table->parser, createSize); + if (! table->v[i]) + return NULL; + memset(table->v[i], 0, createSize); +@@ -7438,7 +7759,7 @@ static void FASTCALL + hashTableClear(HASH_TABLE *table) { + size_t i; + for (i = 0; i < table->size; i++) { +- table->mem->free_fcn(table->v[i]); ++ FREE(table->parser, table->v[i]); + table->v[i] = NULL; + } + table->used = 0; +@@ -7448,17 +7769,17 @@ static void FASTCALL + hashTableDestroy(HASH_TABLE *table) { + size_t i; + for (i = 0; i < table->size; i++) +- table->mem->free_fcn(table->v[i]); +- table->mem->free_fcn(table->v); ++ FREE(table->parser, table->v[i]); ++ FREE(table->parser, table->v); + } + + static void FASTCALL +-hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) { ++hashTableInit(HASH_TABLE *p, XML_Parser parser) { + p->power = 0; + p->size = 0; + p->used = 0; + p->v = NULL; +- p->mem = ms; ++ p->parser = parser; + } + + static void FASTCALL +@@ -7478,13 +7799,13 @@ hashTableIterNext(HASH_TABLE_ITER *iter) { + } + + static void FASTCALL +-poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) { ++poolInit(STRING_POOL *pool, XML_Parser parser) { + pool->blocks = NULL; + pool->freeBlocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; +- pool->mem = ms; ++ pool->parser = parser; + } + + static void FASTCALL +@@ -7511,13 +7832,13 @@ poolDestroy(STRING_POOL *pool) { + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; +- pool->mem->free_fcn(p); ++ FREE(pool->parser, p); + p = tem; + } + p = pool->freeBlocks; + while (p) { + BLOCK *tem = p->next; +- pool->mem->free_fcn(p); ++ FREE(pool->parser, p); + p = tem; + } + } +@@ -7672,8 +7993,8 @@ poolGrow(STRING_POOL *pool) { + if (bytesToAllocate == 0) + return XML_FALSE; + +- temp = (BLOCK *)pool->mem->realloc_fcn(pool->blocks, +- (unsigned)bytesToAllocate); ++ temp = (BLOCK *)REALLOC(pool->parser, pool->blocks, ++ (unsigned)bytesToAllocate); + if (temp == NULL) + return XML_FALSE; + pool->blocks = temp; +@@ -7713,7 +8034,7 @@ poolGrow(STRING_POOL *pool) { + if (bytesToAllocate == 0) + return XML_FALSE; + +- tem = pool->mem->malloc_fcn(bytesToAllocate); ++ tem = MALLOC(pool->parser, bytesToAllocate); + if (! tem) + return XML_FALSE; + tem->size = blockSize; +@@ -7828,7 +8149,10 @@ build_model(XML_Parser parser) { + const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content) + + (dtd->contentStringLen * sizeof(XML_Char))); + +- ret = (XML_Content *)MALLOC(parser, allocsize); ++ // NOTE: We are avoiding MALLOC(..) here to so that ++ // applications that are not using XML_FreeContentModel but plain ++ // free(..) or .free_fcn() to free the content model's memory are safe. ++ ret = (XML_Content *)parser->m_mem.malloc_fcn(allocsize); + if (! ret) + return NULL; + +@@ -7949,7 +8273,7 @@ getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr, + } + + static XML_Char * +-copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { ++copyString(const XML_Char *s, XML_Parser parser) { + size_t charsRequired = 0; + XML_Char *result; + +@@ -7961,7 +8285,7 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { + charsRequired++; + + /* Now allocate space for the copy */ +- result = memsuite->malloc_fcn(charsRequired * sizeof(XML_Char)); ++ result = MALLOC(parser, charsRequired * sizeof(XML_Char)); + if (result == NULL) + return NULL; + /* Copy the original into place */ +diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c +index 5db384d..4a0a2e3 100644 +--- a/expat/tests/runtests.c ++++ b/expat/tests/runtests.c +@@ -51,6 +51,9 @@ + #include // for SIZE_MAX + #include + #include ++#include /* NAN, INFINITY */ ++#include ++#include /* for SIZE_MAX */ + #include + #include /* ptrdiff_t */ + #include +@@ -62,7 +65,10 @@ + # include + #endif + ++#include "expat_config.h" ++ + #include "expat.h" ++#include "internal.h" + #include "chardata.h" + #include "structdata.h" + #include "internal.h" +@@ -11420,6 +11426,209 @@ START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) { + } + END_TEST + ++static size_t ++sizeRecordedFor(void *ptr) { ++ return *(size_t *)((char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t)); ++} ++ ++START_TEST(test_alloc_tracker_size_recorded) { ++ XML_Memory_Handling_Suite memsuite = {malloc, realloc, free}; ++ ++ bool values[] = {true, false}; ++ for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) { ++ const bool useMemSuite = values[i]; ++ XML_Parser parser = useMemSuite ++ ? XML_ParserCreate_MM(NULL, &memsuite, XCS("|")) ++ : XML_ParserCreate(NULL); ++ ++ void *ptr = expat_malloc(parser, 10, -1); ++ ++ assert_true(ptr != NULL); ++ assert_true(sizeRecordedFor(ptr) == 10); ++ ++ assert_true(expat_realloc(parser, ptr, SIZE_MAX / 2, -1) == NULL); ++ ++ assert_true(sizeRecordedFor(ptr) == 10); // i.e. unchanged ++ ++ ptr = expat_realloc(parser, ptr, 20, -1); ++ ++ assert_true(ptr != NULL); ++ assert_true(sizeRecordedFor(ptr) == 20); ++ ++ expat_free(parser, ptr, -1); ++ ++ XML_ParserFree(parser); ++ } ++} ++END_TEST ++ ++START_TEST(test_alloc_tracker_pointer_alignment) { ++ XML_Parser parser = XML_ParserCreate(NULL); ++ assert_true(sizeof(long long) >= sizeof(size_t)); // self-test ++ long long *const ptr ++ = (long long *)expat_malloc(parser, 4 * sizeof(long long), -1); ++ ptr[0] = 0LL; ++ ptr[1] = 1LL; ++ ptr[2] = 2LL; ++ ptr[3] = 3LL; ++ expat_free(parser, ptr, -1); ++ XML_ParserFree(parser); ++} ++END_TEST ++ ++START_TEST(test_alloc_tracker_maximum_amplification) { ++ if (g_reparseDeferralEnabledDefault == XML_TRUE) { ++ return; ++ } ++ ++ XML_Parser parser = XML_ParserCreate(NULL); ++ ++ // Get .m_accounting.countBytesDirect from 0 to 3 ++ const char *const chunk = " "; ++ assert_true(_XML_Parse_SINGLE_BYTES(parser, chunk, (int)strlen(chunk), ++ /*isFinal=*/XML_FALSE) ++ == XML_STATUS_OK); ++ ++ // Stop activation threshold from interfering ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE); ++ ++ // Exceed maximum amplification: should be rejected. ++ assert_true(expat_malloc(parser, 1000, -1) == NULL); ++ ++ // Increase maximum amplification, and try the same amount once more: should ++ // work. ++ assert_true(XML_SetAllocTrackerMaximumAmplification(parser, 3000.0f) ++ == XML_TRUE); ++ ++ void *const ptr = expat_malloc(parser, 1000, -1); ++ assert_true(ptr != NULL); ++ expat_free(parser, ptr, -1); ++ ++ XML_ParserFree(parser); ++} ++END_TEST ++ ++START_TEST(test_alloc_tracker_threshold) { ++ XML_Parser parser = XML_ParserCreate(NULL); ++ ++ // Exceed maximum amplification *before* (default) threshold: should work. ++ void *const ptr = expat_malloc(parser, 1000, -1); ++ assert_true(ptr != NULL); ++ expat_free(parser, ptr, -1); ++ ++ // Exceed maximum amplification *after* threshold: should be rejected. ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 999) == XML_TRUE); ++ assert_true(expat_malloc(parser, 1000, -1) == NULL); ++ ++ XML_ParserFree(parser); ++} ++END_TEST ++ ++START_TEST(test_alloc_tracker_getbuffer_unlimited) { ++ XML_Parser parser = XML_ParserCreate(NULL); ++ ++ // Artificially lower threshold ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE); ++ ++ // Self-test: Prove that threshold is as rejecting as expected ++ assert_true(expat_malloc(parser, 1000, -1) == NULL); ++ // XML_GetBuffer should be allowed to pass, though ++ assert_true(XML_GetBuffer(parser, 1000) != NULL); ++ ++ XML_ParserFree(parser); ++} ++END_TEST ++ ++START_TEST(test_alloc_tracker_api) { ++ XML_Parser parserWithoutParent = XML_ParserCreate(NULL); ++ XML_Parser parserWithParent = XML_ExternalEntityParserCreate( ++ parserWithoutParent, XCS("entity123"), NULL); ++ if (parserWithoutParent == NULL) ++ fail("parserWithoutParent is NULL"); ++ if (parserWithParent == NULL) ++ fail("parserWithParent is NULL"); ++ ++ // XML_SetAllocTrackerMaximumAmplification, error cases ++ if (XML_SetAllocTrackerMaximumAmplification(NULL, 123.0f) == XML_TRUE) ++ fail("Call with NULL parser is NOT supposed to succeed"); ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithParent, 123.0f) ++ == XML_TRUE) ++ fail("Call with non-root parser is NOT supposed to succeed"); ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, NAN) ++ == XML_TRUE) ++ fail("Call with NaN limit is NOT supposed to succeed"); ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, -1.0f) ++ == XML_TRUE) ++ fail("Call with negative limit is NOT supposed to succeed"); ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 0.9f) ++ == XML_TRUE) ++ fail("Call with positive limit <1.0 is NOT supposed to succeed"); ++ ++ // XML_SetAllocTrackerMaximumAmplification, success cases ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 1.0f) ++ == XML_FALSE) ++ fail("Call with positive limit >=1.0 is supposed to succeed"); ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 123456.789f) ++ == XML_FALSE) ++ fail("Call with positive limit >=1.0 is supposed to succeed"); ++ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, INFINITY) ++ == XML_FALSE) ++ fail("Call with positive limit >=1.0 is supposed to succeed"); ++ ++ // XML_SetAllocTrackerActivationThreshold, error cases ++ if (XML_SetAllocTrackerActivationThreshold(NULL, 123) == XML_TRUE) ++ fail("Call with NULL parser is NOT supposed to succeed"); ++ if (XML_SetAllocTrackerActivationThreshold(parserWithParent, 123) == XML_TRUE) ++ fail("Call with non-root parser is NOT supposed to succeed"); ++ ++ // XML_SetAllocTrackerActivationThreshold, success cases ++ if (XML_SetAllocTrackerActivationThreshold(parserWithoutParent, 123) ++ == XML_FALSE) ++ fail("Call with non-NULL parentless parser is supposed to succeed"); ++ ++ XML_ParserFree(parserWithParent); ++ XML_ParserFree(parserWithoutParent); ++} ++END_TEST ++ ++START_TEST(test_mem_api_cycle) { ++ XML_Parser parser = XML_ParserCreate(NULL); ++ ++ void *ptr = XML_MemMalloc(parser, 10); ++ ++ assert_true(ptr != NULL); ++ memset(ptr, 'x', 10); // assert writability, with ASan in mind ++ ++ ptr = XML_MemRealloc(parser, ptr, 20); ++ ++ assert_true(ptr != NULL); ++ memset(ptr, 'y', 20); // assert writability, with ASan in mind ++ ++ XML_MemFree(parser, ptr); ++ ++ XML_ParserFree(parser); ++} ++END_TEST ++ ++START_TEST(test_mem_api_unlimited) { ++ XML_Parser parser = XML_ParserCreate(NULL); ++ ++ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE); ++ ++ void *ptr = XML_MemMalloc(parser, 1000); ++ ++ assert_true(ptr != NULL); ++ ++ ptr = XML_MemRealloc(parser, ptr, 2000); ++ ++ assert_true(ptr != NULL); ++ ++ XML_MemFree(parser, ptr); ++ ++ XML_ParserFree(parser); ++} ++END_TEST ++ + static void + nsalloc_setup(void) { + XML_Memory_Handling_Suite memsuite = {duff_allocator, duff_reallocator, free}; +@@ -11820,10 +12029,8 @@ START_TEST(test_nsalloc_realloc_attributes) { + nsalloc_teardown(); + nsalloc_setup(); + } +- if (i == 0) +- fail("Parsing worked despite failing reallocations"); +- else if (i == max_realloc_count) +- fail("Parsing failed at max reallocation count"); ++ assert_true( ++ i == 0); // because expat_realloc relies on expat_malloc to some extent + } + END_TEST + +@@ -13694,6 +13901,18 @@ make_suite(void) { + tcase_add_test__ifdef_xml_dtd( + tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail); + ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_size_recorded); ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_pointer_alignment); ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, ++ test_alloc_tracker_maximum_amplification); ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_threshold); ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, ++ test_alloc_tracker_getbuffer_unlimited); ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_api); ++ ++ tcase_add_test(tc_alloc, test_mem_api_cycle); ++ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_mem_api_unlimited); ++ + suite_add_tcase(s, tc_nsalloc); + tcase_add_checked_fixture(tc_nsalloc, nsalloc_setup, nsalloc_teardown); + tcase_add_test(tc_nsalloc, test_nsalloc_xmlns); +diff --git a/expat/xmlwf/xmlwf.c b/expat/xmlwf/xmlwf.c +index 7c62919..99622df 100644 +--- a/expat/xmlwf/xmlwf.c ++++ b/expat/xmlwf/xmlwf.c +@@ -908,11 +908,11 @@ usage(const XML_Char *prog, int rc) { + T(" -t write no XML output for [t]iming of plain parsing\n") + T(" -N enable adding doctype and [n]otation declarations\n") + T("\n") +- T("billion laughs attack protection:\n") ++ T("amplification attack protection (e.g. billion laughs):\n") + T(" NOTE: If you ever need to increase these values for non-attack payload, please file a bug report.\n") + T("\n") + T(" -a FACTOR set maximum tolerated [a]mplification factor (default: 100.0)\n") +- T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB)\n") ++ T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)\n") + T("\n") + T("reparse deferral:\n") + T(" -q disable reparse deferral, and allow [q]uadratic parse runtime with large tokens\n") +@@ -921,6 +921,9 @@ usage(const XML_Char *prog, int rc) { + T(" -h show this [h]elp message and exit\n") + T(" -v show program's [v]ersion number and exit\n") + T("\n") ++ T("environment variables:\n") ++ T(" EXPAT_MALLOC_DEBUG=(0|1|2)\n") ++ T(" Control verbosity of allocation tracker (default: 0)\n") + T("exit status:\n") + T(" 0 the input files are well-formed and the output (if requested) was written successfully\n") + T(" 1 could not allocate data structures, signals a serious problem with execution environment\n") +@@ -1133,12 +1136,15 @@ tmain(int argc, XML_Char **argv) { + #ifdef XML_DTD + XML_SetBillionLaughsAttackProtectionMaximumAmplification( + parser, attackMaximumAmplification); ++ XML_SetAllocTrackerMaximumAmplification(parser, ++ attackMaximumAmplification); + #endif + } + if (attackThresholdGiven) { + #ifdef XML_DTD + XML_SetBillionLaughsAttackProtectionActivationThreshold( + parser, attackThresholdBytes); ++ XML_SetAllocTrackerActivationThreshold(parser, attackThresholdBytes); + #else + (void)attackThresholdBytes; // silence -Wunused-but-set-variable + #endif +diff --git a/expat/xmlwf/xmlwf_helpgen.py b/expat/xmlwf/xmlwf_helpgen.py +index 1bd0a0a..1bacd60 100755 +--- a/expat/xmlwf/xmlwf_helpgen.py ++++ b/expat/xmlwf/xmlwf_helpgen.py +@@ -32,6 +32,10 @@ + import argparse + + epilog = """ ++environment variables: ++ EXPAT_MALLOC_DEBUG=(0|1|2) ++ Control verbosity of allocation tracker (default: 0) ++ + exit status: + 0 the input files are well-formed and the output (if requested) was written successfully + 1 could not allocate data structures, signals a serious problem with execution environment +@@ -73,13 +77,13 @@ output_mode.add_argument('-m', action='store_true', help='write [m]eta XML, not + output_mode.add_argument('-t', action='store_true', help='write no XML output for [t]iming of plain parsing') + output_related.add_argument('-N', action='store_true', help='enable adding doctype and [n]otation declarations') + +-billion_laughs = parser.add_argument_group('billion laughs attack protection', ++billion_laughs = parser.add_argument_group('amplification attack protection (e.g. billion laughs)', + description='NOTE: ' + 'If you ever need to increase these values ' + 'for non-attack payload, please file a bug report.') + billion_laughs.add_argument('-a', metavar='FACTOR', + help='set maximum tolerated [a]mplification factor (default: 100.0)') +-billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB)') ++billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)') + + reparse_deferral = parser.add_argument_group('reparse deferral') + reparse_deferral.add_argument('-q', metavar='FACTOR', diff --git a/expat.spec b/expat.spec index a4080f1..0620993 100644 --- a/expat.spec +++ b/expat.spec @@ -1,33 +1,30 @@ -%global unversion 2_2_5 +%global unversion 2_5_0 Summary: An XML parser library Name: expat Version: %(echo %{unversion} | sed 's/_/./g') -Release: 17%{?dist} +Release: 1%{?dist} Source: https://github.com/libexpat/libexpat/archive/R_%{unversion}.tar.gz#/expat-%{version}.tar.gz URL: https://libexpat.github.io/ License: MIT BuildRequires: autoconf, libtool, xmlto, gcc-c++ -Patch0: expat-2.2.5-doc2man.patch -Patch1: expat-2.2.5-CVE-2018-20843.patch -Patch2: expat-2.2.5-CVE-2019-15903.patch -Patch3: expat-2.2.5-Detect-and-prevent-integer-overflow-in-XML_GetBuffer.patch -Patch4: expat-2.2.5-Detect-and-prevent-troublesome-left-shifts.patch -Patch5: expat-2.2.5-Prevent-integer-overflow-on-m_groupSize-in-function.patch -Patch6: expat-2.2.5-Prevent-more-integer-overflows.patch -Patch7: expat-2.2.5-Protect-against-malicious-namespace-declarations.patch -Patch8: expat-2.2.5-Add-missing-validation-of-encoding.patch -Patch9: expat-2.2.5-Prevent-integer-overflow-in-storeRawNames.patch -Patch10: expat-2.2.5-Prevent-integer-overflow-in-copyString.patch -Patch11: expat-2.2.5-Prevent-stack-exhaustion-in-build_model.patch -Patch12: expat-2.2.5-Ensure-raw-tagnames-are-safe-exiting-internalEntityParser.patch -Patch13: expat-2.2.5-CVE-2022-43680.patch -Patch14: expat-2.2.5-CVE-2023-52425.patch -Patch15: expat-2.2.5-CVE-2024-45490.patch -Patch16: expat-2.2.5-CVE-2024-45491.patch -Patch17: expat-2.2.5-CVE-2024-45492.patch -Patch18: expat-2.2.5-CVE-2024-50602.patch -Patch19: expat-2.2.5-CVE-2024-8176.patch +BuildRequires: make +# https://issues.redhat.com/browse/RHEL-24227 +Patch0: expat-2.5.0-CVE-2023-52425.patch +# https://issues.redhat.com/browse/RHEL-28700 +Patch1: expat-2.5.0-CVE-2024-28757.patch +# https://issues.redhat.com/browse/RHEL-56761 +Patch2: expat-2.5.0-CVE-2024-45490.patch +# https://issues.redhat.com/browse/RHEL-57520 +Patch3: expat-2.5.0-CVE-2024-45491.patch +# https://issues.redhat.com/browse/RHEL-57511 +Patch4: expat-2.5.0-CVE-2024-45492.patch +# https://issues.redhat.com/browse/RHEL-65066 +Patch5: expat-2.5.0-CVE-2024-50602.patch +# https://issues.redhat.com/browse/RHEL-57489 +Patch6: expat-2.5.0-CVE-2024-8176.patch +# https://issues.redhat.com/browse/RHEL-114618 +Patch7: expat-2.5.0-CVE-2025-59375.patch %description This is expat, the C library for parsing XML, written by James Clark. Expat @@ -55,27 +52,15 @@ Install it if you need to link statically with expat. %prep %setup -q -n libexpat-R_%{unversion}/expat -%patch0 -p2 -b .doc2man -%patch1 -p2 -b .cve20843 -%patch2 -p2 -b .cve15903 -%patch3 -p1 -b .CVE-2022-23852 -%patch4 -p1 -b .CVE-2021-45960 -%patch5 -p1 -b .CVE-2021-46143 -%patch6 -p1 -b .CVE-2022-22822-CVE-2022-22827 -%patch7 -p1 -b .CVE-2022-25236 -%patch8 -p1 -b .CVE-2022-25235 -%patch9 -p1 -b .CVE-2022-25315 -%patch10 -p1 -b .CVE-2022-25314 -%patch11 -p1 -b .CVE-2022-25313 -%patch12 -p1 -b .CVE-2022-40674 -%patch13 -p1 -b .CVE-2022-43680 pushd .. -%patch14 -p1 -b .CVE-2023-52425 -%patch15 -p1 -b .CVE-2024-45490 -%patch16 -p1 -b .CVE-2024-45491 -%patch17 -p1 -b .CVE-2024-45492 -%patch18 -p1 -b .CVE-2024-50602 -%patch19 -p1 -b .CVE-2024-8176 +%patch0 -p1 -b .CVE-2023-52425 +%patch1 -p1 -b .CVE-2024-28757 +%patch2 -p1 -b .CVE-2024-45490 +%patch3 -p1 -b .CVE-2024-45491 +%patch4 -p1 -b .CVE-2024-45492 +%patch5 -p1 -b .CVE-2024-50602 +%patch6 -p1 -b .CVE-2024-8176 +%patch7 -p1 -b .CVE-2025-59375 popd sed -i 's/install-data-hook/do-nothing-please/' lib/Makefile.am @@ -115,15 +100,21 @@ make check %{_mandir}/*/* %files devel -%doc doc/reference.html doc/*.png doc/*.css examples/*.c +%doc doc/reference.html doc/*.css examples/*.c %{_libdir}/lib*.so %{_libdir}/pkgconfig/*.pc %{_includedir}/*.h +%{_libdir}/cmake/expat-%{version} %files static %{_libdir}/lib*.a %changelog +* Wed Nov 19 2025 Tomas Korbar - 2.5.0-1 +- Rebase to version 2.5.0 +- Fix CVE-2025-59375 +- Resolves: RHEL-114618 + * Mon Apr 07 2025 Tomas Korbar - 2.2.5-17 - Fix CVE-2024-8176 - Resolves: RHEL-57477 diff --git a/sources b/sources index 043438b..30d98c8 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (expat-2.2.5.tar.gz) = 61ce2a479521412e0c56c352106c4adfb61a6bedb883921aba3ebccc29311ddd192646ac2c51b41572728d4de6ab4cb60a1dbc71515d742a80a8b59d89ca74d6 +SHA512 (expat-2.5.0.tar.gz) = 779f0d0f3f2d8b33db0fd044864ab5ab1a40f20501f792fe90ad0d18de536c4765c3749f120e21fec11a0e6c89af1dc576d1fe261c871ca44a594f7b61fd1d9e