diff mbox series

[scarthgap] expat: follow-up for CVE-2024-8176

Message ID 20251008184901.1682495-1-peter.marko@siemens.com
State Under Review
Delegated to: Steve Sakoman
Headers show
Series [scarthgap] expat: follow-up for CVE-2024-8176 | expand

Commit Message

Peter Marko Oct. 8, 2025, 6:49 p.m. UTC
From: Peter Marko <peter.marko@siemens.com>

Expat release 2.7.3 implemented a follow-up for this CVE.
References:
* https://github.com/libexpat/libexpat/blob/R_2_7_3/expat/Changes
* https://security-tracker.debian.org/tracker/CVE-2024-8176
* https://github.com/libexpat/libexpat/pull/1059

Signed-off-by: Peter Marko <peter.marko@siemens.com>
---
 .../expat/expat/CVE-2024-8176-03.patch        |  35 ++++++
 .../expat/expat/CVE-2024-8176-04.patch        | 115 ++++++++++++++++++
 .../expat/expat/CVE-2024-8176-05.patch        |  78 ++++++++++++
 meta/recipes-core/expat/expat_2.6.4.bb        |   3 +
 4 files changed, 231 insertions(+)
 create mode 100644 meta/recipes-core/expat/expat/CVE-2024-8176-03.patch
 create mode 100644 meta/recipes-core/expat/expat/CVE-2024-8176-04.patch
 create mode 100644 meta/recipes-core/expat/expat/CVE-2024-8176-05.patch
diff mbox series

Patch

