diff options
Diffstat (limited to 'meta/recipes-core/expat/expat')
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2013-0340.patch | 1758 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2021-45960.patch | 65 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2021-46143.patch | 49 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2022-22822-27.patch | 257 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2022-23852.patch | 33 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2022-23990.patch | 49 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2022-25235.patch | 283 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2022-25236.patch | 129 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2022-25313-regression.patch | 131 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2022-25313.patch | 230 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2022-25314.patch | 32 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2022-25315.patch | 145 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2022-40674.patch | 53 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2022-43680.patch | 33 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat/libtool-tag.patch | 41 |
15 files changed, 3266 insertions, 22 deletions
diff --git a/meta/recipes-core/expat/expat/CVE-2013-0340.patch b/meta/recipes-core/expat/expat/CVE-2013-0340.patch new file mode 100644 index 0000000000..1ab4d06508 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2013-0340.patch @@ -0,0 +1,1758 @@ +From a644ccf25392523b1329872310e24d0fc5f40629 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Mon, 19 Apr 2021 21:42:51 +0200 +Subject: [PATCH] expat: Backport fix for CVE-2013-0340 + +Issue: https://github.com/libexpat/libexpat/issues/34 + +This patch cherry-picks the following commits from upstream release +2.4.0 onto 2.2.9: + +- b1d039607d3d8a042bf0466bfcc1c0f104e353c8 +- 60959f2b491876199879d97c8ed956eabb0c2e73 + +Upstream-Status: Backport +CVE: CVE-2013-0340 +Signed-off-by: Jasper Orschulko <jasper@fancydomain.eu> +--- + lib/expat.h | 21 +- + lib/internal.h | 30 + + lib/libexpat.def | 3 + + lib/libexpatw.def | 3 + + lib/xmlparse.c | 1147 +++++++++++++++++++++++++++++++++++++-- + 5 files changed, 1143 insertions(+), 61 deletions(-) + +diff --git a/lib/expat.h b/lib/expat.h +index 48a6e2a3..0fb70d9d 100644 +--- a/lib/expat.h ++++ b/lib/expat.h +@@ -115,7 +115,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, ++ /* Added in 2.4.0. */ ++ XML_ERROR_AMPLIFICATION_LIMIT_BREACH + }; + + enum XML_Content_Type { +@@ -997,7 +999,10 @@ enum XML_FeatureEnum { + XML_FEATURE_SIZEOF_XML_LCHAR, + XML_FEATURE_NS, + XML_FEATURE_LARGE_SIZE, +- XML_FEATURE_ATTR_INFO ++ 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 + /* Additional features must be added to the end of this enum. */ + }; + +@@ -1010,6 +1015,18 @@ typedef struct { + XMLPARSEAPI(const XML_Feature *) + XML_GetFeatureList(void); + ++#ifdef XML_DTD ++/* Added in Expat 2.4.0. */ ++XMLPARSEAPI(XML_Bool) ++XML_SetBillionLaughsAttackProtectionMaximumAmplification( ++ XML_Parser parser, float maximumAmplificationFactor); ++ ++/* Added in Expat 2.4.0. */ ++XMLPARSEAPI(XML_Bool) ++XML_SetBillionLaughsAttackProtectionActivationThreshold( ++ XML_Parser parser, unsigned long long activationThresholdBytes); ++#endif ++ + /* Expat follows the semantic versioning convention. + See http://semver.org. + */ +diff --git a/lib/internal.h b/lib/internal.h +index 60913dab..d8b31fa2 100644 +--- a/lib/internal.h ++++ b/lib/internal.h +@@ -101,10 +101,40 @@ + # endif + #endif + ++#include <limits.h> // ULONG_MAX ++ ++#if defined(_WIN32) && ! defined(__USE_MINGW_ANSI_STDIO) ++# define EXPAT_FMT_ULL(midpart) "%" midpart "I64u" ++# if defined(_WIN64) // Note: modifier "td" does not work for MinGW ++# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "I64d" ++# else ++# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d" ++# endif ++#else ++# define EXPAT_FMT_ULL(midpart) "%" midpart "llu" ++# if ! defined(ULONG_MAX) ++# error Compiler did not define ULONG_MAX for us ++# elif ULONG_MAX == 18446744073709551615u // 2^64-1 ++# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld" ++# else ++# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d" ++# endif ++#endif ++ + #ifndef UNUSED_P + # define UNUSED_P(p) (void)p + #endif + ++/* NOTE BEGIN If you ever patch these defaults to greater values ++ for non-attack XML payload in your environment, ++ please file a bug report with libexpat. Thank you! ++*/ ++#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT \ ++ 100.0f ++#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \ ++ 8388608 // 8 MiB, 2^23 ++/* NOTE END */ ++ + #ifdef __cplusplus + extern "C" { + #endif +diff --git a/lib/libexpat.def b/lib/libexpat.def +index 16faf595..5aefa6df 100644 +--- a/lib/libexpat.def ++++ b/lib/libexpat.def +@@ -76,3 +76,6 @@ EXPORTS + XML_SetHashSalt @67 + ; added with version 2.2.5 + _INTERNAL_trim_to_complete_utf8_characters @68 ++; added with version 2.4.0 ++ XML_SetBillionLaughsAttackProtectionActivationThreshold @69 ++ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70 +diff --git a/lib/libexpatw.def b/lib/libexpatw.def +index 16faf595..5aefa6df 100644 +--- a/lib/libexpatw.def ++++ b/lib/libexpatw.def +@@ -76,3 +76,6 @@ EXPORTS + XML_SetHashSalt @67 + ; added with version 2.2.5 + _INTERNAL_trim_to_complete_utf8_characters @68 ++; added with version 2.4.0 ++ XML_SetBillionLaughsAttackProtectionActivationThreshold @69 ++ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70 +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 3aaf35b9..6790bc28 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -47,6 +47,8 @@ + #include <limits.h> /* UINT_MAX */ + #include <stdio.h> /* fprintf */ + #include <stdlib.h> /* getenv, rand_s */ ++#include <stdint.h> /* uintptr_t */ ++#include <math.h> /* isnan */ + + #ifdef _WIN32 + # define getpid GetCurrentProcessId +@@ -373,6 +375,31 @@ typedef struct open_internal_entity { + XML_Bool betweenDecl; /* WFC: PE Between Declarations */ + } OPEN_INTERNAL_ENTITY; + ++enum XML_Account { ++ XML_ACCOUNT_DIRECT, /* bytes directly passed to the Expat parser */ ++ XML_ACCOUNT_ENTITY_EXPANSION, /* intermediate bytes produced during entity ++ expansion */ ++ XML_ACCOUNT_NONE /* i.e. do not account, was accounted already */ ++}; ++ ++#ifdef XML_DTD ++typedef unsigned long long XmlBigCount; ++typedef struct accounting { ++ XmlBigCount countBytesDirect; ++ XmlBigCount countBytesIndirect; ++ int debugLevel; ++ float maximumAmplificationFactor; // >=1.0 ++ unsigned long long activationThresholdBytes; ++} ACCOUNTING; ++ ++typedef struct entity_stats { ++ unsigned int countEverOpened; ++ unsigned int currentDepth; ++ unsigned int maximumDepthSeen; ++ int debugLevel; ++} ENTITY_STATS; ++#endif /* XML_DTD */ ++ + typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start, + const char *end, const char **endPtr); + +@@ -403,16 +430,18 @@ static enum XML_Error initializeEncoding(XML_Parser parser); + static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc, + const char *s, const char *end, int tok, + const char *next, const char **nextPtr, +- XML_Bool haveMore, XML_Bool allowClosingDoctype); ++ 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 doContent(XML_Parser parser, int startTagLevel, + const ENCODING *enc, const char *start, + const char *end, const char **endPtr, +- XML_Bool haveMore); ++ XML_Bool haveMore, enum XML_Account account); + static enum XML_Error doCdataSection(XML_Parser parser, const ENCODING *, + const char **startPtr, const char *end, +- const char **nextPtr, XML_Bool haveMore); ++ const char **nextPtr, XML_Bool haveMore, ++ enum XML_Account account); + #ifdef XML_DTD + static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *, + const char **startPtr, const char *end, +@@ -422,7 +451,8 @@ static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *, + static void freeBindings(XML_Parser parser, BINDING *bindings); + static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *, + const char *s, TAG_NAME *tagNamePtr, +- BINDING **bindingsPtr); ++ BINDING **bindingsPtr, ++ enum XML_Account account); + static enum XML_Error addBinding(XML_Parser parser, PREFIX *prefix, + const ATTRIBUTE_ID *attId, const XML_Char *uri, + BINDING **bindingsPtr); +@@ -431,15 +461,18 @@ static int defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata, + XML_Parser parser); + static enum XML_Error storeAttributeValue(XML_Parser parser, const ENCODING *, + XML_Bool isCdata, const char *, +- const char *, STRING_POOL *); ++ 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 *); ++ const char *, STRING_POOL *, ++ enum XML_Account account); + 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); ++ 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, +@@ -503,6 +536,35 @@ static XML_Parser parserCreate(const XML_Char *encodingName, + + static void parserInit(XML_Parser parser, const XML_Char *encodingName); + ++#ifdef XML_DTD ++static float accountingGetCurrentAmplification(XML_Parser rootParser); ++static void accountingReportStats(XML_Parser originParser, const char *epilog); ++static void accountingOnAbort(XML_Parser originParser); ++static void accountingReportDiff(XML_Parser rootParser, ++ unsigned int levelsAwayFromRootParser, ++ const char *before, const char *after, ++ ptrdiff_t bytesMore, int source_line, ++ enum XML_Account account); ++static XML_Bool accountingDiffTolerated(XML_Parser originParser, int tok, ++ const char *before, const char *after, ++ int source_line, ++ enum XML_Account account); ++ ++static void entityTrackingReportStats(XML_Parser parser, ENTITY *entity, ++ const char *action, int sourceLine); ++static void entityTrackingOnOpen(XML_Parser parser, ENTITY *entity, ++ int sourceLine); ++static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity, ++ int sourceLine); ++ ++static XML_Parser getRootParserOf(XML_Parser parser, ++ unsigned int *outLevelDiff); ++static const char *unsignedCharToPrintable(unsigned char c); ++#endif /* XML_DTD */ ++ ++static unsigned long getDebugLevel(const char *variableName, ++ unsigned long defaultDebugLevel); ++ + #define poolStart(pool) ((pool)->start) + #define poolEnd(pool) ((pool)->ptr) + #define poolLength(pool) ((pool)->ptr - (pool)->start) +@@ -616,6 +678,10 @@ struct XML_ParserStruct { + enum XML_ParamEntityParsing m_paramEntityParsing; + #endif + unsigned long m_hash_secret_salt; ++#ifdef XML_DTD ++ ACCOUNTING m_accounting; ++ ENTITY_STATS m_entity_stats; ++#endif + }; + + #define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) +@@ -1055,6 +1121,18 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { + parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; + #endif + parser->m_hash_secret_salt = 0; ++ ++#ifdef XML_DTD ++ memset(&parser->m_accounting, 0, sizeof(ACCOUNTING)); ++ parser->m_accounting.debugLevel = getDebugLevel("EXPAT_ACCOUNTING_DEBUG", 0u); ++ parser->m_accounting.maximumAmplificationFactor ++ = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT; ++ parser->m_accounting.activationThresholdBytes ++ = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT; ++ ++ memset(&parser->m_entity_stats, 0, sizeof(ENTITY_STATS)); ++ parser->m_entity_stats.debugLevel = getDebugLevel("EXPAT_ENTITY_DEBUG", 0u); ++#endif + } + + /* moves list of bindings to m_freeBindingList */ +@@ -2318,6 +2396,10 @@ 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"); ++ /* Added in 2.4.0. */ ++ case XML_ERROR_AMPLIFICATION_LIMIT_BREACH: ++ return XML_L( ++ "limit on input amplification factor (from DTD and entities) breached"); + } + return NULL; + } +@@ -2354,41 +2436,75 @@ XML_ExpatVersionInfo(void) { + + const XML_Feature *XMLCALL + XML_GetFeatureList(void) { +- static const XML_Feature features[] +- = {{XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"), +- sizeof(XML_Char)}, +- {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"), +- sizeof(XML_LChar)}, ++ static const XML_Feature features[] = { ++ {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"), ++ sizeof(XML_Char)}, ++ {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"), ++ sizeof(XML_LChar)}, + #ifdef XML_UNICODE +- {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0}, ++ {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0}, + #endif + #ifdef XML_UNICODE_WCHAR_T +- {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0}, ++ {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0}, + #endif + #ifdef XML_DTD +- {XML_FEATURE_DTD, XML_L("XML_DTD"), 0}, ++ {XML_FEATURE_DTD, XML_L("XML_DTD"), 0}, + #endif + #ifdef XML_CONTEXT_BYTES +- {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), +- XML_CONTEXT_BYTES}, ++ {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), ++ XML_CONTEXT_BYTES}, + #endif + #ifdef XML_MIN_SIZE +- {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, ++ {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, + #endif + #ifdef XML_NS +- {XML_FEATURE_NS, XML_L("XML_NS"), 0}, ++ {XML_FEATURE_NS, XML_L("XML_NS"), 0}, + #endif + #ifdef XML_LARGE_SIZE +- {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0}, ++ {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0}, + #endif + #ifdef XML_ATTR_INFO +- {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0}, ++ {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0}, + #endif +- {XML_FEATURE_END, NULL, 0}}; ++#ifdef XML_DTD ++ /* Added in Expat 2.4.0. */ ++ {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, ++ XML_L("XML_BLAP_MAX_AMP"), ++ (long int) ++ EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT}, ++ {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, ++ XML_L("XML_BLAP_ACT_THRES"), ++ EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT}, ++#endif ++ {XML_FEATURE_END, NULL, 0}}; + + return features; + } + ++#ifdef XML_DTD ++XML_Bool XMLCALL ++XML_SetBillionLaughsAttackProtectionMaximumAmplification( ++ XML_Parser parser, float maximumAmplificationFactor) { ++ if ((parser == NULL) || (parser->m_parentParser != NULL) ++ || isnan(maximumAmplificationFactor) ++ || (maximumAmplificationFactor < 1.0f)) { ++ return XML_FALSE; ++ } ++ parser->m_accounting.maximumAmplificationFactor = maximumAmplificationFactor; ++ return XML_TRUE; ++} ++ ++XML_Bool XMLCALL ++XML_SetBillionLaughsAttackProtectionActivationThreshold( ++ XML_Parser parser, unsigned long long activationThresholdBytes) { ++ if ((parser == NULL) || (parser->m_parentParser != NULL)) { ++ return XML_FALSE; ++ } ++ parser->m_accounting.activationThresholdBytes = activationThresholdBytes; ++ return XML_TRUE; ++} ++#endif /* XML_DTD */ ++ + /* 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 +@@ -2441,9 +2557,9 @@ storeRawNames(XML_Parser parser) { + 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); ++ enum XML_Error result = doContent( ++ parser, 0, parser->m_encoding, start, end, endPtr, ++ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT); + if (result == XML_ERROR_NONE) { + if (! storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; +@@ -2468,6 +2584,14 @@ 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 (! accountingDiffTolerated(parser, tok, start, next, __LINE__, ++ XML_ACCOUNT_DIRECT)) { ++ accountingOnAbort(parser); ++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; ++ } ++#endif /* XML_DTD */ ++ + /* If we are at the end of the buffer, this would cause the next stage, + i.e. externalEntityInitProcessor3, to pass control directly to + doContent (by detecting XML_TOK_NONE) without processing any xml text +@@ -2505,6 +2629,10 @@ externalEntityInitProcessor3(XML_Parser parser, const char *start, + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + parser->m_eventPtr = start; + tok = XmlContentTok(parser->m_encoding, start, end, &next); ++ /* Note: These bytes are accounted later in: ++ - processXmlDecl ++ - externalEntityContentProcessor ++ */ + parser->m_eventEndPtr = next; + + switch (tok) { +@@ -2546,7 +2674,8 @@ externalEntityContentProcessor(XML_Parser parser, const char *start, + const char *end, const char **endPtr) { + enum XML_Error result + = doContent(parser, 1, parser->m_encoding, start, end, endPtr, +- (XML_Bool)! parser->m_parsingStatus.finalBuffer); ++ (XML_Bool)! parser->m_parsingStatus.finalBuffer, ++ XML_ACCOUNT_ENTITY_EXPANSION); + if (result == XML_ERROR_NONE) { + if (! storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; +@@ -2557,7 +2686,7 @@ externalEntityContentProcessor(XML_Parser parser, const char *start, + static enum XML_Error + doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + const char *s, const char *end, const char **nextPtr, +- XML_Bool haveMore) { ++ XML_Bool haveMore, enum XML_Account account) { + /* save one level of indirection */ + DTD *const dtd = parser->m_dtd; + +@@ -2575,6 +2704,17 @@ 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 ++ const char *accountAfter ++ = ((tok == XML_TOK_TRAILING_RSQB) || (tok == XML_TOK_TRAILING_CR)) ++ ? (haveMore ? s /* i.e. 0 bytes */ : end) ++ : next; ++ if (! accountingDiffTolerated(parser, tok, s, accountAfter, __LINE__, ++ account)) { ++ accountingOnAbort(parser); ++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; ++ } ++#endif + *eventEndPP = next; + switch (tok) { + case XML_TOK_TRAILING_CR: +@@ -2630,6 +2770,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 ++ /* 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 */ + if (parser->m_characterDataHandler) + parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1); + else if (parser->m_defaultHandler) +@@ -2748,7 +2896,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + } + tag->name.str = (XML_Char *)tag->buf; + *toPtr = XML_T('\0'); +- result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings)); ++ result ++ = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings), account); + if (result) + return result; + if (parser->m_startElementHandler) +@@ -2772,7 +2921,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + if (! name.str) + return XML_ERROR_NO_MEMORY; + poolFinish(&parser->m_tempPool); +- result = storeAtts(parser, enc, s, &name, &bindings); ++ result = storeAtts(parser, enc, s, &name, &bindings, ++ XML_ACCOUNT_NONE /* token spans whole start tag */); + if (result != XML_ERROR_NONE) { + freeBindings(parser, bindings); + return result; +@@ -2907,7 +3057,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + /* END disabled code */ + else if (parser->m_defaultHandler) + reportDefault(parser, enc, s, next); +- result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore); ++ result ++ = doCdataSection(parser, enc, &next, end, nextPtr, haveMore, account); + if (result != XML_ERROR_NONE) + return result; + else if (! next) { +@@ -3036,7 +3187,8 @@ freeBindings(XML_Parser parser, BINDING *bindings) { + */ + static enum XML_Error + storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, +- TAG_NAME *tagNamePtr, BINDING **bindingsPtr) { ++ TAG_NAME *tagNamePtr, BINDING **bindingsPtr, ++ enum XML_Account account) { + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ + ELEMENT_TYPE *elementType; + int nDefaultAtts; +@@ -3146,7 +3298,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, + /* normalize the attribute value */ + result = storeAttributeValue( + parser, enc, isCdata, parser->m_atts[i].valuePtr, +- parser->m_atts[i].valueEnd, &parser->m_tempPool); ++ parser->m_atts[i].valueEnd, &parser->m_tempPool, account); + if (result) + return result; + appAtts[attIndex] = poolStart(&parser->m_tempPool); +@@ -3535,9 +3687,9 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + static enum XML_Error PTRCALL + cdataSectionProcessor(XML_Parser parser, const char *start, const char *end, + const char **endPtr) { +- enum XML_Error result +- = doCdataSection(parser, parser->m_encoding, &start, end, endPtr, +- (XML_Bool)! parser->m_parsingStatus.finalBuffer); ++ enum XML_Error result = doCdataSection( ++ parser, parser->m_encoding, &start, end, endPtr, ++ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT); + if (result != XML_ERROR_NONE) + return result; + if (start) { +@@ -3557,7 +3709,8 @@ cdataSectionProcessor(XML_Parser parser, const char *start, const char *end, + */ + static enum XML_Error + doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, +- const char *end, const char **nextPtr, XML_Bool haveMore) { ++ const char *end, const char **nextPtr, XML_Bool haveMore, ++ enum XML_Account account) { + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; +@@ -3575,6 +3728,14 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, + for (;;) { + const char *next; + int tok = XmlCdataSectionTok(enc, s, end, &next); ++#ifdef XML_DTD ++ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) { ++ accountingOnAbort(parser); ++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; ++ } ++#else ++ UNUSED_P(account); ++#endif + *eventEndPP = next; + switch (tok) { + case XML_TOK_CDATA_SECT_CLOSE: +@@ -3719,6 +3880,13 @@ doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, + *eventPP = s; + *startPtr = NULL; + tok = XmlIgnoreSectionTok(enc, s, end, &next); ++# ifdef XML_DTD ++ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, ++ XML_ACCOUNT_DIRECT)) { ++ accountingOnAbort(parser); ++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; ++ } ++# endif + *eventEndPP = next; + switch (tok) { + case XML_TOK_IGNORE_SECT: +@@ -3803,6 +3971,15 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s, + const char *versionend; + const XML_Char *storedversion = NULL; + int standalone = -1; ++ ++#ifdef XML_DTD ++ if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__, ++ XML_ACCOUNT_DIRECT)) { ++ accountingOnAbort(parser); ++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; ++ } ++#endif ++ + if (! (parser->m_ns ? XmlParseXmlDeclNS : XmlParseXmlDecl)( + isGeneralTextEntity, parser->m_encoding, s, next, &parser->m_eventPtr, + &version, &versionend, &encodingName, &newEncoding, &standalone)) { +@@ -3952,6 +4129,10 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, + + for (;;) { + tok = XmlPrologTok(parser->m_encoding, start, end, &next); ++ /* Note: Except for XML_TOK_BOM below, these bytes are accounted later in: ++ - storeEntityValue ++ - processXmlDecl ++ */ + parser->m_eventEndPtr = next; + if (tok <= 0) { + if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) { +@@ -3970,7 +4151,8 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, + break; + } + /* 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, ++ XML_ACCOUNT_DIRECT); + } else if (tok == XML_TOK_XML_DECL) { + enum XML_Error result; + result = processXmlDecl(parser, 0, start, next); +@@ -3997,6 +4179,14 @@ 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 (! accountingDiffTolerated(parser, tok, s, next, __LINE__, ++ XML_ACCOUNT_DIRECT)) { ++ accountingOnAbort(parser); ++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; ++ } ++# endif ++ + *nextPtr = next; + return XML_ERROR_NONE; + } +@@ -4039,16 +4229,24 @@ externalParEntProcessor(XML_Parser parser, const char *s, const char *end, + } + /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM. + However, when parsing an external subset, doProlog will not accept a BOM +- as valid, and report a syntax error, so we have to skip the BOM ++ as valid, and report a syntax error, so we have to skip the BOM, and ++ account for the BOM bytes. + */ + else if (tok == XML_TOK_BOM) { ++ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, ++ XML_ACCOUNT_DIRECT)) { ++ accountingOnAbort(parser); ++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; ++ } ++ + s = next; + tok = XmlPrologTok(parser->m_encoding, s, end, &next); + } + + parser->m_processor = prologProcessor; + return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr, +- (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE); ++ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE, ++ XML_ACCOUNT_DIRECT); + } + + static enum XML_Error PTRCALL +@@ -4061,6 +4259,9 @@ entityValueProcessor(XML_Parser parser, const char *s, const char *end, + + for (;;) { + tok = XmlPrologTok(enc, start, end, &next); ++ /* Note: These bytes are accounted later in: ++ - storeEntityValue ++ */ + if (tok <= 0) { + if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; +@@ -4078,7 +4279,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, XML_ACCOUNT_DIRECT); + } + start = next; + } +@@ -4092,13 +4293,14 @@ prologProcessor(XML_Parser parser, const char *s, const char *end, + const char *next = s; + int tok = XmlPrologTok(parser->m_encoding, s, end, &next); + return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr, +- (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE); ++ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE, ++ XML_ACCOUNT_DIRECT); + } + + 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) { ++ XML_Bool allowClosingDoctype, enum XML_Account account) { + #ifdef XML_DTD + static const XML_Char externalSubsetName[] = {ASCII_HASH, '\0'}; + #endif /* XML_DTD */ +@@ -4125,6 +4327,10 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + static const XML_Char enumValueSep[] = {ASCII_PIPE, '\0'}; + static const XML_Char enumValueStart[] = {ASCII_LPAREN, '\0'}; + ++#ifndef XML_DTD ++ UNUSED_P(account); ++#endif ++ + /* save one level of indirection */ + DTD *const dtd = parser->m_dtd; + +@@ -4189,6 +4395,19 @@ 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 ++ 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 ++ break; ++ default: ++ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) { ++ accountingOnAbort(parser); ++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; ++ } ++ } ++#endif + switch (role) { + case XML_ROLE_XML_DECL: { + enum XML_Error result = processXmlDecl(parser, 0, s, next); +@@ -4464,7 +4683,8 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + const XML_Char *attVal; + enum XML_Error result = storeAttributeValue( + parser, enc, parser->m_declAttributeIsCdata, +- s + enc->minBytesPerChar, next - enc->minBytesPerChar, &dtd->pool); ++ s + enc->minBytesPerChar, next - enc->minBytesPerChar, &dtd->pool, ++ XML_ACCOUNT_NONE); + if (result) + return result; + attVal = poolStart(&dtd->pool); +@@ -4497,8 +4717,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); + if (parser->m_declEntity) { + parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool); + parser->m_declEntity->textLen +@@ -4888,12 +5109,15 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + if (parser->m_externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; ++ entityTrackingOnOpen(parser, entity, __LINE__); + if (! parser->m_externalEntityRefHandler( + parser->m_externalEntityRefHandlerArg, 0, entity->base, + entity->systemId, entity->publicId)) { ++ entityTrackingOnClose(parser, entity, __LINE__); + entity->open = XML_FALSE; + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + } ++ entityTrackingOnClose(parser, entity, __LINE__); + entity->open = XML_FALSE; + handleDefault = XML_FALSE; + if (! dtd->paramEntityRead) { +@@ -5091,6 +5315,13 @@ 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 (! accountingDiffTolerated(parser, tok, s, next, __LINE__, ++ XML_ACCOUNT_DIRECT)) { ++ accountingOnAbort(parser); ++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; ++ } ++#endif + parser->m_eventEndPtr = next; + switch (tok) { + /* report partial linebreak - it might be the last token */ +@@ -5164,6 +5395,9 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) { + return XML_ERROR_NO_MEMORY; + } + entity->open = XML_TRUE; ++#ifdef XML_DTD ++ entityTrackingOnOpen(parser, entity, __LINE__); ++#endif + entity->processed = 0; + openEntity->next = parser->m_openInternalEntities; + parser->m_openInternalEntities = openEntity; +@@ -5182,17 +5416,22 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) { + int tok + = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd, +- tok, next, &next, XML_FALSE, XML_FALSE); ++ 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); ++ 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 { ++#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 */ +@@ -5225,12 +5464,13 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end, + int tok + = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd, +- tok, next, &next, XML_FALSE, XML_TRUE); ++ 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); ++ XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION); + + if (result != XML_ERROR_NONE) + return result; +@@ -5239,6 +5479,9 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end, + entity->processed = (int)(next - (char *)entity->textPtr); + return result; + } else { ++#ifdef XML_DTD ++ entityTrackingOnClose(parser, entity, __LINE__); ++#endif + entity->open = XML_FALSE; + parser->m_openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ +@@ -5252,7 +5495,8 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end, + parser->m_processor = prologProcessor; + tok = XmlPrologTok(parser->m_encoding, s, end, &next); + return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr, +- (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE); ++ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE, ++ XML_ACCOUNT_DIRECT); + } else + #endif /* XML_DTD */ + { +@@ -5260,7 +5504,8 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end, + /* see externalEntityContentProcessor vs contentProcessor */ + return doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding, + s, end, nextPtr, +- (XML_Bool)! parser->m_parsingStatus.finalBuffer); ++ (XML_Bool)! parser->m_parsingStatus.finalBuffer, ++ XML_ACCOUNT_DIRECT); + } + } + +@@ -5275,9 +5520,10 @@ errorProcessor(XML_Parser parser, const char *s, const char *end, + + static enum XML_Error + storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, +- const char *ptr, const char *end, STRING_POOL *pool) { ++ const char *ptr, const char *end, STRING_POOL *pool, ++ enum XML_Account account) { + enum XML_Error result +- = appendAttributeValue(parser, enc, isCdata, ptr, end, pool); ++ = appendAttributeValue(parser, enc, isCdata, ptr, end, pool, account); + if (result) + return result; + if (! isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) +@@ -5289,11 +5535,22 @@ 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) { ++ const char *ptr, const char *end, STRING_POOL *pool, ++ enum XML_Account account) { + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ ++#ifndef XML_DTD ++ UNUSED_P(account); ++#endif ++ + for (;;) { + const char *next; + int tok = XmlAttributeValueTok(enc, ptr, end, &next); ++#ifdef XML_DTD ++ if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) { ++ accountingOnAbort(parser); ++ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; ++ } ++#endif + switch (tok) { + case XML_TOK_NONE: + return XML_ERROR_NONE; +@@ -5353,6 +5610,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 ++ /* 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 */ + if (! poolAppendChar(pool, ch)) + return XML_ERROR_NO_MEMORY; + break; +@@ -5430,9 +5695,16 @@ 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 ++ entityTrackingOnOpen(parser, entity, __LINE__); ++#endif + result = appendAttributeValue(parser, parser->m_internalEncoding, +- isCdata, (char *)entity->textPtr, +- (char *)textEnd, pool); ++ 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; +@@ -5462,13 +5734,16 @@ 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) { ++ const char *entityTextPtr, const char *entityTextEnd, ++ enum XML_Account account) { + 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 + int oldInEntityValue = parser->m_prologState.inEntityValue; + parser->m_prologState.inEntityValue = 1; ++#else ++ UNUSED_P(account); + #endif /* XML_DTD */ + /* never return Null for the value argument in EntityDeclHandler, + since this would indicate an external entity; therefore we +@@ -5481,6 +5756,16 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, + for (;;) { + const char *next; + int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); ++ ++#ifdef XML_DTD ++ if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__, ++ account)) { ++ accountingOnAbort(parser); ++ result = XML_ERROR_AMPLIFICATION_LIMIT_BREACH; ++ goto endEntityValue; ++ } ++#endif ++ + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: + #ifdef XML_DTD +@@ -5516,13 +5801,16 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, + if (parser->m_externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; ++ entityTrackingOnOpen(parser, entity, __LINE__); + if (! parser->m_externalEntityRefHandler( + parser->m_externalEntityRefHandlerArg, 0, entity->base, + entity->systemId, entity->publicId)) { ++ entityTrackingOnClose(parser, entity, __LINE__); + entity->open = XML_FALSE; + result = XML_ERROR_EXTERNAL_ENTITY_HANDLING; + goto endEntityValue; + } ++ entityTrackingOnClose(parser, entity, __LINE__); + entity->open = XML_FALSE; + if (! dtd->paramEntityRead) + dtd->keepProcessing = dtd->standalone; +@@ -5530,9 +5818,12 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, + dtd->keepProcessing = dtd->standalone; + } else { + entity->open = XML_TRUE; ++ entityTrackingOnOpen(parser, entity, __LINE__); + result = storeEntityValue( +- parser, parser->m_internalEncoding, (char *)entity->textPtr, +- (char *)(entity->textPtr + entity->textLen)); ++ 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; +@@ -6893,3 +7184,741 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { + memcpy(result, s, charsRequired * sizeof(XML_Char)); + return result; + } ++ ++#ifdef XML_DTD ++ ++static float ++accountingGetCurrentAmplification(XML_Parser rootParser) { ++ const XmlBigCount countBytesOutput ++ = rootParser->m_accounting.countBytesDirect ++ + rootParser->m_accounting.countBytesIndirect; ++ const float amplificationFactor ++ = rootParser->m_accounting.countBytesDirect ++ ? (countBytesOutput ++ / (float)(rootParser->m_accounting.countBytesDirect)) ++ : 1.0f; ++ assert(! rootParser->m_parentParser); ++ return amplificationFactor; ++} ++ ++static void ++accountingReportStats(XML_Parser originParser, const char *epilog) { ++ const XML_Parser rootParser = getRootParserOf(originParser, NULL); ++ assert(! rootParser->m_parentParser); ++ ++ if (rootParser->m_accounting.debugLevel < 1) { ++ return; ++ } ++ ++ const float amplificationFactor ++ = accountingGetCurrentAmplification(rootParser); ++ fprintf(stderr, ++ "expat: Accounting(%p): Direct " EXPAT_FMT_ULL( ++ "10") ", indirect " EXPAT_FMT_ULL("10") ", amplification %8.2f%s", ++ (void *)rootParser, rootParser->m_accounting.countBytesDirect, ++ rootParser->m_accounting.countBytesIndirect, ++ (double)amplificationFactor, epilog); ++} ++ ++static void ++accountingOnAbort(XML_Parser originParser) { ++ accountingReportStats(originParser, " ABORTING\n"); ++} ++ ++static void ++accountingReportDiff(XML_Parser rootParser, ++ unsigned int levelsAwayFromRootParser, const char *before, ++ const char *after, ptrdiff_t bytesMore, int source_line, ++ enum XML_Account account) { ++ assert(! rootParser->m_parentParser); ++ ++ fprintf(stderr, ++ " (+" EXPAT_FMT_PTRDIFF_T("6") " bytes %s|%d, xmlparse.c:%d) %*s\"", ++ bytesMore, (account == XML_ACCOUNT_DIRECT) ? "DIR" : "EXP", ++ levelsAwayFromRootParser, source_line, 10, ""); ++ ++ const char ellipis[] = "[..]"; ++ const size_t ellipsisLength = sizeof(ellipis) /* because compile-time */ - 1; ++ const unsigned int contextLength = 10; ++ ++ /* Note: Performance is of no concern here */ ++ const char *walker = before; ++ if ((rootParser->m_accounting.debugLevel >= 3) ++ || (after - before) ++ <= (ptrdiff_t)(contextLength + ellipsisLength + contextLength)) { ++ for (; walker < after; walker++) { ++ fprintf(stderr, "%s", unsignedCharToPrintable(walker[0])); ++ } ++ } else { ++ for (; walker < before + contextLength; walker++) { ++ fprintf(stderr, "%s", unsignedCharToPrintable(walker[0])); ++ } ++ fprintf(stderr, ellipis); ++ walker = after - contextLength; ++ for (; walker < after; walker++) { ++ fprintf(stderr, "%s", unsignedCharToPrintable(walker[0])); ++ } ++ } ++ fprintf(stderr, "\"\n"); ++} ++ ++static XML_Bool ++accountingDiffTolerated(XML_Parser originParser, int tok, const char *before, ++ const char *after, int source_line, ++ enum XML_Account account) { ++ /* Note: We need to check the token type *first* to be sure that ++ * we can even access variable <after>, safely. ++ * E.g. for XML_TOK_NONE <after> may hold an invalid pointer. */ ++ switch (tok) { ++ case XML_TOK_INVALID: ++ case XML_TOK_PARTIAL: ++ case XML_TOK_PARTIAL_CHAR: ++ case XML_TOK_NONE: ++ return XML_TRUE; ++ } ++ ++ if (account == XML_ACCOUNT_NONE) ++ return XML_TRUE; /* because these bytes have been accounted for, already */ ++ ++ unsigned int levelsAwayFromRootParser; ++ const XML_Parser rootParser ++ = getRootParserOf(originParser, &levelsAwayFromRootParser); ++ assert(! rootParser->m_parentParser); ++ ++ const int isDirect ++ = (account == XML_ACCOUNT_DIRECT) && (originParser == rootParser); ++ const ptrdiff_t bytesMore = after - before; ++ ++ XmlBigCount *const additionTarget ++ = isDirect ? &rootParser->m_accounting.countBytesDirect ++ : &rootParser->m_accounting.countBytesIndirect; ++ ++ /* Detect and avoid integer overflow */ ++ if (*additionTarget > (XmlBigCount)(-1) - (XmlBigCount)bytesMore) ++ return XML_FALSE; ++ *additionTarget += bytesMore; ++ ++ const XmlBigCount countBytesOutput ++ = rootParser->m_accounting.countBytesDirect ++ + rootParser->m_accounting.countBytesIndirect; ++ const float amplificationFactor ++ = accountingGetCurrentAmplification(rootParser); ++ const XML_Bool tolerated ++ = (countBytesOutput < rootParser->m_accounting.activationThresholdBytes) ++ || (amplificationFactor ++ <= rootParser->m_accounting.maximumAmplificationFactor); ++ ++ if (rootParser->m_accounting.debugLevel >= 2) { ++ accountingReportStats(rootParser, ""); ++ accountingReportDiff(rootParser, levelsAwayFromRootParser, before, after, ++ bytesMore, source_line, account); ++ } ++ ++ return tolerated; ++} ++ ++static void ++entityTrackingReportStats(XML_Parser rootParser, ENTITY *entity, ++ const char *action, int sourceLine) { ++ assert(! rootParser->m_parentParser); ++ if (rootParser->m_entity_stats.debugLevel < 1) ++ return; ++ ++# if defined(XML_UNICODE) ++ const char *const entityName = "[..]"; ++# else ++ const char *const entityName = entity->name; ++# endif ++ ++ fprintf( ++ stderr, ++ "expat: Entities(%p): Count %9d, depth %2d/%2d %*s%s%s; %s length %d (xmlparse.c:%d)\n", ++ (void *)rootParser, rootParser->m_entity_stats.countEverOpened, ++ rootParser->m_entity_stats.currentDepth, ++ rootParser->m_entity_stats.maximumDepthSeen, ++ (rootParser->m_entity_stats.currentDepth - 1) * 2, "", ++ entity->is_param ? "%" : "&", entityName, action, entity->textLen, ++ sourceLine); ++} ++ ++static void ++entityTrackingOnOpen(XML_Parser originParser, ENTITY *entity, int sourceLine) { ++ const XML_Parser rootParser = getRootParserOf(originParser, NULL); ++ assert(! rootParser->m_parentParser); ++ ++ rootParser->m_entity_stats.countEverOpened++; ++ rootParser->m_entity_stats.currentDepth++; ++ if (rootParser->m_entity_stats.currentDepth ++ > rootParser->m_entity_stats.maximumDepthSeen) { ++ rootParser->m_entity_stats.maximumDepthSeen++; ++ } ++ ++ entityTrackingReportStats(rootParser, entity, "OPEN ", sourceLine); ++} ++ ++static void ++entityTrackingOnClose(XML_Parser originParser, ENTITY *entity, int sourceLine) { ++ const XML_Parser rootParser = getRootParserOf(originParser, NULL); ++ assert(! rootParser->m_parentParser); ++ ++ entityTrackingReportStats(rootParser, entity, "CLOSE", sourceLine); ++ rootParser->m_entity_stats.currentDepth--; ++} ++ ++static XML_Parser ++getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff) { ++ XML_Parser rootParser = parser; ++ unsigned int stepsTakenUpwards = 0; ++ while (rootParser->m_parentParser) { ++ rootParser = rootParser->m_parentParser; ++ stepsTakenUpwards++; ++ } ++ assert(! rootParser->m_parentParser); ++ if (outLevelDiff != NULL) { ++ *outLevelDiff = stepsTakenUpwards; ++ } ++ return rootParser; ++} ++ ++static const char * ++unsignedCharToPrintable(unsigned char c) { ++ switch (c) { ++ case 0: ++ return "\\0"; ++ case 1: ++ return "\\x1"; ++ case 2: ++ return "\\x2"; ++ case 3: ++ return "\\x3"; ++ case 4: ++ return "\\x4"; ++ case 5: ++ return "\\x5"; ++ case 6: ++ return "\\x6"; ++ case 7: ++ return "\\x7"; ++ case 8: ++ return "\\x8"; ++ case 9: ++ return "\\t"; ++ case 10: ++ return "\\n"; ++ case 11: ++ return "\\xB"; ++ case 12: ++ return "\\xC"; ++ case 13: ++ return "\\r"; ++ case 14: ++ return "\\xE"; ++ case 15: ++ return "\\xF"; ++ case 16: ++ return "\\x10"; ++ case 17: ++ return "\\x11"; ++ case 18: ++ return "\\x12"; ++ case 19: ++ return "\\x13"; ++ case 20: ++ return "\\x14"; ++ case 21: ++ return "\\x15"; ++ case 22: ++ return "\\x16"; ++ case 23: ++ return "\\x17"; ++ case 24: ++ return "\\x18"; ++ case 25: ++ return "\\x19"; ++ case 26: ++ return "\\x1A"; ++ case 27: ++ return "\\x1B"; ++ case 28: ++ return "\\x1C"; ++ case 29: ++ return "\\x1D"; ++ case 30: ++ return "\\x1E"; ++ case 31: ++ return "\\x1F"; ++ case 32: ++ return " "; ++ case 33: ++ return "!"; ++ case 34: ++ return "\\\""; ++ case 35: ++ return "#"; ++ case 36: ++ return "$"; ++ case 37: ++ return "%"; ++ case 38: ++ return "&"; ++ case 39: ++ return "'"; ++ case 40: ++ return "("; ++ case 41: ++ return ")"; ++ case 42: ++ return "*"; ++ case 43: ++ return "+"; ++ case 44: ++ return ","; ++ case 45: ++ return "-"; ++ case 46: ++ return "."; ++ case 47: ++ return "/"; ++ case 48: ++ return "0"; ++ case 49: ++ return "1"; ++ case 50: ++ return "2"; ++ case 51: ++ return "3"; ++ case 52: ++ return "4"; ++ case 53: ++ return "5"; ++ case 54: ++ return "6"; ++ case 55: ++ return "7"; ++ case 56: ++ return "8"; ++ case 57: ++ return "9"; ++ case 58: ++ return ":"; ++ case 59: ++ return ";"; ++ case 60: ++ return "<"; ++ case 61: ++ return "="; ++ case 62: ++ return ">"; ++ case 63: ++ return "?"; ++ case 64: ++ return "@"; ++ case 65: ++ return "A"; ++ case 66: ++ return "B"; ++ case 67: ++ return "C"; ++ case 68: ++ return "D"; ++ case 69: ++ return "E"; ++ case 70: ++ return "F"; ++ case 71: ++ return "G"; ++ case 72: ++ return "H"; ++ case 73: ++ return "I"; ++ case 74: ++ return "J"; ++ case 75: ++ return "K"; ++ case 76: ++ return "L"; ++ case 77: ++ return "M"; ++ case 78: ++ return "N"; ++ case 79: ++ return "O"; ++ case 80: ++ return "P"; ++ case 81: ++ return "Q"; ++ case 82: ++ return "R"; ++ case 83: ++ return "S"; ++ case 84: ++ return "T"; ++ case 85: ++ return "U"; ++ case 86: ++ return "V"; ++ case 87: ++ return "W"; ++ case 88: ++ return "X"; ++ case 89: ++ return "Y"; ++ case 90: ++ return "Z"; ++ case 91: ++ return "["; ++ case 92: ++ return "\\\\"; ++ case 93: ++ return "]"; ++ case 94: ++ return "^"; ++ case 95: ++ return "_"; ++ case 96: ++ return "`"; ++ case 97: ++ return "a"; ++ case 98: ++ return "b"; ++ case 99: ++ return "c"; ++ case 100: ++ return "d"; ++ case 101: ++ return "e"; ++ case 102: ++ return "f"; ++ case 103: ++ return "g"; ++ case 104: ++ return "h"; ++ case 105: ++ return "i"; ++ case 106: ++ return "j"; ++ case 107: ++ return "k"; ++ case 108: ++ return "l"; ++ case 109: ++ return "m"; ++ case 110: ++ return "n"; ++ case 111: ++ return "o"; ++ case 112: ++ return "p"; ++ case 113: ++ return "q"; ++ case 114: ++ return "r"; ++ case 115: ++ return "s"; ++ case 116: ++ return "t"; ++ case 117: ++ return "u"; ++ case 118: ++ return "v"; ++ case 119: ++ return "w"; ++ case 120: ++ return "x"; ++ case 121: ++ return "y"; ++ case 122: ++ return "z"; ++ case 123: ++ return "{"; ++ case 124: ++ return "|"; ++ case 125: ++ return "}"; ++ case 126: ++ return "~"; ++ case 127: ++ return "\\x7F"; ++ case 128: ++ return "\\x80"; ++ case 129: ++ return "\\x81"; ++ case 130: ++ return "\\x82"; ++ case 131: ++ return "\\x83"; ++ case 132: ++ return "\\x84"; ++ case 133: ++ return "\\x85"; ++ case 134: ++ return "\\x86"; ++ case 135: ++ return "\\x87"; ++ case 136: ++ return "\\x88"; ++ case 137: ++ return "\\x89"; ++ case 138: ++ return "\\x8A"; ++ case 139: ++ return "\\x8B"; ++ case 140: ++ return "\\x8C"; ++ case 141: ++ return "\\x8D"; ++ case 142: ++ return "\\x8E"; ++ case 143: ++ return "\\x8F"; ++ case 144: ++ return "\\x90"; ++ case 145: ++ return "\\x91"; ++ case 146: ++ return "\\x92"; ++ case 147: ++ return "\\x93"; ++ case 148: ++ return "\\x94"; ++ case 149: ++ return "\\x95"; ++ case 150: ++ return "\\x96"; ++ case 151: ++ return "\\x97"; ++ case 152: ++ return "\\x98"; ++ case 153: ++ return "\\x99"; ++ case 154: ++ return "\\x9A"; ++ case 155: ++ return "\\x9B"; ++ case 156: ++ return "\\x9C"; ++ case 157: ++ return "\\x9D"; ++ case 158: ++ return "\\x9E"; ++ case 159: ++ return "\\x9F"; ++ case 160: ++ return "\\xA0"; ++ case 161: ++ return "\\xA1"; ++ case 162: ++ return "\\xA2"; ++ case 163: ++ return "\\xA3"; ++ case 164: ++ return "\\xA4"; ++ case 165: ++ return "\\xA5"; ++ case 166: ++ return "\\xA6"; ++ case 167: ++ return "\\xA7"; ++ case 168: ++ return "\\xA8"; ++ case 169: ++ return "\\xA9"; ++ case 170: ++ return "\\xAA"; ++ case 171: ++ return "\\xAB"; ++ case 172: ++ return "\\xAC"; ++ case 173: ++ return "\\xAD"; ++ case 174: ++ return "\\xAE"; ++ case 175: ++ return "\\xAF"; ++ case 176: ++ return "\\xB0"; ++ case 177: ++ return "\\xB1"; ++ case 178: ++ return "\\xB2"; ++ case 179: ++ return "\\xB3"; ++ case 180: ++ return "\\xB4"; ++ case 181: ++ return "\\xB5"; ++ case 182: ++ return "\\xB6"; ++ case 183: ++ return "\\xB7"; ++ case 184: ++ return "\\xB8"; ++ case 185: ++ return "\\xB9"; ++ case 186: ++ return "\\xBA"; ++ case 187: ++ return "\\xBB"; ++ case 188: ++ return "\\xBC"; ++ case 189: ++ return "\\xBD"; ++ case 190: ++ return "\\xBE"; ++ case 191: ++ return "\\xBF"; ++ case 192: ++ return "\\xC0"; ++ case 193: ++ return "\\xC1"; ++ case 194: ++ return "\\xC2"; ++ case 195: ++ return "\\xC3"; ++ case 196: ++ return "\\xC4"; ++ case 197: ++ return "\\xC5"; ++ case 198: ++ return "\\xC6"; ++ case 199: ++ return "\\xC7"; ++ case 200: ++ return "\\xC8"; ++ case 201: ++ return "\\xC9"; ++ case 202: ++ return "\\xCA"; ++ case 203: ++ return "\\xCB"; ++ case 204: ++ return "\\xCC"; ++ case 205: ++ return "\\xCD"; ++ case 206: ++ return "\\xCE"; ++ case 207: ++ return "\\xCF"; ++ case 208: ++ return "\\xD0"; ++ case 209: ++ return "\\xD1"; ++ case 210: ++ return "\\xD2"; ++ case 211: ++ return "\\xD3"; ++ case 212: ++ return "\\xD4"; ++ case 213: ++ return "\\xD5"; ++ case 214: ++ return "\\xD6"; ++ case 215: ++ return "\\xD7"; ++ case 216: ++ return "\\xD8"; ++ case 217: ++ return "\\xD9"; ++ case 218: ++ return "\\xDA"; ++ case 219: ++ return "\\xDB"; ++ case 220: ++ return "\\xDC"; ++ case 221: ++ return "\\xDD"; ++ case 222: ++ return "\\xDE"; ++ case 223: ++ return "\\xDF"; ++ case 224: ++ return "\\xE0"; ++ case 225: ++ return "\\xE1"; ++ case 226: ++ return "\\xE2"; ++ case 227: ++ return "\\xE3"; ++ case 228: ++ return "\\xE4"; ++ case 229: ++ return "\\xE5"; ++ case 230: ++ return "\\xE6"; ++ case 231: ++ return "\\xE7"; ++ case 232: ++ return "\\xE8"; ++ case 233: ++ return "\\xE9"; ++ case 234: ++ return "\\xEA"; ++ case 235: ++ return "\\xEB"; ++ case 236: ++ return "\\xEC"; ++ case 237: ++ return "\\xED"; ++ case 238: ++ return "\\xEE"; ++ case 239: ++ return "\\xEF"; ++ case 240: ++ return "\\xF0"; ++ case 241: ++ return "\\xF1"; ++ case 242: ++ return "\\xF2"; ++ case 243: ++ return "\\xF3"; ++ case 244: ++ return "\\xF4"; ++ case 245: ++ return "\\xF5"; ++ case 246: ++ return "\\xF6"; ++ case 247: ++ return "\\xF7"; ++ case 248: ++ return "\\xF8"; ++ case 249: ++ return "\\xF9"; ++ case 250: ++ return "\\xFA"; ++ case 251: ++ return "\\xFB"; ++ case 252: ++ return "\\xFC"; ++ case 253: ++ return "\\xFD"; ++ case 254: ++ return "\\xFE"; ++ case 255: ++ return "\\xFF"; ++ default: ++ assert(0); /* never gets here */ ++ return "dead code"; ++ } ++ assert(0); /* never gets here */ ++} ++ ++#endif /* XML_DTD */ ++ ++static unsigned long ++getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) { ++ const char *const valueOrNull = getenv(variableName); ++ if (valueOrNull == NULL) { ++ return defaultDebugLevel; ++ } ++ const char *const value = valueOrNull; ++ ++ errno = 0; ++ char *afterValue = (char *)value; ++ unsigned long debugLevel = strtoul(value, &afterValue, 10); ++ if ((errno != 0) || (afterValue[0] != '\0')) { ++ errno = 0; ++ return defaultDebugLevel; ++ } ++ ++ return debugLevel; ++} +-- +2.32.0 + diff --git a/meta/recipes-core/expat/expat/CVE-2021-45960.patch b/meta/recipes-core/expat/expat/CVE-2021-45960.patch new file mode 100644 index 0000000000..523449e22c --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2021-45960.patch @@ -0,0 +1,65 @@ +From 0adcb34c49bee5b19bd29b16a578c510c23597ea Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Mon, 27 Dec 2021 20:15:02 +0100 +Subject: [PATCH] lib: Detect and prevent troublesome left shifts in function + storeAtts (CVE-2021-45960) + +Upstream-Status: Backport: +https://github.com/libexpat/libexpat/pull/534/commits/0adcb34c49bee5b19bd29b16a578c510c23597ea + +CVE: CVE-2021-45960 +Signed-off-by: Steve Sakoman <steve@sakoman.com> + +--- + expat/lib/xmlparse.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c +index d730f41c3..b47c31b05 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -3414,7 +3414,13 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, + if (nPrefixes) { + int j; /* hash table index */ + unsigned long version = parser->m_nsAttsVersion; +- int nsAttsSize = (int)1 << parser->m_nsAttsPower; ++ ++ /* Detect and prevent invalid shift */ ++ if (parser->m_nsAttsPower >= sizeof(unsigned int) * 8 /* bits per byte */) { ++ return XML_ERROR_NO_MEMORY; ++ } ++ ++ unsigned int nsAttsSize = 1u << parser->m_nsAttsPower; + unsigned char oldNsAttsPower = parser->m_nsAttsPower; + /* size of hash table must be at least 2 * (# of prefixed attributes) */ + if ((nPrefixes << 1) +@@ -3425,7 +3431,28 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, + ; + if (parser->m_nsAttsPower < 3) + parser->m_nsAttsPower = 3; +- nsAttsSize = (int)1 << parser->m_nsAttsPower; ++ ++ /* Detect and prevent invalid shift */ ++ if (parser->m_nsAttsPower >= sizeof(nsAttsSize) * 8 /* bits per byte */) { ++ /* Restore actual size of memory in m_nsAtts */ ++ parser->m_nsAttsPower = oldNsAttsPower; ++ return XML_ERROR_NO_MEMORY; ++ } ++ ++ nsAttsSize = 1u << parser->m_nsAttsPower; ++ ++ /* Detect and prevent integer overflow. ++ * The preprocessor guard addresses the "always false" warning ++ * from -Wtype-limits on platforms where ++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ ++#if UINT_MAX >= SIZE_MAX ++ if (nsAttsSize > (size_t)(-1) / sizeof(NS_ATT)) { ++ /* Restore actual size of memory in m_nsAtts */ ++ parser->m_nsAttsPower = oldNsAttsPower; ++ return XML_ERROR_NO_MEMORY; ++ } ++#endif ++ + temp = (NS_ATT *)REALLOC(parser, parser->m_nsAtts, + nsAttsSize * sizeof(NS_ATT)); + if (! temp) { diff --git a/meta/recipes-core/expat/expat/CVE-2021-46143.patch b/meta/recipes-core/expat/expat/CVE-2021-46143.patch new file mode 100644 index 0000000000..b1a726d9a8 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2021-46143.patch @@ -0,0 +1,49 @@ +From 85ae9a2d7d0e9358f356b33977b842df8ebaec2b Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Sat, 25 Dec 2021 20:52:08 +0100 +Subject: [PATCH] lib: Prevent integer overflow on m_groupSize in function + doProlog (CVE-2021-46143) + +Upstream-Status: Backport: +https://github.com/libexpat/libexpat/pull/538/commits/85ae9a2d7d0e9358f356b33977b842df8ebaec2b + +CVE: CVE-2021-46143 + +Signed-off-by: Steve Sakoman <steve@sakoman.com> +--- + expat/lib/xmlparse.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c +index b47c31b0..8f243126 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -5046,6 +5046,11 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + if (parser->m_prologState.level >= parser->m_groupSize) { + if (parser->m_groupSize) { + { ++ /* Detect and prevent integer overflow */ ++ if (parser->m_groupSize > (unsigned int)(-1) / 2u) { ++ return XML_ERROR_NO_MEMORY; ++ } ++ + char *const new_connector = (char *)REALLOC( + parser, parser->m_groupConnector, parser->m_groupSize *= 2); + if (new_connector == NULL) { +@@ -5056,6 +5061,16 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + } + + if (dtd->scaffIndex) { ++ /* Detect and prevent integer overflow. ++ * The preprocessor guard addresses the "always false" warning ++ * from -Wtype-limits on platforms where ++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ ++#if UINT_MAX >= SIZE_MAX ++ if (parser->m_groupSize > (size_t)(-1) / sizeof(int)) { ++ return XML_ERROR_NO_MEMORY; ++ } ++#endif ++ + int *const new_scaff_index = (int *)REALLOC( + parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int)); + if (new_scaff_index == NULL) diff --git a/meta/recipes-core/expat/expat/CVE-2022-22822-27.patch b/meta/recipes-core/expat/expat/CVE-2022-22822-27.patch new file mode 100644 index 0000000000..e569fbc7ab --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2022-22822-27.patch @@ -0,0 +1,257 @@ +From 9f93e8036e842329863bf20395b8fb8f73834d9e Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Thu, 30 Dec 2021 22:46:03 +0100 +Subject: [PATCH] lib: Prevent integer overflow at multiple places + (CVE-2022-22822 to CVE-2022-22827) + +The involved functions are: +- addBinding (CVE-2022-22822) +- build_model (CVE-2022-22823) +- defineAttribute (CVE-2022-22824) +- lookup (CVE-2022-22825) +- nextScaffoldPart (CVE-2022-22826) +- storeAtts (CVE-2022-22827) + +Upstream-Status: Backport: +https://github.com/libexpat/libexpat/pull/539/commits/9f93e8036e842329863bf20395b8fb8f73834d9e + +CVE: CVE-2022-22822 CVE-2022-22823 CVE-2022-22824 CVE-2022-22825 CVE-2022-22826 CVE-2022-22827 +Signed-off-by: Steve Sakoman <steve@sakoman.com> + +--- + expat/lib/xmlparse.c | 153 ++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 151 insertions(+), 2 deletions(-) + +diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c +index 8f243126..575e73ee 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -3261,13 +3261,38 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, + + /* get the attributes from the tokenizer */ + n = XmlGetAttributes(enc, attStr, parser->m_attsSize, parser->m_atts); ++ ++ /* Detect and prevent integer overflow */ ++ if (n > INT_MAX - nDefaultAtts) { ++ return XML_ERROR_NO_MEMORY; ++ } ++ + if (n + nDefaultAtts > parser->m_attsSize) { + int oldAttsSize = parser->m_attsSize; + ATTRIBUTE *temp; + #ifdef XML_ATTR_INFO + XML_AttrInfo *temp2; + #endif ++ ++ /* Detect and prevent integer overflow */ ++ if ((nDefaultAtts > INT_MAX - INIT_ATTS_SIZE) ++ || (n > INT_MAX - (nDefaultAtts + INIT_ATTS_SIZE))) { ++ return XML_ERROR_NO_MEMORY; ++ } ++ + parser->m_attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; ++ ++ /* Detect and prevent integer overflow. ++ * The preprocessor guard addresses the "always false" warning ++ * from -Wtype-limits on platforms where ++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ ++#if UINT_MAX >= SIZE_MAX ++ if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(ATTRIBUTE)) { ++ parser->m_attsSize = oldAttsSize; ++ return XML_ERROR_NO_MEMORY; ++ } ++#endif ++ + temp = (ATTRIBUTE *)REALLOC(parser, (void *)parser->m_atts, + parser->m_attsSize * sizeof(ATTRIBUTE)); + if (temp == NULL) { +@@ -3276,6 +3301,17 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, + } + parser->m_atts = temp; + #ifdef XML_ATTR_INFO ++ /* Detect and prevent integer overflow. ++ * The preprocessor guard addresses the "always false" warning ++ * from -Wtype-limits on platforms where ++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ ++# if UINT_MAX >= SIZE_MAX ++ if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(XML_AttrInfo)) { ++ parser->m_attsSize = oldAttsSize; ++ return XML_ERROR_NO_MEMORY; ++ } ++# endif ++ + temp2 = (XML_AttrInfo *)REALLOC(parser, (void *)parser->m_attInfo, + parser->m_attsSize * sizeof(XML_AttrInfo)); + if (temp2 == NULL) { +@@ -3610,9 +3646,31 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, + tagNamePtr->prefixLen = prefixLen; + for (i = 0; localPart[i++];) + ; /* i includes null terminator */ ++ ++ /* Detect and prevent integer overflow */ ++ if (binding->uriLen > INT_MAX - prefixLen ++ || i > INT_MAX - (binding->uriLen + prefixLen)) { ++ return XML_ERROR_NO_MEMORY; ++ } ++ + n = i + binding->uriLen + prefixLen; + if (n > binding->uriAlloc) { + TAG *p; ++ ++ /* Detect and prevent integer overflow */ ++ if (n > INT_MAX - EXPAND_SPARE) { ++ return XML_ERROR_NO_MEMORY; ++ } ++ /* Detect and prevent integer overflow. ++ * The preprocessor guard addresses the "always false" warning ++ * from -Wtype-limits on platforms where ++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ ++#if UINT_MAX >= SIZE_MAX ++ if ((unsigned)(n + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) { ++ return XML_ERROR_NO_MEMORY; ++ } ++#endif ++ + uri = (XML_Char *)MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char)); + if (! uri) + return XML_ERROR_NO_MEMORY; +@@ -3708,6 +3766,21 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + if (parser->m_freeBindingList) { + b = parser->m_freeBindingList; + if (len > b->uriAlloc) { ++ /* Detect and prevent integer overflow */ ++ if (len > INT_MAX - EXPAND_SPARE) { ++ return XML_ERROR_NO_MEMORY; ++ } ++ ++ /* Detect and prevent integer overflow. ++ * The preprocessor guard addresses the "always false" warning ++ * from -Wtype-limits on platforms where ++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ ++#if UINT_MAX >= SIZE_MAX ++ if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) { ++ return XML_ERROR_NO_MEMORY; ++ } ++#endif ++ + XML_Char *temp = (XML_Char *)REALLOC( + parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (temp == NULL) +@@ -3720,6 +3793,21 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + b = (BINDING *)MALLOC(parser, sizeof(BINDING)); + if (! b) + return XML_ERROR_NO_MEMORY; ++ ++ /* Detect and prevent integer overflow */ ++ if (len > INT_MAX - EXPAND_SPARE) { ++ return XML_ERROR_NO_MEMORY; ++ } ++ /* Detect and prevent integer overflow. ++ * The preprocessor guard addresses the "always false" warning ++ * from -Wtype-limits on platforms where ++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ ++#if UINT_MAX >= SIZE_MAX ++ if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) { ++ return XML_ERROR_NO_MEMORY; ++ } ++#endif ++ + b->uri + = (XML_Char *)MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (! b->uri) { +@@ -6141,7 +6229,24 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, + } + } else { + DEFAULT_ATTRIBUTE *temp; ++ ++ /* Detect and prevent integer overflow */ ++ if (type->allocDefaultAtts > INT_MAX / 2) { ++ return 0; ++ } ++ + int count = type->allocDefaultAtts * 2; ++ ++ /* Detect and prevent integer overflow. ++ * The preprocessor guard addresses the "always false" warning ++ * from -Wtype-limits on platforms where ++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ ++#if UINT_MAX >= SIZE_MAX ++ if ((unsigned)count > (size_t)(-1) / sizeof(DEFAULT_ATTRIBUTE)) { ++ return 0; ++ } ++#endif ++ + temp = (DEFAULT_ATTRIBUTE *)REALLOC(parser, type->defaultAtts, + (count * sizeof(DEFAULT_ATTRIBUTE))); + if (temp == NULL) +@@ -6792,8 +6897,20 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { + /* check for overflow (table is half full) */ + if (table->used >> (table->power - 1)) { + unsigned char newPower = table->power + 1; ++ ++ /* Detect and prevent invalid shift */ ++ if (newPower >= sizeof(unsigned long) * 8 /* bits per byte */) { ++ return NULL; ++ } ++ + size_t newSize = (size_t)1 << newPower; + unsigned long newMask = (unsigned long)newSize - 1; ++ ++ /* Detect and prevent integer overflow */ ++ if (newSize > (size_t)(-1) / sizeof(NAMED *)) { ++ return NULL; ++ } ++ + size_t tsize = newSize * sizeof(NAMED *); + NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize); + if (! newV) +@@ -7143,6 +7260,20 @@ nextScaffoldPart(XML_Parser parser) { + if (dtd->scaffCount >= dtd->scaffSize) { + CONTENT_SCAFFOLD *temp; + if (dtd->scaffold) { ++ /* Detect and prevent integer overflow */ ++ if (dtd->scaffSize > UINT_MAX / 2u) { ++ return -1; ++ } ++ /* Detect and prevent integer overflow. ++ * The preprocessor guard addresses the "always false" warning ++ * from -Wtype-limits on platforms where ++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ ++#if UINT_MAX >= SIZE_MAX ++ if (dtd->scaffSize > (size_t)(-1) / 2u / sizeof(CONTENT_SCAFFOLD)) { ++ return -1; ++ } ++#endif ++ + temp = (CONTENT_SCAFFOLD *)REALLOC( + parser, dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) +@@ -7212,8 +7343,26 @@ build_model(XML_Parser parser) { + XML_Content *ret; + XML_Content *cpos; + XML_Char *str; +- int allocsize = (dtd->scaffCount * sizeof(XML_Content) +- + (dtd->contentStringLen * sizeof(XML_Char))); ++ ++ /* Detect and prevent integer overflow. ++ * The preprocessor guard addresses the "always false" warning ++ * from -Wtype-limits on platforms where ++ * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ ++#if UINT_MAX >= SIZE_MAX ++ if (dtd->scaffCount > (size_t)(-1) / sizeof(XML_Content)) { ++ return NULL; ++ } ++ if (dtd->contentStringLen > (size_t)(-1) / sizeof(XML_Char)) { ++ return NULL; ++ } ++#endif ++ if (dtd->scaffCount * sizeof(XML_Content) ++ > (size_t)(-1) - dtd->contentStringLen * sizeof(XML_Char)) { ++ return NULL; ++ } ++ ++ const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content) ++ + (dtd->contentStringLen * sizeof(XML_Char))); + + ret = (XML_Content *)MALLOC(parser, allocsize); + if (! ret) diff --git a/meta/recipes-core/expat/expat/CVE-2022-23852.patch b/meta/recipes-core/expat/expat/CVE-2022-23852.patch new file mode 100644 index 0000000000..41425c108b --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2022-23852.patch @@ -0,0 +1,33 @@ +From 847a645152f5ebc10ac63b74b604d0c1a79fae40 Mon Sep 17 00:00:00 2001 +From: Samanta Navarro <ferivoz@riseup.net> +Date: Sat, 22 Jan 2022 17:48:00 +0100 +Subject: [PATCH] lib: Detect and prevent integer overflow in XML_GetBuffer + (CVE-2022-23852) + +Upstream-Status: Backport: +https://github.com/libexpat/libexpat/commit/847a645152f5ebc10ac63b74b604d0c1a79fae40 + +CVE: CVE-2022-23852 + +Signed-off-by: Steve Sakoman <steve@sakoman.com> + +--- + expat/lib/xmlparse.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/expat/lib/xmlparse.c b/expat/lib/xmlparse.c +index d54af683..5ce31402 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -2067,6 +2067,11 @@ XML_GetBuffer(XML_Parser parser, int len) { + keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer); + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; ++ /* Detect and prevent integer overflow */ ++ if (keep > INT_MAX - neededSize) { ++ parser->m_errorCode = XML_ERROR_NO_MEMORY; ++ return NULL; ++ } + neededSize += keep; + #endif /* defined XML_CONTEXT_BYTES */ + if (neededSize diff --git a/meta/recipes-core/expat/expat/CVE-2022-23990.patch b/meta/recipes-core/expat/expat/CVE-2022-23990.patch new file mode 100644 index 0000000000..c599517b3e --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2022-23990.patch @@ -0,0 +1,49 @@ +From ede41d1e186ed2aba88a06e84cac839b770af3a1 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Wed, 26 Jan 2022 02:36:43 +0100 +Subject: [PATCH] lib: Prevent integer overflow in doProlog (CVE-2022-23990) + +The change from "int nameLen" to "size_t nameLen" +addresses the overflow on "nameLen++" in code +"for (; name[nameLen++];)" right above the second +change in the patch. + +Upstream-Status: Backport: +https://github.com/libexpat/libexpat/pull/551/commits/ede41d1e186ed2aba88a06e84cac839b770af3a1 + +CVE: CVE-2022-23990 + +Signed-off-by: Steve Sakoman <steve@sakoman.com> + +--- + lib/xmlparse.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/lib/xmlparse.c b/expat/lib/xmlparse.c +index 5ce31402..d1d17005 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -5372,7 +5372,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + if (dtd->in_eldecl) { + ELEMENT_TYPE *el; + const XML_Char *name; +- int nameLen; ++ size_t nameLen; + const char *nxt + = (quant == XML_CQUANT_NONE ? next : next - enc->minBytesPerChar); + int myindex = nextScaffoldPart(parser); +@@ -5388,7 +5388,13 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + nameLen = 0; + for (; name[nameLen++];) + ; +- dtd->contentStringLen += nameLen; ++ ++ /* Detect and prevent integer overflow */ ++ if (nameLen > UINT_MAX - dtd->contentStringLen) { ++ return XML_ERROR_NO_MEMORY; ++ } ++ ++ dtd->contentStringLen += (unsigned)nameLen; + if (parser->m_elementDeclHandler) + handleDefault = XML_FALSE; + } diff --git a/meta/recipes-core/expat/expat/CVE-2022-25235.patch b/meta/recipes-core/expat/expat/CVE-2022-25235.patch new file mode 100644 index 0000000000..be9182a5c1 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2022-25235.patch @@ -0,0 +1,283 @@ +From ee2a5b50e7d1940ba8745715b62ceb9efd3a96da Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Tue, 8 Feb 2022 17:37:14 +0100 +Subject: [PATCH] lib: Drop unused macro UTF8_GET_NAMING + +Upstream-Status: Backport +https://github.com/libexpat/libexpat/pull/562/commits + +CVE: CVE-2022-25235 + +Signed-off-by: Steve Sakoman <steve@sakoman.com> + +--- + expat/lib/xmltok.c | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/lib/xmltok.c b/lib/xmltok.c +index a72200e8..3bddf125 100644 +--- a/lib/xmltok.c ++++ b/lib/xmltok.c +@@ -95,11 +95,6 @@ + + ((((byte)[1]) & 3) << 1) + ((((byte)[2]) >> 5) & 1)] \ + & (1u << (((byte)[2]) & 0x1F))) + +-#define UTF8_GET_NAMING(pages, p, n) \ +- ((n) == 2 \ +- ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \ +- : ((n) == 3 ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) : 0)) +- + /* Detection of invalid UTF-8 sequences is based on Table 3.1B + of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/ + with the additional restriction of not allowing the Unicode +From 3f0a0cb644438d4d8e3294cd0b1245d0edb0c6c6 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Tue, 8 Feb 2022 04:32:20 +0100 +Subject: [PATCH] lib: Add missing validation of encoding (CVE-2022-25235) + +--- + expat/lib/xmltok_impl.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/lib/xmltok_impl.c b/lib/xmltok_impl.c +index 0430591b4..64a3b2c15 100644 +--- a/lib/xmltok_impl.c ++++ b/lib/xmltok_impl.c +@@ -61,7 +61,7 @@ + case BT_LEAD##n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ +- if (! IS_NAME_CHAR(enc, ptr, n)) { \ ++ if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NAME_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ +@@ -90,7 +90,7 @@ + case BT_LEAD##n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ +- if (! IS_NMSTRT_CHAR(enc, ptr, n)) { \ ++ if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NMSTRT_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ +@@ -1134,6 +1134,10 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, + case BT_LEAD##n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ ++ if (IS_INVALID_CHAR(enc, ptr, n)) { \ ++ *nextTokPtr = ptr; \ ++ return XML_TOK_INVALID; \ ++ } \ + if (IS_NMSTRT_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NAME; \ +From c85a3025e7a1be086dc34e7559fbc543914d047f Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Wed, 9 Feb 2022 01:00:38 +0100 +Subject: [PATCH] lib: Add comments to BT_LEAD* cases where encoding has + already been validated + +--- + expat/lib/xmltok_impl.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/lib/xmltok_impl.c b/lib/xmltok_impl.c +index 64a3b2c1..84ff35f9 100644 +--- a/lib/xmltok_impl.c ++++ b/lib/xmltok_impl.c +@@ -1266,7 +1266,7 @@ PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, const char *end, + switch (BYTE_TYPE(enc, ptr)) { + # define LEAD_CASE(n) \ + case BT_LEAD##n: \ +- ptr += n; \ ++ ptr += n; /* NOTE: The encoding has already been validated. */ \ + break; + LEAD_CASE(2) + LEAD_CASE(3) +@@ -1335,7 +1335,7 @@ PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, const char *end, + switch (BYTE_TYPE(enc, ptr)) { + # define LEAD_CASE(n) \ + case BT_LEAD##n: \ +- ptr += n; \ ++ ptr += n; /* NOTE: The encoding has already been validated. */ \ + break; + LEAD_CASE(2) + LEAD_CASE(3) +@@ -1514,7 +1514,7 @@ PREFIX(getAtts)(const ENCODING *enc, const char *ptr, int attsMax, + state = inName; \ + } + # define LEAD_CASE(n) \ +- case BT_LEAD##n: \ ++ case BT_LEAD##n: /* NOTE: The encoding has already been validated. */ \ + START_NAME ptr += (n - MINBPC(enc)); \ + break; + LEAD_CASE(2) +@@ -1726,7 +1726,7 @@ PREFIX(nameLength)(const ENCODING *enc, const char *ptr) { + switch (BYTE_TYPE(enc, ptr)) { + # define LEAD_CASE(n) \ + case BT_LEAD##n: \ +- ptr += n; \ ++ ptr += n; /* NOTE: The encoding has already been validated. */ \ + break; + LEAD_CASE(2) + LEAD_CASE(3) +@@ -1771,7 +1771,7 @@ PREFIX(updatePosition)(const ENCODING *enc, const char *ptr, const char *end, + switch (BYTE_TYPE(enc, ptr)) { + # define LEAD_CASE(n) \ + case BT_LEAD##n: \ +- ptr += n; \ ++ ptr += n; /* NOTE: The encoding has already been validated. */ \ + break; + LEAD_CASE(2) + LEAD_CASE(3) +From 6a5510bc6b7efe743356296724e0b38300f05379 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Tue, 8 Feb 2022 04:06:21 +0100 +Subject: [PATCH] tests: Cover missing validation of encoding (CVE-2022-25235) + +--- + expat/tests/runtests.c | 109 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 109 insertions(+) + +diff --git a/tests/runtests.c b/tests/runtests.c +index bc5344b1..9b155b82 100644 +--- a/tests/runtests.c ++++ b/tests/runtests.c +@@ -5998,6 +5998,105 @@ START_TEST(test_utf8_in_cdata_section_2) { + } + END_TEST + ++START_TEST(test_utf8_in_start_tags) { ++ struct test_case { ++ bool goodName; ++ bool goodNameStart; ++ const char *tagName; ++ }; ++ ++ // The idea with the tests below is this: ++ // We want to cover 1-, 2- and 3-byte sequences, 4-byte sequences ++ // go to isNever and are hence not a concern. ++ // ++ // We start with a character that is a valid name character ++ // (or even name-start character, see XML 1.0r4 spec) and then we flip ++ // single bits at places where (1) the result leaves the UTF-8 encoding space ++ // and (2) we stay in the same n-byte sequence family. ++ // ++ // The flipped bits are highlighted in angle brackets in comments, ++ // e.g. "[<1>011 1001]" means we had [0011 1001] but we now flipped ++ // the most significant bit to 1 to leave UTF-8 encoding space. ++ struct test_case cases[] = { ++ // 1-byte UTF-8: [0xxx xxxx] ++ {true, true, "\x3A"}, // [0011 1010] = ASCII colon ':' ++ {false, false, "\xBA"}, // [<1>011 1010] ++ {true, false, "\x39"}, // [0011 1001] = ASCII nine '9' ++ {false, false, "\xB9"}, // [<1>011 1001] ++ ++ // 2-byte UTF-8: [110x xxxx] [10xx xxxx] ++ {true, true, "\xDB\xA5"}, // [1101 1011] [1010 0101] = ++ // Arabic small waw U+06E5 ++ {false, false, "\x9B\xA5"}, // [1<0>01 1011] [1010 0101] ++ {false, false, "\xDB\x25"}, // [1101 1011] [<0>010 0101] ++ {false, false, "\xDB\xE5"}, // [1101 1011] [1<1>10 0101] ++ {true, false, "\xCC\x81"}, // [1100 1100] [1000 0001] = ++ // combining char U+0301 ++ {false, false, "\x8C\x81"}, // [1<0>00 1100] [1000 0001] ++ {false, false, "\xCC\x01"}, // [1100 1100] [<0>000 0001] ++ {false, false, "\xCC\xC1"}, // [1100 1100] [1<1>00 0001] ++ ++ // 3-byte UTF-8: [1110 xxxx] [10xx xxxx] [10xxxxxx] ++ {true, true, "\xE0\xA4\x85"}, // [1110 0000] [1010 0100] [1000 0101] = ++ // Devanagari Letter A U+0905 ++ {false, false, "\xA0\xA4\x85"}, // [1<0>10 0000] [1010 0100] [1000 0101] ++ {false, false, "\xE0\x24\x85"}, // [1110 0000] [<0>010 0100] [1000 0101] ++ {false, false, "\xE0\xE4\x85"}, // [1110 0000] [1<1>10 0100] [1000 0101] ++ {false, false, "\xE0\xA4\x05"}, // [1110 0000] [1010 0100] [<0>000 0101] ++ {false, false, "\xE0\xA4\xC5"}, // [1110 0000] [1010 0100] [1<1>00 0101] ++ {true, false, "\xE0\xA4\x81"}, // [1110 0000] [1010 0100] [1000 0001] = ++ // combining char U+0901 ++ {false, false, "\xA0\xA4\x81"}, // [1<0>10 0000] [1010 0100] [1000 0001] ++ {false, false, "\xE0\x24\x81"}, // [1110 0000] [<0>010 0100] [1000 0001] ++ {false, false, "\xE0\xE4\x81"}, // [1110 0000] [1<1>10 0100] [1000 0001] ++ {false, false, "\xE0\xA4\x01"}, // [1110 0000] [1010 0100] [<0>000 0001] ++ {false, false, "\xE0\xA4\xC1"}, // [1110 0000] [1010 0100] [1<1>00 0001] ++ }; ++ const bool atNameStart[] = {true, false}; ++ ++ size_t i = 0; ++ char doc[1024]; ++ size_t failCount = 0; ++ ++ for (; i < sizeof(cases) / sizeof(cases[0]); i++) { ++ size_t j = 0; ++ for (; j < sizeof(atNameStart) / sizeof(atNameStart[0]); j++) { ++ const bool expectedSuccess ++ = atNameStart[j] ? cases[i].goodNameStart : cases[i].goodName; ++ sprintf(doc, "<%s%s><!--", atNameStart[j] ? "" : "a", cases[i].tagName); ++ XML_Parser parser = XML_ParserCreate(NULL); ++ ++ const enum XML_Status status ++ = XML_Parse(parser, doc, (int)strlen(doc), /*isFinal=*/XML_FALSE); ++ ++ bool success = true; ++ if ((status == XML_STATUS_OK) != expectedSuccess) { ++ success = false; ++ } ++ if ((status == XML_STATUS_ERROR) ++ && (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)) { ++ success = false; ++ } ++ ++ if (! success) { ++ fprintf( ++ stderr, ++ "FAIL case %2u (%sat name start, %u-byte sequence, error code %d)\n", ++ (unsigned)i + 1u, atNameStart[j] ? " " : "not ", ++ (unsigned)strlen(cases[i].tagName), XML_GetErrorCode(parser)); ++ failCount++; ++ } ++ ++ XML_ParserFree(parser); ++ } ++ } ++ ++ if (failCount > 0) { ++ fail("UTF-8 regression detected"); ++ } ++} ++END_TEST ++ + /* Test trailing spaces in elements are accepted */ + static void XMLCALL + record_element_end_handler(void *userData, const XML_Char *name) { +@@ -6175,6 +6274,14 @@ START_TEST(test_bad_doctype) { + } + END_TEST + ++START_TEST(test_bad_doctype_utf8) { ++ const char *text = "<!DOCTYPE \xDB\x25" ++ "doc><doc/>"; // [1101 1011] [<0>010 0101] ++ expect_failure(text, XML_ERROR_INVALID_TOKEN, ++ "Invalid UTF-8 in DOCTYPE not faulted"); ++} ++END_TEST ++ + START_TEST(test_bad_doctype_utf16) { + const char text[] = + /* <!DOCTYPE doc [ \x06f2 ]><doc/> +@@ -11870,6 +11977,7 @@ make_suite(void) { + tcase_add_test(tc_basic, test_ext_entity_utf8_non_bom); + tcase_add_test(tc_basic, test_utf8_in_cdata_section); + tcase_add_test(tc_basic, test_utf8_in_cdata_section_2); ++ tcase_add_test(tc_basic, test_utf8_in_start_tags); + tcase_add_test(tc_basic, test_trailing_spaces_in_elements); + tcase_add_test(tc_basic, test_utf16_attribute); + tcase_add_test(tc_basic, test_utf16_second_attr); +@@ -11878,6 +11986,7 @@ make_suite(void) { + tcase_add_test(tc_basic, test_bad_attr_desc_keyword); + tcase_add_test(tc_basic, test_bad_attr_desc_keyword_utf16); + tcase_add_test(tc_basic, test_bad_doctype); ++ tcase_add_test(tc_basic, test_bad_doctype_utf8); + tcase_add_test(tc_basic, test_bad_doctype_utf16); + tcase_add_test(tc_basic, test_bad_doctype_plus); + tcase_add_test(tc_basic, test_bad_doctype_star); diff --git a/meta/recipes-core/expat/expat/CVE-2022-25236.patch b/meta/recipes-core/expat/expat/CVE-2022-25236.patch new file mode 100644 index 0000000000..ba6443fc6a --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2022-25236.patch @@ -0,0 +1,129 @@ +From 6881a4fc8596307ab9ff2e85e605afa2e413ab71 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Sat, 12 Feb 2022 00:19:13 +0100 +Subject: [PATCH] lib: Fix (harmless) use of uninitialized memory + +Upstream-Status: Backport +https://github.com/libexpat/libexpat/pull/561/commits + +CVE: CVE-2022-25236 + +Signed-off-by: Steve Sakoman <steve@sakoman.com> + +--- + expat/lib/xmlparse.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 902895d5..c768f856 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -718,8 +718,7 @@ XML_ParserCreate(const XML_Char *encodingName) { + + XML_Parser XMLCALL + XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) { +- XML_Char tmp[2]; +- *tmp = nsSep; ++ XML_Char tmp[2] = {nsSep, 0}; + return XML_ParserCreate_MM(encodingName, NULL, tmp); + } + +@@ -1344,8 +1343,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, + would be otherwise. + */ + if (parser->m_ns) { +- XML_Char tmp[2]; +- *tmp = parser->m_namespaceSeparator; ++ XML_Char tmp[2] = {parser->m_namespaceSeparator, 0}; + parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); + } else { + parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); +From a2fe525e660badd64b6c557c2b1ec26ddc07f6e4 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Sat, 12 Feb 2022 01:09:29 +0100 +Subject: [PATCH] lib: Protect against malicious namespace declarations + (CVE-2022-25236) + +--- + expat/lib/xmlparse.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index c768f856..a3aef88c 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -3754,6 +3754,17 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + if (! mustBeXML && isXMLNS + && (len > xmlnsLen || uri[len] != xmlnsNamespace[len])) + isXMLNS = XML_FALSE; ++ ++ // NOTE: While Expat does not validate namespace URIs against RFC 3986, ++ // we have to at least make sure that the XML processor on top of ++ // Expat (that is splitting tag names by namespace separator into ++ // 2- or 3-tuples (uri-local or uri-local-prefix)) cannot be confused ++ // by an attacker putting additional namespace separator characters ++ // into namespace declarations. That would be ambiguous and not to ++ // be expected. ++ if (parser->m_ns && (uri[len] == parser->m_namespaceSeparator)) { ++ return XML_ERROR_SYNTAX; ++ } + } + isXML = isXML && len == xmlLen; + isXMLNS = isXMLNS && len == xmlnsLen; +From 2de077423fb22750ebea599677d523b53cb93b1d Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Sat, 12 Feb 2022 00:51:43 +0100 +Subject: [PATCH] tests: Cover CVE-2022-25236 + +--- + expat/tests/runtests.c | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +diff --git a/tests/runtests.c b/tests/runtests.c +index d07203f2..bc5344b1 100644 +--- a/tests/runtests.c ++++ b/tests/runtests.c +@@ -7220,6 +7220,35 @@ START_TEST(test_ns_double_colon_doctype) { + } + END_TEST + ++START_TEST(test_ns_separator_in_uri) { ++ struct test_case { ++ enum XML_Status expectedStatus; ++ const char *doc; ++ }; ++ struct test_case cases[] = { ++ {XML_STATUS_OK, "<doc xmlns='one_two' />"}, ++ {XML_STATUS_ERROR, "<doc xmlns='one
two' />"}, ++ }; ++ ++ size_t i = 0; ++ size_t failCount = 0; ++ for (; i < sizeof(cases) / sizeof(cases[0]); i++) { ++ XML_Parser parser = XML_ParserCreateNS(NULL, '\n'); ++ XML_SetElementHandler(parser, dummy_start_element, dummy_end_element); ++ if (XML_Parse(parser, cases[i].doc, (int)strlen(cases[i].doc), ++ /*isFinal*/ XML_TRUE) ++ != cases[i].expectedStatus) { ++ failCount++; ++ } ++ XML_ParserFree(parser); ++ } ++ ++ if (failCount) { ++ fail("Namespace separator handling is broken"); ++ } ++} ++END_TEST ++ + /* Control variable; the number of times duff_allocator() will successfully + * allocate */ + #define ALLOC_ALWAYS_SUCCEED (-1) +@@ -11905,6 +11934,7 @@ make_suite(void) { + tcase_add_test(tc_namespace, test_ns_utf16_doctype); + tcase_add_test(tc_namespace, test_ns_invalid_doctype); + tcase_add_test(tc_namespace, test_ns_double_colon_doctype); ++ tcase_add_test(tc_namespace, test_ns_separator_in_uri); + + suite_add_tcase(s, tc_misc); + tcase_add_checked_fixture(tc_misc, NULL, basic_teardown); diff --git a/meta/recipes-core/expat/expat/CVE-2022-25313-regression.patch b/meta/recipes-core/expat/expat/CVE-2022-25313-regression.patch new file mode 100644 index 0000000000..af255e8cb5 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2022-25313-regression.patch @@ -0,0 +1,131 @@ +From b12f34fe32821a69dc12ff9a021daca0856de238 Mon Sep 17 00:00:00 2001 +From: Samanta Navarro <ferivoz@riseup.net> +Date: Sat, 19 Feb 2022 23:59:25 +0000 +Subject: [PATCH] Fix build_model regression. + +The iterative approach in build_model failed to fill children arrays +correctly. A preorder traversal is not required and turned out to be the +culprit. Use an easier algorithm: + +Add nodes from scaffold tree starting at index 0 (root) to the target +array whenever children are encountered. This ensures that children +are adjacent to each other. This complies with the recursive version. + +Store only the scaffold index in numchildren field to prevent a direct +processing of these children, which would require a recursive solution. +This allows the algorithm to iterate through the target array from start +to end without jumping back and forth, converting on the fly. + +Co-authored-by: Sebastian Pipping <sebastian@pipping.org> +--- + lib/xmlparse.c | 79 ++++++++++++++++++++++++++------------------ + 1 file changed, 47 insertions(+), 32 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index c479a258..84885b5a 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -7373,39 +7373,58 @@ build_model(XML_Parser parser) { + * + * The iterative approach works as follows: + * +- * - We use space in the target array for building a temporary stack structure +- * while that space is still unused. +- * The stack grows from the array's end downwards and the "actual data" +- * grows from the start upwards, sequentially. +- * (Because stack grows downwards, pushing onto the stack is a decrement +- * while popping off the stack is an increment.) ++ * - We have two writing pointers, both walking up the result array; one does ++ * the work, the other creates "jobs" for its colleague to do, and leads ++ * the way: + * +- * - A stack element appears as a regular XML_Content node on the outside, +- * but only uses a single field -- numchildren -- to store the source +- * tree node array index. These are the breadcrumbs leading the way back +- * during pre-order (node first) depth-first traversal. ++ * - The faster one, pointer jobDest, always leads and writes "what job ++ * to do" by the other, once they reach that place in the ++ * array: leader "jobDest" stores the source node array index (relative ++ * to array dtd->scaffold) in field "numchildren". + * +- * - The reason we know the stack will never grow into (or overlap with) +- * the area with data of value at the start of the array is because +- * the overall number of elements to process matches the size of the array, +- * and the sum of fully processed nodes and yet-to-be processed nodes +- * on the stack, cannot be more than the total number of nodes. +- * It is possible for the top of the stack and the about-to-write node +- * to meet, but that is safe because we get the source index out +- * before doing any writes on that node. ++ * - The slower one, pointer dest, looks at the value stored in the ++ * "numchildren" field (which actually holds a source node array index ++ * at that time) and puts the real data from dtd->scaffold in. ++ * ++ * - Before the loop starts, jobDest writes source array index 0 ++ * (where the root node is located) so that dest will have something to do ++ * when it starts operation. ++ * ++ * - Whenever nodes with children are encountered, jobDest appends ++ * them as new jobs, in order. As a result, tree node siblings are ++ * adjacent in the resulting array, for example: ++ * ++ * [0] root, has two children ++ * [1] first child of 0, has three children ++ * [3] first child of 1, does not have children ++ * [4] second child of 1, does not have children ++ * [5] third child of 1, does not have children ++ * [2] second child of 0, does not have children ++ * ++ * Or (the same data) presented in flat array view: ++ * ++ * [0] root, has two children ++ * ++ * [1] first child of 0, has three children ++ * [2] second child of 0, does not have children ++ * ++ * [3] first child of 1, does not have children ++ * [4] second child of 1, does not have children ++ * [5] third child of 1, does not have children ++ * ++ * - The algorithm repeats until all target array indices have been processed. + */ + XML_Content *dest = ret; /* tree node writing location, moves upwards */ + XML_Content *const destLimit = &ret[dtd->scaffCount]; +- XML_Content *const stackBottom = &ret[dtd->scaffCount]; +- XML_Content *stackTop = stackBottom; /* i.e. stack is initially empty */ ++ XML_Content *jobDest = ret; /* next free writing location in target array */ + str = (XML_Char *)&ret[dtd->scaffCount]; + +- /* Push source tree root node index onto the stack */ +- (--stackTop)->numchildren = 0; ++ /* Add the starting job, the root node (index 0) of the source tree */ ++ (jobDest++)->numchildren = 0; + + for (; dest < destLimit; dest++) { +- /* Pop source tree node index off the stack */ +- const int src_node = (int)(stackTop++)->numchildren; ++ /* Retrieve source tree array index from job storage */ ++ const int src_node = (int)dest->numchildren; + + /* Convert item */ + dest->type = dtd->scaffold[src_node].type; +@@ -7427,16 +7446,12 @@ build_model(XML_Parser parser) { + int cn; + dest->name = NULL; + dest->numchildren = dtd->scaffold[src_node].childcnt; +- dest->children = &dest[1]; ++ dest->children = jobDest; + +- /* Push children to the stack +- * in a way where the first child ends up at the top of the +- * (downwards growing) stack, in order to be processed first. */ +- stackTop -= dest->numchildren; ++ /* Append scaffold indices of children to array */ + for (i = 0, cn = dtd->scaffold[src_node].firstchild; +- i < dest->numchildren; i++, cn = dtd->scaffold[cn].nextsib) { +- (stackTop + i)->numchildren = (unsigned int)cn; +- } ++ i < dest->numchildren; i++, cn = dtd->scaffold[cn].nextsib) ++ (jobDest++)->numchildren = (unsigned int)cn; + } + } + diff --git a/meta/recipes-core/expat/expat/CVE-2022-25313.patch b/meta/recipes-core/expat/expat/CVE-2022-25313.patch new file mode 100644 index 0000000000..470d66e9dd --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2022-25313.patch @@ -0,0 +1,230 @@ +From 9b4ce651b26557f16103c3a366c91934ecd439ab Mon Sep 17 00:00:00 2001 +From: Samanta Navarro <ferivoz@riseup.net> +Date: Tue, 15 Feb 2022 11:54:29 +0000 +Subject: [PATCH] Prevent stack exhaustion in build_model + +It is possible to trigger stack exhaustion in build_model function if +depth of nested children in DTD element is large enough. This happens +because build_node is a recursively called function within build_model. + +The code has been adjusted to run iteratively. It uses the already +allocated heap space as temporary stack (growing from top to bottom). + +Output is identical to recursive version. No new fields in data +structures were added, i.e. it keeps full API and ABI compatibility. +Instead the numchildren variable is used to temporarily keep the +index of items (uint vs int). + +Documentation and readability improvements kindly added by Sebastian. + +Proof of Concept: + +1. Compile poc binary which parses XML file line by line + +``` +cat > poc.c << EOF + #include <err.h> + #include <expat.h> + #include <stdio.h> + + XML_Parser parser; + + static void XMLCALL + dummy_element_decl_handler(void *userData, const XML_Char *name, + XML_Content *model) { + XML_FreeContentModel(parser, model); + } + + int main(int argc, char *argv[]) { + FILE *fp; + char *p = NULL; + size_t s = 0; + ssize_t l; + if (argc != 2) + errx(1, "usage: poc poc.xml"); + if ((parser = XML_ParserCreate(NULL)) == NULL) + errx(1, "XML_ParserCreate"); + XML_SetElementDeclHandler(parser, dummy_element_decl_handler); + if ((fp = fopen(argv[1], "r")) == NULL) + err(1, "fopen"); + while ((l = getline(&p, &s, fp)) > 0) + if (XML_Parse(parser, p, (int)l, XML_FALSE) != XML_STATUS_OK) + errx(1, "XML_Parse"); + XML_ParserFree(parser); + free(p); + fclose(fp); + return 0; + } +EOF +cc -std=c11 -D_POSIX_C_SOURCE=200809L -lexpat -o poc poc.c +``` + +2. Create XML file with a lot of nested groups in DTD element + +``` +cat > poc.xml.zst.b64 << EOF +KLUv/aQkACAAPAEA+DwhRE9DVFlQRSB1d3UgWwo8IUVMRU1FTlQgdXd1CigBAHv/58AJAgAQKAIA +ECgCABAoAgAQKAIAECgCABAoAgAQKHwAAChvd28KKQIA2/8gV24XBAIAECkCABApAgAQKQIAECkC +ABApAgAQKQIAEClVAAAgPl0+CgEA4A4I2VwwnQ== +EOF +base64 -d poc.xml.zst.b64 | zstd -d > poc.xml +``` + +3. Run Proof of Concept + +``` +./poc poc.xml +``` + +Co-authored-by: Sebastian Pipping <sebastian@pipping.org> + +Upstream-Status: Backport +https://github.com/libexpat/libexpat/pull/558/commits/9b4ce651b26557f16103c3a366c91934ecd439ab + +CVE: CVE-2022-25313 + +Signed-off-by: Steve Sakoman <steve@sakoman.com> + +--- + expat/lib/xmlparse.c | 116 +++++++++++++++++++++++++++++-------------- + 1 file changed, 79 insertions(+), 37 deletions(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 4b43e613..594cf12c 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -7317,44 +7317,15 @@ nextScaffoldPart(XML_Parser parser) { + return next; + } + +-static void +-build_node(XML_Parser parser, int src_node, XML_Content *dest, +- XML_Content **contpos, XML_Char **strpos) { +- DTD *const dtd = parser->m_dtd; /* save one level of indirection */ +- dest->type = dtd->scaffold[src_node].type; +- dest->quant = dtd->scaffold[src_node].quant; +- if (dest->type == XML_CTYPE_NAME) { +- const XML_Char *src; +- dest->name = *strpos; +- src = dtd->scaffold[src_node].name; +- for (;;) { +- *(*strpos)++ = *src; +- if (! *src) +- break; +- src++; +- } +- dest->numchildren = 0; +- dest->children = NULL; +- } else { +- unsigned int i; +- int cn; +- dest->numchildren = dtd->scaffold[src_node].childcnt; +- dest->children = *contpos; +- *contpos += dest->numchildren; +- for (i = 0, cn = dtd->scaffold[src_node].firstchild; i < dest->numchildren; +- i++, cn = dtd->scaffold[cn].nextsib) { +- build_node(parser, cn, &(dest->children[i]), contpos, strpos); +- } +- dest->name = NULL; +- } +-} +- + static XML_Content * + build_model(XML_Parser parser) { ++ /* Function build_model transforms the existing parser->m_dtd->scaffold ++ * array of CONTENT_SCAFFOLD tree nodes into a new array of ++ * XML_Content tree nodes followed by a gapless list of zero-terminated ++ * strings. */ + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ + XML_Content *ret; +- XML_Content *cpos; +- XML_Char *str; ++ XML_Char *str; /* the current string writing location */ + + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning +@@ -7380,10 +7351,81 @@ build_model(XML_Parser parser) { + if (! ret) + return NULL; + +- str = (XML_Char *)(&ret[dtd->scaffCount]); +- cpos = &ret[1]; ++ /* What follows is an iterative implementation (of what was previously done ++ * recursively in a dedicated function called "build_node". The old recursive ++ * build_node could be forced into stack exhaustion from input as small as a ++ * few megabyte, and so that was a security issue. Hence, a function call ++ * stack is avoided now by resolving recursion.) ++ * ++ * The iterative approach works as follows: ++ * ++ * - We use space in the target array for building a temporary stack structure ++ * while that space is still unused. ++ * The stack grows from the array's end downwards and the "actual data" ++ * grows from the start upwards, sequentially. ++ * (Because stack grows downwards, pushing onto the stack is a decrement ++ * while popping off the stack is an increment.) ++ * ++ * - A stack element appears as a regular XML_Content node on the outside, ++ * but only uses a single field -- numchildren -- to store the source ++ * tree node array index. These are the breadcrumbs leading the way back ++ * during pre-order (node first) depth-first traversal. ++ * ++ * - The reason we know the stack will never grow into (or overlap with) ++ * the area with data of value at the start of the array is because ++ * the overall number of elements to process matches the size of the array, ++ * and the sum of fully processed nodes and yet-to-be processed nodes ++ * on the stack, cannot be more than the total number of nodes. ++ * It is possible for the top of the stack and the about-to-write node ++ * to meet, but that is safe because we get the source index out ++ * before doing any writes on that node. ++ */ ++ XML_Content *dest = ret; /* tree node writing location, moves upwards */ ++ XML_Content *const destLimit = &ret[dtd->scaffCount]; ++ XML_Content *const stackBottom = &ret[dtd->scaffCount]; ++ XML_Content *stackTop = stackBottom; /* i.e. stack is initially empty */ ++ str = (XML_Char *)&ret[dtd->scaffCount]; ++ ++ /* Push source tree root node index onto the stack */ ++ (--stackTop)->numchildren = 0; ++ ++ for (; dest < destLimit; dest++) { ++ /* Pop source tree node index off the stack */ ++ const int src_node = (int)(stackTop++)->numchildren; ++ ++ /* Convert item */ ++ dest->type = dtd->scaffold[src_node].type; ++ dest->quant = dtd->scaffold[src_node].quant; ++ if (dest->type == XML_CTYPE_NAME) { ++ const XML_Char *src; ++ dest->name = str; ++ src = dtd->scaffold[src_node].name; ++ for (;;) { ++ *str++ = *src; ++ if (! *src) ++ break; ++ src++; ++ } ++ dest->numchildren = 0; ++ dest->children = NULL; ++ } else { ++ unsigned int i; ++ int cn; ++ dest->name = NULL; ++ dest->numchildren = dtd->scaffold[src_node].childcnt; ++ dest->children = &dest[1]; ++ ++ /* Push children to the stack ++ * in a way where the first child ends up at the top of the ++ * (downwards growing) stack, in order to be processed first. */ ++ stackTop -= dest->numchildren; ++ for (i = 0, cn = dtd->scaffold[src_node].firstchild; ++ i < dest->numchildren; i++, cn = dtd->scaffold[cn].nextsib) { ++ (stackTop + i)->numchildren = (unsigned int)cn; ++ } ++ } ++ } + +- build_node(parser, 0, ret, &cpos, &str); + return ret; + } + diff --git a/meta/recipes-core/expat/expat/CVE-2022-25314.patch b/meta/recipes-core/expat/expat/CVE-2022-25314.patch new file mode 100644 index 0000000000..2f713ebb54 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2022-25314.patch @@ -0,0 +1,32 @@ +From efcb347440ade24b9f1054671e6bd05e60b4cafd Mon Sep 17 00:00:00 2001 +From: Samanta Navarro <ferivoz@riseup.net> +Date: Tue, 15 Feb 2022 11:56:57 +0000 +Subject: [PATCH] Prevent integer overflow in copyString + +The copyString function is only used for encoding string supplied by +the library user. + +Upstream-Status: Backport +https://github.com/libexpat/libexpat/pull/560/commits/efcb347440ade24b9f1054671e6bd05e60b4cafd + +CVE: CVE-2022-25314 + +Signed-off-by: Steve Sakoman <steve@sakoman.com> + +--- + expat/lib/xmlparse.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 4b43e613..a39377c2 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -7412,7 +7412,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) { +- int charsRequired = 0; ++ size_t charsRequired = 0; + XML_Char *result; + + /* First determine how long the string is */ diff --git a/meta/recipes-core/expat/expat/CVE-2022-25315.patch b/meta/recipes-core/expat/expat/CVE-2022-25315.patch new file mode 100644 index 0000000000..a39771d28a --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2022-25315.patch @@ -0,0 +1,145 @@ +From eb0362808b4f9f1e2345a0cf203b8cc196d776d9 Mon Sep 17 00:00:00 2001 +From: Samanta Navarro <ferivoz@riseup.net> +Date: Tue, 15 Feb 2022 11:55:46 +0000 +Subject: [PATCH] Prevent integer overflow in storeRawNames + +It is possible to use an integer overflow in storeRawNames for out of +boundary heap writes. Default configuration is affected. If compiled +with XML_UNICODE then the attack does not work. Compiling with +-fsanitize=address confirms the following proof of concept. + +The problem can be exploited by abusing the m_buffer expansion logic. +Even though the initial size of m_buffer is a power of two, eventually +it can end up a little bit lower, thus allowing allocations very close +to INT_MAX (since INT_MAX/2 can be surpassed). This means that tag +names can be parsed which are almost INT_MAX in size. + +Unfortunately (from an attacker point of view) INT_MAX/2 is also a +limitation in string pools. Having a tag name of INT_MAX/2 characters +or more is not possible. + +Expat can convert between different encodings. UTF-16 documents which +contain only ASCII representable characters are twice as large as their +ASCII encoded counter-parts. + +The proof of concept works by taking these three considerations into +account: + +1. Move the m_buffer size slightly below a power of two by having a + short root node <a>. This allows the m_buffer to grow very close + to INT_MAX. +2. The string pooling forbids tag names longer than or equal to + INT_MAX/2, so keep the attack tag name smaller than that. +3. To be able to still overflow INT_MAX even though the name is + limited at INT_MAX/2-1 (nul byte) we use UTF-16 encoding and a tag + which only contains ASCII characters. UTF-16 always stores two + bytes per character while the tag name is converted to using only + one. Our attack node byte count must be a bit higher than + 2/3 INT_MAX so the converted tag name is around INT_MAX/3 which + in sum can overflow INT_MAX. + +Thanks to our small root node, m_buffer can handle 2/3 INT_MAX bytes +without running into INT_MAX boundary check. The string pooling is +able to store INT_MAX/3 as tag name because the amount is below +INT_MAX/2 limitation. And creating the sum of both eventually overflows +in storeRawNames. + +Proof of Concept: + +1. Compile expat with -fsanitize=address. + +2. Create Proof of Concept binary which iterates through input + file 16 MB at once for better performance and easier integer + calculations: + +``` +cat > poc.c << EOF + #include <err.h> + #include <expat.h> + #include <stdlib.h> + #include <stdio.h> + + #define CHUNK (16 * 1024 * 1024) + int main(int argc, char *argv[]) { + XML_Parser parser; + FILE *fp; + char *buf; + int i; + + if (argc != 2) + errx(1, "usage: poc file.xml"); + if ((parser = XML_ParserCreate(NULL)) == NULL) + errx(1, "failed to create expat parser"); + if ((fp = fopen(argv[1], "r")) == NULL) { + XML_ParserFree(parser); + err(1, "failed to open file"); + } + if ((buf = malloc(CHUNK)) == NULL) { + fclose(fp); + XML_ParserFree(parser); + err(1, "failed to allocate buffer"); + } + i = 0; + while (fread(buf, CHUNK, 1, fp) == 1) { + printf("iteration %d: XML_Parse returns %d\n", ++i, + XML_Parse(parser, buf, CHUNK, XML_FALSE)); + } + free(buf); + fclose(fp); + XML_ParserFree(parser); + return 0; + } +EOF +gcc -fsanitize=address -lexpat -o poc poc.c +``` + +3. Construct specially prepared UTF-16 XML file: + +``` +dd if=/dev/zero bs=1024 count=794624 | tr '\0' 'a' > poc-utf8.xml +echo -n '<a><' | dd conv=notrunc of=poc-utf8.xml +echo -n '><' | dd conv=notrunc of=poc-utf8.xml bs=1 seek=805306368 +iconv -f UTF-8 -t UTF-16LE poc-utf8.xml > poc-utf16.xml +``` + +4. Run proof of concept: + +``` +./poc poc-utf16.xml +``` + +Upstream-Status: Backport +https://github.com/libexpat/libexpat/pull/559/commits/eb0362808b4f9f1e2345a0cf203b8cc196d776d9 + +CVE: CVE-2022-25315 + +Signed-off-by: Steve Sakoman <steve@sakoman.com> +--- + lib/xmlparse.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index 4b43e613..f34d6ab5 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -2563,6 +2563,7 @@ storeRawNames(XML_Parser parser) { + while (tag) { + int bufSize; + int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); ++ size_t rawNameLen; + char *rawNameBuf = tag->buf + nameLen; + /* Stop if already stored. Since m_tagStack is a stack, we can stop + at the first entry that has already been copied; everything +@@ -2574,7 +2575,11 @@ storeRawNames(XML_Parser parser) { + /* For re-use purposes we need to ensure that the + size of tag->buf is a multiple of sizeof(XML_Char). + */ +- bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)); ++ rawNameLen = ROUND_UP(tag->rawNameLength, sizeof(XML_Char)); ++ /* Detect and prevent integer overflow. */ ++ if (rawNameLen > (size_t)INT_MAX - nameLen) ++ return XML_FALSE; ++ bufSize = nameLen + (int)rawNameLen; + if (bufSize > tag->bufEnd - tag->buf) { + char *temp = (char *)REALLOC(parser, tag->buf, bufSize); + if (temp == NULL) diff --git a/meta/recipes-core/expat/expat/CVE-2022-40674.patch b/meta/recipes-core/expat/expat/CVE-2022-40674.patch new file mode 100644 index 0000000000..8b95f5f198 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2022-40674.patch @@ -0,0 +1,53 @@ +From 4a32da87e931ba54393d465bb77c40b5c33d343b Mon Sep 17 00:00:00 2001 +From: Rhodri James <rhodri@wildebeest.org.uk> +Date: Wed, 17 Aug 2022 18:26:18 +0100 +Subject: [PATCH] Ensure raw tagnames are safe exiting internalEntityParser + +It is possible to concoct a situation in which parsing is +suspended while substituting in an internal entity, so that +XML_ResumeParser directly uses internalEntityProcessor as +its processor. If the subsequent parse includes some unclosed +tags, this will return without calling storeRawNames to ensure +that the raw versions of the tag names are stored in memory other +than the parse buffer itself. If the parse buffer is then changed +or reallocated (for example if processing a file line by line), +badness will ensue. + +This patch ensures storeRawNames is always called when needed +after calling doContent. The earlier call do doContent does +not need the same protection; it only deals with entity +substitution, which cannot leave unbalanced tags, and in any +case the raw names will be pointing into the stored entity +value not the parse buffer. + +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/4a32da87e931ba54393d465bb77c40b5c33d343b] +CVE: CVE-2022-40674 +Signed-off-by: Virendra Thakur <virendrak@kpit.com> +--- + expat/lib/xmlparse.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +Index: expat/lib/xmlparse.c +=================================================================== +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -5657,10 +5657,15 @@ internalEntityProcessor(XML_Parser parse + { + parser->m_processor = contentProcessor; + /* see externalEntityContentProcessor vs contentProcessor */ +- return doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding, +- s, end, nextPtr, +- (XML_Bool)! parser->m_parsingStatus.finalBuffer, +- XML_ACCOUNT_DIRECT); ++ 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; ++ } ++ return result; + } + } + diff --git a/meta/recipes-core/expat/expat/CVE-2022-43680.patch b/meta/recipes-core/expat/expat/CVE-2022-43680.patch new file mode 100644 index 0000000000..6f93bc3ed7 --- /dev/null +++ b/meta/recipes-core/expat/expat/CVE-2022-43680.patch @@ -0,0 +1,33 @@ +From 5290462a7ea1278a8d5c0d5b2860d4e244f997e4 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Tue, 20 Sep 2022 02:44:34 +0200 +Subject: [PATCH] lib: Fix overeager DTD destruction in + XML_ExternalEntityParserCreate + +CVE: CVE-2022-43680 +Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/5290462a7ea1278a8d5c0d5b2860d4e244f997e4.patch] +Signed-off-by: Ranjitsinh Rathod <ranjitsinh.rathod@kpit.com> +Comments: Hunk refreshed +--- + lib/xmlparse.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/lib/xmlparse.c b/lib/xmlparse.c +index aacd6e7fc..57bf103cc 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -1035,6 +1035,14 @@ parserCreate(const XML_Char *encodingNam + parserInit(parser, encodingName); + + if (encodingName && ! parser->m_protocolEncodingName) { ++ if (dtd) { ++ // We need to stop the upcoming call to XML_ParserFree from happily ++ // destroying parser->m_dtd because the DTD is shared with the parent ++ // parser and the only guard that keeps XML_ParserFree from destroying ++ // parser->m_dtd is parser->m_isParamEntity but it will be set to ++ // XML_TRUE only later in XML_ExternalEntityParserCreate (or not at all). ++ parser->m_dtd = NULL; ++ } + XML_ParserFree(parser); + return NULL; + } diff --git a/meta/recipes-core/expat/expat/libtool-tag.patch b/meta/recipes-core/expat/expat/libtool-tag.patch index 0a0aed23e5..c59ccbbede 100644 --- a/meta/recipes-core/expat/expat/libtool-tag.patch +++ b/meta/recipes-core/expat/expat/libtool-tag.patch @@ -1,30 +1,27 @@ -From 10342e6b600858b091bc7771e454d9e06af06410 Mon Sep 17 00:00:00 2001 -From: Khem Raj <raj.khem@gmail.com> -Date: Thu, 2 Nov 2017 18:20:57 +0800 +From da433dbe79f2d4d5d7d79869c669594c99c5de9c Mon Sep 17 00:00:00 2001 +From: Jasper Orschulko <jasper@fancydomain.eu> +Date: Wed, 16 Jun 2021 19:00:30 +0200 Subject: [PATCH] Add CC tag to build -Add CC tag to build - Upstream-Status: Pending -Signed-off-by: Khem Raj <raj.khem@gmail.com> -Signed-off-by: Dengke Du <dengke.du@windriver.com> +Signed-off-by: Jasper Orschulko <jasper@fancydomain.eu> --- - Makefile.in | 2 +- + Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) -diff --git a/Makefile.in b/Makefile.in -index 9560a95..d444bd6 100644 ---- a/Makefile.in -+++ b/Makefile.in -@@ -319,7 +319,7 @@ LIBCURRENT = @LIBCURRENT@ - LIBOBJS = @LIBOBJS@ - LIBREVISION = @LIBREVISION@ - LIBS = @LIBS@ --LIBTOOL = @LIBTOOL@ -+LIBTOOL = @LIBTOOL@ --tag CC - LIPO = @LIPO@ - LN_S = @LN_S@ - LTLIBOBJS = @LTLIBOBJS@ +diff --git a/Makefile.am b/Makefile.am +index 5e1d37dd..f7a6dece 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -36,7 +36,7 @@ AUTOMAKE_OPTIONS = \ + subdir-objects + + ACLOCAL_AMFLAGS = -I m4 +-LIBTOOLFLAGS = --verbose ++LIBTOOLFLAGS = --verbose --tag=CC + + SUBDIRS = lib # lib goes first to build first + if WITH_EXAMPLES -- -2.7.4 +2.32.0 |