From f16bfa167103399a5ecb84bc07dad3b42ada1c07 Mon Sep 17 00:00:00 2001 From: caixiaomeng Date: Mon, 8 Apr 2024 16:50:42 +0800 Subject: [PATCH] fix CVE-2024-28757 and CVE-2023-52426 --- backport-001-CVE-2023-52426.patch | 73 ++++++ backport-002-CVE-2023-52426.patch | 29 +++ backport-003-CVE-2023-52426.patch | 402 ++++++++++++++++++++++++++++++ backport-004-CVE-2023-52426.patch | 184 ++++++++++++++ backport-CVE-2024-28757-001.patch | 57 +++++ backport-CVE-2024-28757-002.patch | 26 ++ backport-CVE-2024-28757-003.patch | 86 +++++++ backport-CVE-2024-28757-004.patch | 75 ++++++ expat.spec | 28 ++- 9 files changed, 955 insertions(+), 5 deletions(-) create mode 100644 backport-001-CVE-2023-52426.patch create mode 100644 backport-002-CVE-2023-52426.patch create mode 100644 backport-003-CVE-2023-52426.patch create mode 100644 backport-004-CVE-2023-52426.patch create mode 100644 backport-CVE-2024-28757-001.patch create mode 100644 backport-CVE-2024-28757-002.patch create mode 100644 backport-CVE-2024-28757-003.patch create mode 100644 backport-CVE-2024-28757-004.patch diff --git a/backport-001-CVE-2023-52426.patch b/backport-001-CVE-2023-52426.patch new file mode 100644 index 0000000..32f0a30 --- /dev/null +++ b/backport-001-CVE-2023-52426.patch @@ -0,0 +1,73 @@ +From daa89e42c005cc7f4f7af9eee271ae0723d30300 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Thu, 26 Oct 2023 00:59:52 +0200 +Subject: [PATCH 01/17] cmake: Introduce option EXPAT_GE to control macro + XML_GE + +Reference: https://github.com//libexpat/libexpat/commit/daa89e42c005cc7f4f7af9eee271ae0723d30300 +Conflict: remove expat_config_h_cmake__expected.txt + change expat_shy_set to option in CMakeLists.txt +--- + CMakeLists.txt | 9 +++++++++ + expat_config.h.cmake | 3 +++ + 2 files changed, 12 insertions(+) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index cd12a99..697d4d7 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -126,6 +126,8 @@ set(EXPAT_CONTEXT_BYTES 1024 CACHE STRING "Define to specify how much context to + mark_as_advanced(EXPAT_CONTEXT_BYTES) + option(EXPAT_DTD "Define to make parameter entity parsing functionality available" ON) + mark_as_advanced(EXPAT_DTD) ++option(EXPAT_GE "Define to make general entity parsing functionality available" ON) ++mark_as_advanced(EXPAT_GE) + option(EXPAT_NS "Define to make XML Namespaces functionality available" ON) + mark_as_advanced(EXPAT_NS) + option(EXPAT_WARNINGS_AS_ERRORS "Treat all compiler warnings as errors" OFF) +@@ -156,6 +158,11 @@ endif() + # + # Environment checks + # ++if(EXPAT_DTD AND NOT EXPAT_GE) ++ message(SEND_ERROR "Option EXPAT_DTD requires that EXPAT_GE is also enabled.") ++ message(SEND_ERROR "Please either enable option EXPAT_GE (recommended) or disable EXPAT_DTD also.") ++endif() ++ + if(EXPAT_WITH_LIBBSD) + find_library(LIB_BSD NAMES bsd) + if(NOT LIB_BSD) +@@ -258,6 +265,7 @@ endif() + + _expat_copy_bool_int(EXPAT_ATTR_INFO XML_ATTR_INFO) + _expat_copy_bool_int(EXPAT_DTD XML_DTD) ++_expat_copy_bool_int(EXPAT_GE XML_GE) + _expat_copy_bool_int(EXPAT_LARGE_SIZE XML_LARGE_SIZE) + _expat_copy_bool_int(EXPAT_MIN_SIZE XML_MIN_SIZE) + _expat_copy_bool_int(EXPAT_NS XML_NS) +@@ -758,6 +766,7 @@ message(STATUS " // Advanced options, changes not advised") + message(STATUS " Attributes info .......... ${EXPAT_ATTR_INFO}") + message(STATUS " Context bytes ............ ${EXPAT_CONTEXT_BYTES}") + message(STATUS " DTD support .............. ${EXPAT_DTD}") ++message(STATUS " General entities ......... ${EXPAT_GE}") + message(STATUS " Large size ............... ${EXPAT_LARGE_SIZE}") + message(STATUS " Minimum size ............. ${EXPAT_MIN_SIZE}") + message(STATUS " Namespace support ........ ${EXPAT_NS}") +diff --git a/expat_config.h.cmake b/expat_config.h.cmake +index 173fed1..18d979a 100644 +--- a/expat_config.h.cmake ++++ b/expat_config.h.cmake +@@ -100,6 +100,9 @@ + /* Define to make parameter entity parsing functionality available. */ + #cmakedefine XML_DTD + ++/* Define as 1/0 to enable/disable support for general entities. */ ++#define XML_GE @XML_GE@ ++ + /* Define to make XML Namespaces functionality available. */ + #cmakedefine XML_NS + +-- +2.33.0 + + diff --git a/backport-002-CVE-2023-52426.patch b/backport-002-CVE-2023-52426.patch new file mode 100644 index 0000000..3271597 --- /dev/null +++ b/backport-002-CVE-2023-52426.patch @@ -0,0 +1,29 @@ +From ed87a4793404e91c0cc0c81435fcfcc64a8be9f4 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Thu, 26 Oct 2023 00:45:23 +0200 +Subject: [PATCH 02/17] configure.ac: Define macro XML_GE as 1 + +Reference: https://github.com//libexpat/libexpat/commit/ed87a4793404e91c0cc0c81435fcfcc64a8be9f4 +Conflict: remove expat_config_h_in__expected.txt + +--- + configure.ac | 2 ++ + 2 files changed, 3 insertions(+) + +diff --git a/expat/configure.ac b/expat/configure.ac +index c9f95bca..fec4ecd0 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -303,6 +303,8 @@ AC_SUBST(FILEMAP) + dnl Some basic configuration: + AC_DEFINE([XML_NS], 1, + [Define to make XML Namespaces functionality available.]) ++AC_DEFINE([XML_GE], 1, ++ [Define as 1/0 to enable/disable support for general entities.]) + AC_DEFINE([XML_DTD], 1, + [Define to make parameter entity parsing functionality available.]) + AC_DEFINE([XML_DEV_URANDOM], 1, +-- +2.37.3.windows.1 + + diff --git a/backport-003-CVE-2023-52426.patch b/backport-003-CVE-2023-52426.patch new file mode 100644 index 0000000..f896bce --- /dev/null +++ b/backport-003-CVE-2023-52426.patch @@ -0,0 +1,402 @@ +From ff958ebde28854845b28167695a678cc49a50c4b Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Thu, 26 Oct 2023 00:43:22 +0200 +Subject: [PATCH] lib|xmlwf|cmake: Extend scope of billion laughs attack + protection + +.. from "defined(XML_DTD)" to "defined(XML_DTD) || XML_GE==1". + +Reference: https://github.com//libexpat/libexpat/commit/ff958ebde28854845b28167695a678cc49a50c4b +Conflict: remove _expat_def_file_toggle in CMakeLists.txt of win32 + remove _EXPAT_COMMENT_DTD_OR_GE in libexpat.def.cmake of MS VC++ + adapt entityValueInitProcessor + adapt internalEntityProcessor + + +--- + lib/expat.h | 8 +- + lib/internal.h | 2 +- + lib/xmlparse.c | 71 +-- + xmlwf/xmlwf.c | 18 +- + 10 files changed, 644 insertions(+), 46 deletions(-) + +diff --git a/lib/expat.h b/lib/expat.h +index b7d6d35..a4fb7cf 100644 +--- a/lib/expat.h ++++ b/lib/expat.h +@@ -1024,13 +1024,15 @@ typedef struct { + XMLPARSEAPI(const XML_Feature *) + XML_GetFeatureList(void); + +-#ifdef XML_DTD +-/* Added in Expat 2.4.0. */ ++#if defined(XML_DTD) || XML_GE == 1 ++/* Added in Expat 2.4.0 for XML_DTD defined and ++ * added in Expat 2.6.0 for XML_GE == 1. */ + XMLPARSEAPI(XML_Bool) + XML_SetBillionLaughsAttackProtectionMaximumAmplification( + XML_Parser parser, float maximumAmplificationFactor); + +-/* Added in Expat 2.4.0. */ ++/* Added in Expat 2.4.0 for XML_DTD defined and ++ * added in Expat 2.6.0 for XML_GE == 1. */ + XMLPARSEAPI(XML_Bool) + XML_SetBillionLaughsAttackProtectionActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes); +diff --git a/lib/internal.h b/lib/internal.h +index 444eba0..0b1fbfb 100644 +--- a/lib/internal.h ++++ b/lib/internal.h +@@ -152,7 +152,7 @@ extern "C" { + void _INTERNAL_trim_to_complete_utf8_characters(const char *from, + const char **fromLimRef); + +-#if defined(XML_DTD) ++#if defined(XML_DTD) || XML_GE == 1 + unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser); + unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser); + const char *unsignedCharToPrintable(unsigned char c); +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 3e458e1..689095a 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -404,7 +404,7 @@ enum XML_Account { + XML_ACCOUNT_NONE /* i.e. do not account, was accounted already */ + }; + +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + typedef unsigned long long XmlBigCount; + typedef struct accounting { + XmlBigCount countBytesDirect; +@@ -420,7 +420,7 @@ typedef struct entity_stats { + unsigned int maximumDepthSeen; + int debugLevel; + } ENTITY_STATS; +-#endif /* XML_DTD */ ++#endif /* defined(XML_DTD) || XML_GE == 1 */ + + typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start, + const char *end, const char **endPtr); +@@ -558,7 +558,7 @@ static XML_Parser parserCreate(const XML_Char *encodingName, + + static void parserInit(XML_Parser parser, const XML_Char *encodingName); + +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + static float accountingGetCurrentAmplification(XML_Parser rootParser); + static void accountingReportStats(XML_Parser originParser, const char *epilog); + static void accountingOnAbort(XML_Parser originParser); +@@ -581,7 +581,7 @@ static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity, + + static XML_Parser getRootParserOf(XML_Parser parser, + unsigned int *outLevelDiff); +-#endif /* XML_DTD */ ++#endif /* defined(XML_DTD) || XML_GE == 1 */ + + static unsigned long getDebugLevel(const char *variableName, + unsigned long defaultDebugLevel); +@@ -699,7 +699,7 @@ struct XML_ParserStruct { + enum XML_ParamEntityParsing m_paramEntityParsing; + #endif + unsigned long m_hash_secret_salt; +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + ACCOUNTING m_accounting; + ENTITY_STATS m_entity_stats; + #endif +@@ -1158,7 +1158,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { + #endif + parser->m_hash_secret_salt = 0; + +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + memset(&parser->m_accounting, 0, sizeof(ACCOUNTING)); + parser->m_accounting.debugLevel = getDebugLevel("EXPAT_ACCOUNTING_DEBUG", 0u); + parser->m_accounting.maximumAmplificationFactor +@@ -2517,8 +2517,9 @@ XML_GetFeatureList(void) { + #ifdef XML_ATTR_INFO + {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0}, + #endif +-#ifdef XML_DTD +- /* Added in Expat 2.4.0. */ ++#if defined(XML_DTD) || XML_GE == 1 ++ /* Added in Expat 2.4.0 for XML_DTD defined and ++ * added in Expat 2.6.0 for XML_GE == 1. */ + {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, + XML_L("XML_BLAP_MAX_AMP"), + (long int) +@@ -2532,7 +2533,7 @@ XML_GetFeatureList(void) { + return features; + } + +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + XML_Bool XMLCALL + XML_SetBillionLaughsAttackProtectionMaximumAmplification( + XML_Parser parser, float maximumAmplificationFactor) { +@@ -2554,7 +2555,7 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold( + parser->m_accounting.activationThresholdBytes = activationThresholdBytes; + return XML_TRUE; + } +-#endif /* XML_DTD */ ++#endif /* defined(XML_DTD) || XML_GE == 1 */ + + /* Initially tag->rawName always points into the parse buffer; + for those TAG instances opened while the current parse buffer was +@@ -2640,13 +2641,13 @@ externalEntityInitProcessor2(XML_Parser parser, const char *start, + int tok = XmlContentTok(parser->m_encoding, start, end, &next); + switch (tok) { + case XML_TOK_BOM: +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, start, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + } +-#endif /* XML_DTD */ ++#endif /* defined(XML_DTD) || XML_GE == 1 */ + + /* If we are at the end of the buffer, this would cause the next stage, + i.e. externalEntityInitProcessor3, to pass control directly to +@@ -2760,7 +2761,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + for (;;) { + const char *next = s; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(enc, s, end, &next); +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + const char *accountAfter + = ((tok == XML_TOK_TRAILING_RSQB) || (tok == XML_TOK_TRAILING_CR)) + ? (haveMore ? s /* i.e. 0 bytes */ : end) +@@ -2826,14 +2827,14 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + XML_Char ch = (XML_Char)XmlPredefinedEntityName( + enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar); + if (ch) { +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + /* NOTE: We are replacing 4-6 characters original input for 1 character + * so there is no amplification and hence recording without + * protection. */ + accountingDiffTolerated(parser, tok, (char *)&ch, + ((char *)&ch) + sizeof(XML_Char), __LINE__, + XML_ACCOUNT_ENTITY_EXPANSION); +-#endif /* XML_DTD */ ++#endif /* defined(XML_DTD) || XML_GE == 1 */ + if (parser->m_characterDataHandler) + parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1); + else if (parser->m_defaultHandler) +@@ -4033,7 +4034,7 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, + for (;;) { + const char *next = s; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */ + int tok = XmlCdataSectionTok(enc, s, end, &next); +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; +@@ -4185,7 +4186,7 @@ doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, + *eventPP = s; + *startPtr = NULL; + tok = XmlIgnoreSectionTok(enc, s, end, &next); +-# ifdef XML_DTD ++# if defined(XML_DTD) || XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); +@@ -4277,7 +4278,7 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s, + const XML_Char *storedversion = NULL; + int standalone = -1; + +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); +@@ -4484,7 +4485,7 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, + */ + else if (tok == XML_TOK_BOM && next == end + && ! parser->m_parsingStatus.finalBuffer) { +-# ifdef XML_DTD ++# if defined(XML_DTD) || XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); +@@ -4700,11 +4701,13 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + } + } + role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc); +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + switch (role) { + case XML_ROLE_INSTANCE_START: // bytes accounted in contentProcessor + case XML_ROLE_XML_DECL: // bytes accounted in processXmlDecl +- case XML_ROLE_TEXT_DECL: // bytes accounted in processXmlDecl ++# ifdef XML_DTD ++ case XML_ROLE_TEXT_DECL: // bytes accounted in processXmlDecl ++# endif + break; + default: + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) { +@@ -5641,7 +5644,7 @@ epilogProcessor(XML_Parser parser, const char *s, const char *end, + for (;;) { + const char *next = NULL; + int tok = XmlPrologTok(parser->m_encoding, s, end, &next); +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); +@@ -5721,7 +5724,7 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) { + return XML_ERROR_NO_MEMORY; + } + entity->open = XML_TRUE; +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + entityTrackingOnOpen(parser, entity, __LINE__); + #endif + entity->processed = 0; +@@ -5755,9 +5758,9 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) { + entity->processed = (int)(next - textStart); + parser->m_processor = internalEntityProcessor; + } else { +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + entityTrackingOnClose(parser, entity, __LINE__); +-#endif /* XML_DTD */ ++#endif /* defined(XML_DTD) || XML_GE == 1 */ + entity->open = XML_FALSE; + parser->m_openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ +@@ -5805,7 +5808,7 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end, + entity->processed = (int)(next - (const char *)entity->textPtr); + return result; + } else { +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + entityTrackingOnClose(parser, entity, __LINE__); + #endif + entity->open = XML_FALSE; +@@ -5877,7 +5880,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *next + = ptr; /* XmlAttributeValueTok doesn't always set the last arg */ + int tok = XmlAttributeValueTok(enc, ptr, end, &next); +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; +@@ -5942,14 +5945,14 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + XML_Char ch = (XML_Char)XmlPredefinedEntityName( + enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar); + if (ch) { +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + /* NOTE: We are replacing 4-6 characters original input for 1 character + * so there is no amplification and hence recording without + * protection. */ + accountingDiffTolerated(parser, tok, (char *)&ch, + ((char *)&ch) + sizeof(XML_Char), __LINE__, + XML_ACCOUNT_ENTITY_EXPANSION); +-#endif /* XML_DTD */ ++#endif /* defined(XML_DTD) || XML_GE == 1 */ + if (! poolAppendChar(pool, ch)) + return XML_ERROR_NO_MEMORY; + break; +@@ -6027,14 +6030,14 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + enum XML_Error result; + const XML_Char *textEnd = entity->textPtr + entity->textLen; + entity->open = XML_TRUE; +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + 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 ++#if defined(XML_DTD) || XML_GE == 1 + entityTrackingOnClose(parser, entity, __LINE__); + #endif + entity->open = XML_FALSE; +@@ -6090,7 +6093,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, + = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */ + int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); + +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__, + account)) { + accountingOnAbort(parser); +@@ -7636,7 +7639,7 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { + return result; + } + +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + + static float + accountingGetCurrentAmplification(XML_Parser rootParser) { +@@ -8367,7 +8370,7 @@ unsignedCharToPrintable(unsigned char c) { + assert(0); /* never gets here */ + } + +-#endif /* XML_DTD */ ++#endif /* defined(XML_DTD) || XML_GE == 1 */ + + static unsigned long + getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) { + +diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c +index 342d6c5..20b0504 100644 +--- a/xmlwf/xmlwf.c ++++ b/xmlwf/xmlwf.c +@@ -1061,9 +1061,10 @@ tmain(int argc, XML_Char **argv) { + " (needs a floating point number greater or equal than 1.0)")); + exit(XMLWF_EXIT_USAGE_ERROR); + } +-#ifndef XML_DTD +- ftprintf(stderr, T("Warning: Given amplification limit ignored") T( +- ", xmlwf has been compiled without DTD support.\n")); ++#if ! defined(XML_DTD) && XML_GE == 0 ++ ftprintf(stderr, ++ T("Warning: Given amplification limit ignored") ++ T(", xmlwf has been compiled without DTD/GE support.\n")); + #endif + break; + } +@@ -1082,9 +1083,10 @@ tmain(int argc, XML_Char **argv) { + exit(XMLWF_EXIT_USAGE_ERROR); + } + attackThresholdGiven = XML_TRUE; +-#ifndef XML_DTD +- ftprintf(stderr, T("Warning: Given attack threshold ignored") T( +- ", xmlwf has been compiled without DTD support.\n")); ++#if ! defined(XML_DTD) && XML_GE == 0 ++ ftprintf(stderr, ++ T("Warning: Given attack threshold ignored") ++ T(", xmlwf has been compiled without DTD/GE support.\n")); + #endif + break; + } +@@ -1119,13 +1121,13 @@ tmain(int argc, XML_Char **argv) { + } + + if (attackMaximumAmplification != -1.0f) { +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + XML_SetBillionLaughsAttackProtectionMaximumAmplification( + parser, attackMaximumAmplification); + #endif + } + if (attackThresholdGiven) { +-#ifdef XML_DTD ++#if defined(XML_DTD) || XML_GE == 1 + XML_SetBillionLaughsAttackProtectionActivationThreshold( + parser, attackThresholdBytes); + #endif +-- +2.33.0 + + diff --git a/backport-004-CVE-2023-52426.patch b/backport-004-CVE-2023-52426.patch new file mode 100644 index 0000000..8311a77 --- /dev/null +++ b/backport-004-CVE-2023-52426.patch @@ -0,0 +1,184 @@ +From 2b127c20b220b673cf52c6be8bef725bf04cbeaf Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Thu, 26 Oct 2023 18:32:11 +0200 +Subject: [PATCH 07/17] lib: Make XML_GE==0 use self-references as entity + replacement text + +Reference: https://github.com//libexpat/libexpat/commit/2b127c20b220b673cf52c6be8bef725bf04cbeaf +Conflict: NA + +--- + lib/xmlparse.c | 81 +++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 72 insertions(+), 9 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index db148b21..6a38dbe2 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -512,9 +512,13 @@ static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *, + static ATTRIBUTE_ID *getAttributeId(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); + static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); ++#if XML_GE == 1 + static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end, + enum XML_Account account); ++#else ++static enum XML_Error storeSelfEntityValue(XML_Parser parser, ENTITY *entity); ++#endif + static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); + static int reportComment(XML_Parser parser, const ENCODING *enc, +@@ -5053,6 +5057,9 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + break; + case XML_ROLE_ENTITY_VALUE: + if (dtd->keepProcessing) { ++#if defined(XML_DTD) || XML_GE == 1 ++ // This will store the given replacement text in ++ // parser->m_declEntity->textPtr. + enum XML_Error result + = storeEntityValue(parser, enc, s + enc->minBytesPerChar, + next - enc->minBytesPerChar, XML_ACCOUNT_NONE); +@@ -5073,6 +5080,25 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + poolDiscard(&dtd->entityValuePool); + if (result != XML_ERROR_NONE) + return result; ++#else ++ // This will store "&entity123;" in parser->m_declEntity->textPtr ++ // to end up as "&entity123;" in the handler. ++ if (parser->m_declEntity != NULL) { ++ const enum XML_Error result ++ = storeSelfEntityValue(parser, parser->m_declEntity); ++ if (result != XML_ERROR_NONE) ++ return result; ++ ++ if (parser->m_entityDeclHandler) { ++ *eventEndPP = s; ++ parser->m_entityDeclHandler( ++ parser->m_handlerArg, parser->m_declEntity->name, ++ parser->m_declEntity->is_param, parser->m_declEntity->textPtr, ++ parser->m_declEntity->textLen, parser->m_curBase, 0, 0, 0); ++ handleDefault = XML_FALSE; ++ } ++ } ++#endif + } + break; + case XML_ROLE_DOCTYPE_SYSTEM_ID: +@@ -5131,6 +5157,16 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + } + break; + case XML_ROLE_ENTITY_COMPLETE: ++#if XML_GE == 0 ++ // This will store "&entity123;" in entity->textPtr ++ // to end up as "&entity123;" in the handler. ++ if (parser->m_declEntity != NULL) { ++ const enum XML_Error result ++ = storeSelfEntityValue(parser, parser->m_declEntity); ++ if (result != XML_ERROR_NONE) ++ return result; ++ } ++#endif + if (dtd->keepProcessing && parser->m_declEntity + && parser->m_entityDeclHandler) { + *eventEndPP = s; +@@ -6103,6 +6139,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + /* not reached */ + } + ++#if XML_GE == 1 + static enum XML_Error + storeEntityValue(XML_Parser parser, const ENCODING *enc, + const char *entityTextPtr, const char *entityTextEnd, +@@ -6110,12 +6147,12 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ + STRING_POOL *pool = &(dtd->entityValuePool); + enum XML_Error result = XML_ERROR_NONE; +-#ifdef XML_DTD ++# ifdef XML_DTD + int oldInEntityValue = parser->m_prologState.inEntityValue; + parser->m_prologState.inEntityValue = 1; +-#else ++# else + UNUSED_P(account); +-#endif /* XML_DTD */ ++# endif /* XML_DTD */ + /* never return Null for the value argument in EntityDeclHandler, + since this would indicate an external entity; therefore we + have to make sure that entityValuePool.start is not null */ +@@ -6129,18 +6166,18 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, + = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */ + int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); + +-#if defined(XML_DTD) || XML_GE == 1 ++# if defined(XML_DTD) || XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__, + account)) { + accountingOnAbort(parser); + result = XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + goto endEntityValue; + } +-#endif ++# endif + + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: +-#ifdef XML_DTD ++# ifdef XML_DTD + if (parser->m_isParamEntity || enc != parser->m_encoding) { + const XML_Char *name; + ENTITY *entity; +@@ -6202,7 +6239,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, + } + break; + } +-#endif /* XML_DTD */ ++# endif /* XML_DTD */ + /* In the internal subset, PE references are not legal + within markup declarations, e.g entity values in this case. */ + parser->m_eventPtr = entityTextPtr; +@@ -6283,12 +6320,38 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, + entityTextPtr = next; + } + endEntityValue: +-#ifdef XML_DTD ++# ifdef XML_DTD + parser->m_prologState.inEntityValue = oldInEntityValue; +-#endif /* XML_DTD */ ++# endif /* XML_DTD */ + return result; + } + ++#else /* XML_GE == 0 */ ++ ++static enum XML_Error ++storeSelfEntityValue(XML_Parser parser, ENTITY *entity) { ++ // This will store "&entity123;" in entity->textPtr ++ // to end up as "&entity123;" in the handler. ++ const char *const entity_start = "&"; ++ const char *const entity_end = ";"; ++ ++ STRING_POOL *const pool = &(parser->m_dtd->entityValuePool); ++ if (! poolAppendString(pool, entity_start) ++ || ! poolAppendString(pool, entity->name) ++ || ! poolAppendString(pool, entity_end)) { ++ poolDiscard(pool); ++ return XML_ERROR_NO_MEMORY; ++ } ++ ++ entity->textPtr = poolStart(pool); ++ entity->textLen = (int)(poolLength(pool)); ++ poolFinish(pool); ++ ++ return XML_ERROR_NONE; ++} ++ ++#endif /* XML_GE == 0 */ ++ + static void FASTCALL + normalizeLines(XML_Char *s) { + XML_Char *p; +-- +2.37.3.windows.1 + + diff --git a/backport-CVE-2024-28757-001.patch b/backport-CVE-2024-28757-001.patch new file mode 100644 index 0000000..08da7a0 --- /dev/null +++ b/backport-CVE-2024-28757-001.patch @@ -0,0 +1,57 @@ +From 1d50b80cf31de87750103656f6eb693746854aa8 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Mon, 4 Mar 2024 23:49:06 +0100 +Subject: [PATCH] lib/xmlparse.c: Detect billion laughs attack with isolated + external parser + +When parsing DTD content with code like .. + + XML_Parser parser = XML_ParserCreate(NULL); + XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL); + enum XML_Status status = XML_Parse(ext_parser, doc, (int)strlen(doc), XML_TRUE); + +.. there are 0 bytes accounted as direct input and all input from accounted +as indirect input. Now function accountingGetCurrentAmplification cannot calculate +the current amplification ratio as "(direct + indirect) / direct", and it did refuse +to divide by 0 as one would expect, but it returned 1.0 for this case to indicate +no amplification over direct input. As a result, billion laughs attacks from +DTD-only input were not detected with this isolated way of using an external parser. + +The new approach is to assume direct input of length not 0 but 22 -- derived from +ghost input "", the shortest possible way to include an external +DTD --, and do the usual "(direct + indirect) / direct" math with "direct := 22". + +GitHub issue #839 has more details on this issue and its origin in ClusterFuzz +finding 66812. +--- + expat/lib/xmlparse.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/lib/xmlparse.c b/expat/lib/xmlparse.c +index b884d82b..d44baa68 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -7787,6 +7787,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; +@@ -7794,7 +7796,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; + } +-- +2.33.0 + + diff --git a/backport-CVE-2024-28757-002.patch b/backport-CVE-2024-28757-002.patch new file mode 100644 index 0000000..ad961a5 --- /dev/null +++ b/backport-CVE-2024-28757-002.patch @@ -0,0 +1,26 @@ +From a4c86a395ee447c59175c762af3d17f7107b2261 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Sun, 3 Mar 2024 02:19:58 +0100 +Subject: [PATCH] lib/xmlparse.c: Reject directly recursive parameter entities + +--- + expat/lib/xmlparse.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/xmlparse.c b/expat/lib/xmlparse.c +index b884d82b..8e667fcb 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -6240,7 +6240,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; +-- +2.33.0 + + diff --git a/backport-CVE-2024-28757-003.patch b/backport-CVE-2024-28757-003.patch new file mode 100644 index 0000000..99c4df4 --- /dev/null +++ b/backport-CVE-2024-28757-003.patch @@ -0,0 +1,86 @@ +From bef3be76723b344565736a902c98bad5f127f5bc Mon Sep 17 00:00:00 2001 +From: caixiaomeng 00662745 +Date: Thu, 21 Mar 2024 20:29:49 +0800 +Subject: [PATCH] tests-Cover-amplification-tracking-for-isolated-exte + +--- + tests/runtests.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 55 insertions(+) + +diff --git a/tests/runtests.c b/tests/runtests.c +index 2237b4d..191e7c1 100644 +--- a/tests/runtests.c ++++ b/tests/runtests.c +@@ -11966,6 +11966,59 @@ START_TEST(test_helper_unsigned_char_to_printable) { + END_TEST + #endif // defined(XML_DTD) + ++START_TEST(test_amplification_isolated_external_parser) { ++ // NOTE: Length 44 is precisely twice the length of "" ++ // (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(parser != NULL); ++ ++ assert(XML_SetBillionLaughsAttackProtectionMaximumAmplification( ++ parser, maximumToleratedAmplification) ++ == XML_TRUE); ++ assert(XML_SetBillionLaughsAttackProtectionActivationThreshold( ++ parser, activationThresholdBytes) ++ == XML_TRUE); ++ ++ XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL); ++ assert(ext_parser != NULL); ++ ++ const enum XML_Status actualStatus ++ = _XML_Parse_SINGLE_BYTES(ext_parser, doc, docLen, XML_TRUE); ++ ++ assert(actualStatus == expectedStatus); ++ if (actualStatus != XML_STATUS_OK) { ++ assert(XML_GetErrorCode(ext_parser) ++ == XML_ERROR_AMPLIFICATION_LIMIT_BREACH); ++ } ++ ++ XML_ParserFree(ext_parser); ++ XML_ParserFree(parser); ++ } ++} ++END_TEST ++ + static Suite * + make_suite(void) { + Suite *s = suite_create("basic"); +@@ -12206,6 +12259,8 @@ 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__ifdef_xml_dtd(tc_basic, ++ test_amplification_isolated_external_parser); + + suite_add_tcase(s, tc_namespace); + tcase_add_checked_fixture(tc_namespace, namespace_setup, namespace_teardown); +-- +2.33.0 + + diff --git a/backport-CVE-2024-28757-004.patch b/backport-CVE-2024-28757-004.patch new file mode 100644 index 0000000..1511b59 --- /dev/null +++ b/backport-CVE-2024-28757-004.patch @@ -0,0 +1,75 @@ +From 7cf9724d39209d085849b96e5012e658760902ad Mon Sep 17 00:00:00 2001 +From: caixiaomeng 00662745 +Date: Thu, 21 Mar 2024 20:40:48 +0800 +Subject: [PATCH] tests-Cover-rejection-of-direct-parameter-entity-rec + +--- + tests/runtests.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 44 insertions(+) + +diff --git a/tests/runtests.c b/tests/runtests.c +index 191e7c1..1c1ce56 100644 +--- a/tests/runtests.c ++++ b/tests/runtests.c +@@ -12019,6 +12019,48 @@ START_TEST(test_amplification_isolated_external_parser) { + } + 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(parser != NULL); ++ ++ XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL); ++ assert(ext_parser != NULL); ++ ++ const enum XML_Status actualStatus ++ = _XML_Parse_SINGLE_BYTES(ext_parser, doc, (int)strlen(doc), XML_TRUE); ++ ++ assert(actualStatus == expectedStatus); ++ if (actualStatus != XML_STATUS_OK) { ++ assert(XML_GetErrorCode(ext_parser) ++ == XML_ERROR_RECURSIVE_ENTITY_REF); ++ } ++ ++ XML_ParserFree(ext_parser); ++ XML_ParserFree(parser); ++ } ++} ++END_TEST ++ + static Suite * + make_suite(void) { + Suite *s = suite_create("basic"); +@@ -12261,6 +12303,8 @@ make_suite(void) { + tcase_add_test(tc_basic, test_empty_element_abort); + tcase_add_test__ifdef_xml_dtd(tc_basic, + test_amplification_isolated_external_parser); ++ tcase_add_test__ifdef_xml_dtd(tc_basic, ++ test_recursive_external_parameter_entity_2); + + suite_add_tcase(s, tc_namespace); + tcase_add_checked_fixture(tc_namespace, namespace_setup, namespace_teardown); +-- +2.33.0 + + diff --git a/expat.spec b/expat.spec index 84dec90..57b2664 100644 --- a/expat.spec +++ b/expat.spec @@ -1,7 +1,7 @@ %define Rversion %(echo %{version} | sed -e 's/\\./_/g' -e 's/^/R_/') Name: expat Version: 2.4.1 -Release: 8 +Release: 10 Summary: An XML parser library License: MIT URL: https://libexpat.github.io/ @@ -30,6 +30,14 @@ Patch19: backport-0001-CVE-2022-40674.patch Patch20: backport-0002-CVE-2022-40674.patch Patch21: backport-CVE-2022-43680.patch Patch22: backport-tests-Cover-overeager-DTD-destruction-in-XML_Externa.patch +Patch23: backport-CVE-2024-28757-001.patch +Patch24: backport-CVE-2024-28757-002.patch +Patch25: backport-CVE-2024-28757-003.patch +Patch26: backport-CVE-2024-28757-004.patch +Patch27: backport-001-CVE-2023-52426.patch +Patch28: backport-002-CVE-2023-52426.patch +Patch29: backport-003-CVE-2023-52426.patch +Patch30: backport-004-CVE-2023-52426.patch BuildRequires: sed,autoconf,automake,gcc-c++,libtool,xmlto @@ -83,12 +91,21 @@ make check %{_mandir}/man1/* %changelog -* Tue Dec 13 2022 zhoupengcheng - 2.4.8-8 -- Move autoreconf to build +* Mon Mar 25 2024 caixiaomeng - 2.4.1-10 +- fix CVE-2023-52426 -* Sat Oct 29 2022 fuanan - 2.4.1-7 +* Mon Mar 25 2024 caixiaomeng - 2.4.1-9 +- fix CVE-2024-28757 + +* Sat Oct 29 2022 fuanan - 2.4.1-8 - fix CVE-2022-43680 +* Tue Oct 11 2022 huangduirong - 2.4.1-7 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC:Move autoreconf to build + * Thu Sep 15 2022 panxiaohe - 2.4.1-6 - fix CVE-2022-40674 @@ -100,7 +117,7 @@ make check * Sat Feb 26 2022 yangzhuangzhuang - 2.4.1-4 - Type:CVE -- ID:CVE-2022-25235 CVE-2022-25236 CVE-2022-25313 CVE-2022-25314 CVE-2022-25315 +- ID:Fix CVE-2022-25235 CVE-2022-25236 CVE-2022-25313 CVE-2022-25314 CVE-2022-25315 - SUG:NA - DESC:Fix CVE-2022-25235 CVE-2022-25236 CVE-2022-25313 CVE-2022-25314 CVE-2022-25315 @@ -159,3 +176,4 @@ make check * Thu Aug 29 2019 openEuler Buildteam - 2.2.6-1 - Package Init +