diff --git a/meta/recipes-core/expat/expat/CVE-2024-8176-03.patch b/meta/recipes-core/expat/expat/CVE-2024-8176-03.patch
new file mode 100644
index 00000000000..c9990d5547e
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2024-8176-03.patch
@@ -0,0 +1,35 @@ 
+From ba80428c2207259103b73871d447dee34755340c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Berkay=20Eren=20=C3=9Cr=C3=BCn?= <berkay.ueruen@tum.de>
+Date: Tue, 23 Sep 2025 11:22:14 +0200
+Subject: [PATCH] lib: Fix detection of asynchronous tags in entities
+
+According to the XML standard, tags must be closed within the same
+element in which they are opened. Since the change of the entity
+processing method in version 2.7.0, violations of this rule have not
+been handled correctly for entities.
+
+This commit adds the required checks to detect any violations and
+restores the correct behaviour.
+
+CVE: CVE-2024-8176
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/pull/1059]
+Signed-off-by: Peter Marko <peter.marko@siemens.com>
+---
+ lib/xmlparse.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index ce29ab6f..ba4e3c48 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -6087,6 +6087,10 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
+     // process its possible inner entities (which are added to the
+     // m_openInternalEntities during doProlog or doContent calls above)
+     entity->hasMore = XML_FALSE;
++    if (! entity->is_param
++        && (openEntity->startTagLevel != parser->m_tagLevel)) {
++      return XML_ERROR_ASYNC_ENTITY;
++    }
+     triggerReenter(parser);
+     return result;
+   } // End of entity processing, "if" block will return here
diff --git a/meta/recipes-core/expat/expat/CVE-2024-8176-04.patch b/meta/recipes-core/expat/expat/CVE-2024-8176-04.patch
new file mode 100644
index 00000000000..9623467698f
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2024-8176-04.patch
@@ -0,0 +1,115 @@ 
+From 81a114f7eebcd41a6993337128cda337986a26f4 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping <sebastian@pipping.org>
+Date: Mon, 15 Sep 2025 21:57:07 +0200
+Subject: [PATCH] tests: Cover XML_ERROR_ASYNC_ENTITY cases
+
+CVE: CVE-2024-8176
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/pull/1059]
+Signed-off-by: Peter Marko <peter.marko@siemens.com>
+---
+ tests/misc_tests.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 87 insertions(+)
+
+diff --git a/tests/misc_tests.c b/tests/misc_tests.c
+index 3346bce6..19f41df7 100644
+--- a/tests/misc_tests.c
++++ b/tests/misc_tests.c
+@@ -621,6 +621,91 @@ START_TEST(test_misc_expected_event_ptr_issue_980) {
+ }
+ END_TEST
+ 
++START_TEST(test_misc_sync_entity_tolerated) {
++  const char *const doc = "<!DOCTYPE t0 [\n"
++                          "   <!ENTITY a '<t1></t1>'>\n"
++                          "   <!ENTITY b '<t2>two</t2>'>\n"
++                          "   <!ENTITY c '<t3>three<t4>four</t4>three</t3>'>\n"
++                          "   <!ENTITY d '<t5>&b;</t5>'>\n"
++                          "]>\n"
++                          "<t0>&a;&b;&c;&d;</t0>\n";
++  XML_Parser parser = XML_ParserCreate(NULL);
++
++  assert_true(_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc),
++                                      /*isFinal=*/XML_TRUE)
++              == XML_STATUS_OK);
++
++  XML_ParserFree(parser);
++}
++END_TEST
++
++START_TEST(test_misc_async_entity_rejected) {
++  struct test_case {
++    const char *doc;
++    enum XML_Status expectedStatusNoGE;
++    enum XML_Error expectedErrorNoGE;
++  };
++  const struct test_case cases[] = {
++      // Opened by one entity, closed by another
++      {"<!DOCTYPE t0 [\n"
++       "   <!ENTITY open '<t1>'>\n"
++       "   <!ENTITY close '</t1>'>\n"
++       "]>\n"
++       "<t0>&open;&close;</t0>\n",
++       XML_STATUS_OK, XML_ERROR_NONE},
++      // Opened by tag, closed by entity (non-root case)
++      {"<!DOCTYPE t0 [\n"
++       "  <!ENTITY g0 ''>\n"
++       "  <!ENTITY g1 '&g0;</t1>'>\n"
++       "]>\n"
++       "<t0><t1>&g1;</t0>\n",
++       XML_STATUS_ERROR, XML_ERROR_TAG_MISMATCH},
++      // Opened by tag, closed by entity (root case)
++      {"<!DOCTYPE t0 [\n"
++       "  <!ENTITY g0 ''>\n"
++       "  <!ENTITY g1 '&g0;</t0>'>\n"
++       "]>\n"
++       "<t0>&g1;\n",
++       XML_STATUS_ERROR, XML_ERROR_NO_ELEMENTS},
++      // Opened by entity, closed by tag <-- regression from 2.7.0
++      {"<!DOCTYPE t0 [\n"
++       "  <!ENTITY g0 ''>\n"
++       "  <!ENTITY g1 '<t1>&g0;'>\n"
++       "]>\n"
++       "<t0>&g1;</t1></t0>\n",
++       XML_STATUS_ERROR, XML_ERROR_TAG_MISMATCH},
++      // Opened by tag, closed by entity; then the other way around
++      {"<!DOCTYPE t0 [\n"
++       "  <!ENTITY open '<t1>'>\n"
++       "  <!ENTITY close '</t1>'>\n"
++       "]>\n"
++       "<t0><t1>&close;&open;</t1></t0>\n",
++       XML_STATUS_OK, XML_ERROR_NONE},
++  };
++
++  for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
++    const struct test_case testCase = cases[i];
++    set_subtest("cases[%d]", (int)i);
++
++    const char *const doc = testCase.doc;
++#if XML_GE == 1
++    const enum XML_Status expectedStatus = XML_STATUS_ERROR;
++    const enum XML_Error expectedError = XML_ERROR_ASYNC_ENTITY;
++#else
++    const enum XML_Status expectedStatus = testCase.expectedStatusNoGE;
++    const enum XML_Error expectedError = testCase.expectedErrorNoGE;
++#endif
++
++    XML_Parser parser = XML_ParserCreate(NULL);
++    assert_true(_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc),
++                                        /*isFinal=*/XML_TRUE)
++                == expectedStatus);
++    assert_true(XML_GetErrorCode(parser) == expectedError);
++    XML_ParserFree(parser);
++  }
++}
++END_TEST
++
+ void
+ make_miscellaneous_test_case(Suite *s) {
+   TCase *tc_misc = tcase_create("miscellaneous tests");
+@@ -649,4 +734,6 @@ make_miscellaneous_test_case(Suite *s) {
+   tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser);
+   tcase_add_test__if_xml_ge(tc_misc, test_renter_loop_finite_content);
+   tcase_add_test(tc_misc, test_misc_expected_event_ptr_issue_980);
++  tcase_add_test(tc_misc, test_misc_sync_entity_tolerated);
++  tcase_add_test(tc_misc, test_misc_async_entity_rejected);
+ }
diff --git a/meta/recipes-core/expat/expat/CVE-2024-8176-05.patch b/meta/recipes-core/expat/expat/CVE-2024-8176-05.patch
new file mode 100644
index 00000000000..063a590a118
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2024-8176-05.patch
@@ -0,0 +1,78 @@ 
+From a9aaf85cfc3025b7013b5adc4bef2ce32ecc7fb1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Berkay=20Eren=20=C3=9Cr=C3=BCn?= <berkay.ueruen@tum.de>
+Date: Tue, 23 Sep 2025 12:12:50 +0200
+Subject: [PATCH] tests: Add line/column checks to async entity tests
+
+CVE: CVE-2024-8176
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/pull/1059]
+Signed-off-by: Peter Marko <peter.marko@siemens.com>
+---
+ tests/misc_tests.c | 17 ++++++++++++-----
+ 1 file changed, 12 insertions(+), 5 deletions(-)
+
+diff --git a/tests/misc_tests.c b/tests/misc_tests.c
+index 19f41df7..7a4d2455 100644
+--- a/tests/misc_tests.c
++++ b/tests/misc_tests.c
+@@ -644,6 +644,8 @@ START_TEST(test_misc_async_entity_rejected) {
+     const char *doc;
+     enum XML_Status expectedStatusNoGE;
+     enum XML_Error expectedErrorNoGE;
++    XML_Size expectedErrorLine;
++    XML_Size expectedErrorColumn;
+   };
+   const struct test_case cases[] = {
+       // Opened by one entity, closed by another
+@@ -652,35 +654,35 @@ START_TEST(test_misc_async_entity_rejected) {
+        "   <!ENTITY close '</t1>'>\n"
+        "]>\n"
+        "<t0>&open;&close;</t0>\n",
+-       XML_STATUS_OK, XML_ERROR_NONE},
++       XML_STATUS_OK, XML_ERROR_NONE, 5, 4},
+       // Opened by tag, closed by entity (non-root case)
+       {"<!DOCTYPE t0 [\n"
+        "  <!ENTITY g0 ''>\n"
+        "  <!ENTITY g1 '&g0;</t1>'>\n"
+        "]>\n"
+        "<t0><t1>&g1;</t0>\n",
+-       XML_STATUS_ERROR, XML_ERROR_TAG_MISMATCH},
++       XML_STATUS_ERROR, XML_ERROR_TAG_MISMATCH, 5, 8},
+       // Opened by tag, closed by entity (root case)
+       {"<!DOCTYPE t0 [\n"
+        "  <!ENTITY g0 ''>\n"
+        "  <!ENTITY g1 '&g0;</t0>'>\n"
+        "]>\n"
+        "<t0>&g1;\n",
+-       XML_STATUS_ERROR, XML_ERROR_NO_ELEMENTS},
++       XML_STATUS_ERROR, XML_ERROR_NO_ELEMENTS, 5, 4},
+       // Opened by entity, closed by tag <-- regression from 2.7.0
+       {"<!DOCTYPE t0 [\n"
+        "  <!ENTITY g0 ''>\n"
+        "  <!ENTITY g1 '<t1>&g0;'>\n"
+        "]>\n"
+        "<t0>&g1;</t1></t0>\n",
+-       XML_STATUS_ERROR, XML_ERROR_TAG_MISMATCH},
++       XML_STATUS_ERROR, XML_ERROR_TAG_MISMATCH, 5, 4},
+       // Opened by tag, closed by entity; then the other way around
+       {"<!DOCTYPE t0 [\n"
+        "  <!ENTITY open '<t1>'>\n"
+        "  <!ENTITY close '</t1>'>\n"
+        "]>\n"
+        "<t0><t1>&close;&open;</t1></t0>\n",
+-       XML_STATUS_OK, XML_ERROR_NONE},
++       XML_STATUS_OK, XML_ERROR_NONE, 5, 8},
+   };
+ 
+   for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
+@@ -701,6 +703,11 @@ START_TEST(test_misc_async_entity_rejected) {
+                                         /*isFinal=*/XML_TRUE)
+                 == expectedStatus);
+     assert_true(XML_GetErrorCode(parser) == expectedError);
++#if XML_GE == 1
++    assert_true(XML_GetCurrentLineNumber(parser) == testCase.expectedErrorLine);
++    assert_true(XML_GetCurrentColumnNumber(parser)
++                == testCase.expectedErrorColumn);
++#endif
+     XML_ParserFree(parser);
+   }
+ }
diff --git a/meta/recipes-core/expat/expat_2.6.4.bb b/meta/recipes-core/expat/expat_2.6.4.bb
index ab0b1d54c13..816beaa8a3d 100644
--- a/meta/recipes-core/expat/expat_2.6.4.bb
+++ b/meta/recipes-core/expat/expat_2.6.4.bb
@@ -13,6 +13,9 @@  SRC_URI = "${GITHUB_BASE_URI}/download/R_${VERSION_TAG}/expat-${PV}.tar.bz2  \
            file://0001-tests-Cover-indirect-entity-recursion.patch;striplevel=2 \
            file://CVE-2024-8176-01.patch;striplevel=2 \
            file://CVE-2024-8176-02.patch;striplevel=2 \
+           file://CVE-2024-8176-03.patch \
+           file://CVE-2024-8176-04.patch \
+           file://CVE-2024-8176-05.patch \
            "
 
 GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/"