diff --git a/backport-CVE-2024-28757-001.patch b/backport-CVE-2024-28757-001.patch new file mode 100644 index 0000000000000000000000000000000000000000..08da7a0ba5a4f41457bf1cc493b47e3455cf32fb --- /dev/null +++ b/backport-CVE-2024-28757-001.patch @@ -0,0 +1,57 @@ +From 1d50b80cf31de87750103656f6eb693746854aa8 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Mon, 4 Mar 2024 23:49:06 +0100 +Subject: [PATCH] lib/xmlparse.c: Detect billion laughs attack with isolated + external parser + +When parsing DTD content with code like .. + + XML_Parser parser = XML_ParserCreate(NULL); + XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL); + enum XML_Status status = XML_Parse(ext_parser, doc, (int)strlen(doc), XML_TRUE); + +.. there are 0 bytes accounted as direct input and all input from accounted +as indirect input. Now function accountingGetCurrentAmplification cannot calculate +the current amplification ratio as "(direct + indirect) / direct", and it did refuse +to divide by 0 as one would expect, but it returned 1.0 for this case to indicate +no amplification over direct input. As a result, billion laughs attacks from +DTD-only input were not detected with this isolated way of using an external parser. + +The new approach is to assume direct input of length not 0 but 22 -- derived from +ghost input "", the shortest possible way to include an external +DTD --, and do the usual "(direct + indirect) / direct" math with "direct := 22". + +GitHub issue #839 has more details on this issue and its origin in ClusterFuzz +finding 66812. +--- + expat/lib/xmlparse.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/lib/xmlparse.c b/expat/lib/xmlparse.c +index b884d82b..d44baa68 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -7787,6 +7787,8 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { + + static float + accountingGetCurrentAmplification(XML_Parser rootParser) { ++ // 1.........1.........12 => 22 ++ const size_t lenOfShortestInclude = sizeof("") - 1; + const XmlBigCount countBytesOutput + = rootParser->m_accounting.countBytesDirect + + rootParser->m_accounting.countBytesIndirect; +@@ -7794,7 +7796,9 @@ accountingGetCurrentAmplification(XML_Parser rootParser) { + = rootParser->m_accounting.countBytesDirect + ? (countBytesOutput + / (float)(rootParser->m_accounting.countBytesDirect)) +- : 1.0f; ++ : ((lenOfShortestInclude ++ + rootParser->m_accounting.countBytesIndirect) ++ / (float)lenOfShortestInclude); + assert(! rootParser->m_parentParser); + return amplificationFactor; + } +-- +2.33.0 + + diff --git a/backport-CVE-2024-28757-002.patch b/backport-CVE-2024-28757-002.patch new file mode 100644 index 0000000000000000000000000000000000000000..ad961a5ffa4df5a419c0b50e57159582c85ce952 --- /dev/null +++ b/backport-CVE-2024-28757-002.patch @@ -0,0 +1,26 @@ +From a4c86a395ee447c59175c762af3d17f7107b2261 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Sun, 3 Mar 2024 02:19:58 +0100 +Subject: [PATCH] lib/xmlparse.c: Reject directly recursive parameter entities + +--- + expat/lib/xmlparse.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/xmlparse.c b/expat/lib/xmlparse.c +index b884d82b..8e667fcb 100644 +--- a/lib/xmlparse.c ++++ b/lib/xmlparse.c +@@ -6240,7 +6240,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, + dtd->keepProcessing = dtd->standalone; + goto endEntityValue; + } +- if (entity->open) { ++ if (entity->open || (entity == parser->m_declEntity)) { + if (enc == parser->m_encoding) + parser->m_eventPtr = entityTextPtr; + result = XML_ERROR_RECURSIVE_ENTITY_REF; +-- +2.33.0 + + diff --git a/backport-CVE-2024-28757-003.patch b/backport-CVE-2024-28757-003.patch new file mode 100644 index 0000000000000000000000000000000000000000..99c4df40d90774ad80db7ce9ef6dbcffc979a6c2 --- /dev/null +++ b/backport-CVE-2024-28757-003.patch @@ -0,0 +1,86 @@ +From bef3be76723b344565736a902c98bad5f127f5bc Mon Sep 17 00:00:00 2001 +From: caixiaomeng 00662745 +Date: Thu, 21 Mar 2024 20:29:49 +0800 +Subject: [PATCH] tests-Cover-amplification-tracking-for-isolated-exte + +--- + tests/runtests.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 55 insertions(+) + +diff --git a/tests/runtests.c b/tests/runtests.c +index 2237b4d..191e7c1 100644 +--- a/tests/runtests.c ++++ b/tests/runtests.c +@@ -11966,6 +11966,59 @@ START_TEST(test_helper_unsigned_char_to_printable) { + END_TEST + #endif // defined(XML_DTD) + ++START_TEST(test_amplification_isolated_external_parser) { ++ // NOTE: Length 44 is precisely twice the length of "" ++ // (22) that is used in function accountingGetCurrentAmplification in ++ // xmlparse.c. ++ // 1.........1.........1.........1.........1..4 => 44 ++ const char doc[] = ""; ++ const int docLen = (int)sizeof(doc) - 1; ++ const float maximumToleratedAmplification = 2.0f; ++ ++ struct TestCase { ++ int offsetOfThreshold; ++ enum XML_Status expectedStatus; ++ }; ++ ++ struct TestCase cases[] = { ++ {-2, XML_STATUS_ERROR}, {-1, XML_STATUS_ERROR}, {0, XML_STATUS_ERROR}, ++ {+1, XML_STATUS_OK}, {+2, XML_STATUS_OK}, ++ }; ++ ++ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { ++ const int offsetOfThreshold = cases[i].offsetOfThreshold; ++ const enum XML_Status expectedStatus = cases[i].expectedStatus; ++ const unsigned long long activationThresholdBytes ++ = docLen + offsetOfThreshold; ++ ++ XML_Parser parser = XML_ParserCreate(NULL); ++ assert(parser != NULL); ++ ++ assert(XML_SetBillionLaughsAttackProtectionMaximumAmplification( ++ parser, maximumToleratedAmplification) ++ == XML_TRUE); ++ assert(XML_SetBillionLaughsAttackProtectionActivationThreshold( ++ parser, activationThresholdBytes) ++ == XML_TRUE); ++ ++ XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL); ++ assert(ext_parser != NULL); ++ ++ const enum XML_Status actualStatus ++ = _XML_Parse_SINGLE_BYTES(ext_parser, doc, docLen, XML_TRUE); ++ ++ assert(actualStatus == expectedStatus); ++ if (actualStatus != XML_STATUS_OK) { ++ assert(XML_GetErrorCode(ext_parser) ++ == XML_ERROR_AMPLIFICATION_LIMIT_BREACH); ++ } ++ ++ XML_ParserFree(ext_parser); ++ XML_ParserFree(parser); ++ } ++} ++END_TEST ++ + static Suite * + make_suite(void) { + Suite *s = suite_create("basic"); +@@ -12206,6 +12259,8 @@ make_suite(void) { + tcase_add_test(tc_basic, test_bad_notation); + tcase_add_test(tc_basic, test_default_doctype_handler); + tcase_add_test(tc_basic, test_empty_element_abort); ++ tcase_add_test__ifdef_xml_dtd(tc_basic, ++ test_amplification_isolated_external_parser); + + suite_add_tcase(s, tc_namespace); + tcase_add_checked_fixture(tc_namespace, namespace_setup, namespace_teardown); +-- +2.33.0 + + diff --git a/backport-CVE-2024-28757-004.patch b/backport-CVE-2024-28757-004.patch new file mode 100644 index 0000000000000000000000000000000000000000..1511b59745b87a80e25ad6f9e02855786ac76b25 --- /dev/null +++ b/backport-CVE-2024-28757-004.patch @@ -0,0 +1,75 @@ +From 7cf9724d39209d085849b96e5012e658760902ad Mon Sep 17 00:00:00 2001 +From: caixiaomeng 00662745 +Date: Thu, 21 Mar 2024 20:40:48 +0800 +Subject: [PATCH] tests-Cover-rejection-of-direct-parameter-entity-rec + +--- + tests/runtests.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 44 insertions(+) + +diff --git a/tests/runtests.c b/tests/runtests.c +index 191e7c1..1c1ce56 100644 +--- a/tests/runtests.c ++++ b/tests/runtests.c +@@ -12019,6 +12019,48 @@ START_TEST(test_amplification_isolated_external_parser) { + } + END_TEST + ++START_TEST(test_recursive_external_parameter_entity_2) { ++ struct TestCase { ++ const char *doc; ++ enum XML_Status expectedStatus; ++ }; ++ ++ struct TestCase cases[] = { ++ {"", XML_STATUS_ERROR}, ++ {"" ++ "", ++ XML_STATUS_ERROR}, ++ {"" ++ "", ++ XML_STATUS_OK}, ++ {"", XML_STATUS_OK}, ++ }; ++ ++ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { ++ const char *const doc = cases[i].doc; ++ const enum XML_Status expectedStatus = cases[i].expectedStatus; ++ ++ XML_Parser parser = XML_ParserCreate(NULL); ++ assert(parser != NULL); ++ ++ XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL); ++ assert(ext_parser != NULL); ++ ++ const enum XML_Status actualStatus ++ = _XML_Parse_SINGLE_BYTES(ext_parser, doc, (int)strlen(doc), XML_TRUE); ++ ++ assert(actualStatus == expectedStatus); ++ if (actualStatus != XML_STATUS_OK) { ++ assert(XML_GetErrorCode(ext_parser) ++ == XML_ERROR_RECURSIVE_ENTITY_REF); ++ } ++ ++ XML_ParserFree(ext_parser); ++ XML_ParserFree(parser); ++ } ++} ++END_TEST ++ + static Suite * + make_suite(void) { + Suite *s = suite_create("basic"); +@@ -12261,6 +12303,8 @@ make_suite(void) { + tcase_add_test(tc_basic, test_empty_element_abort); + tcase_add_test__ifdef_xml_dtd(tc_basic, + test_amplification_isolated_external_parser); ++ tcase_add_test__ifdef_xml_dtd(tc_basic, ++ test_recursive_external_parameter_entity_2); + + suite_add_tcase(s, tc_namespace); + tcase_add_checked_fixture(tc_namespace, namespace_setup, namespace_teardown); +-- +2.33.0 + + diff --git a/expat.spec b/expat.spec index 3980c018461a83ef98998e099ce5f0d432471204..b64024fff2e6d12fd46fac4d74341d3f80c9bed7 100644 --- a/expat.spec +++ b/expat.spec @@ -1,7 +1,7 @@ -%define Rversion %(echo %{version} | sed -e 's/\\./_/g' -e 's/^/R_/') +%define Rversion %(echo %{version} | sed -e 's/\./_/g' -e 's/^/R_/') Name: expat Version: 2.4.1 -Release: 8 +Release: 9 Summary: An XML parser library License: MIT URL: https://libexpat.github.io/ @@ -30,6 +30,10 @@ Patch19: backport-0001-CVE-2022-40674.patch Patch20: backport-0002-CVE-2022-40674.patch Patch21: backport-CVE-2022-43680.patch Patch22: backport-tests-Cover-overeager-DTD-destruction-in-XML_Externa.patch +Patch23: backport-CVE-2024-28757-001.patch +Patch24: backport-CVE-2024-28757-002.patch +Patch25: backport-CVE-2024-28757-003.patch +Patch26: backport-CVE-2024-28757-004.patch BuildRequires: sed,autoconf,automake,gcc-c++,libtool,xmlto @@ -51,7 +55,7 @@ This package provides with static libraries and header files for developing wit %build autoreconf -fiv -%configure CFLAGS="$RPM_OPT_FLAGS -fPIC" DOCBOOK_TO_MAN="xmlto man --skip-validation" +%configure CFLAGS=" -fPIC" DOCBOOK_TO_MAN="xmlto man --skip-validation" %make_build %install @@ -83,6 +87,9 @@ make check %{_mandir}/man1/* %changelog +* Thu Mar 21 2024 caixiaomeng - 2.4.1-9 +- fix CVE-2024-28757 + * Sat Oct 29 2022 fuanan - 2.4.1-8 - fix CVE-2022-43680 @@ -162,3 +169,4 @@ make check * Thu Aug 29 2019 openEuler Buildteam - 2.2.6-1 - Package Init +