expat: follow-up for CVE-2024-8176

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

(From OE-Core rev: 5bbb9ee52674f5aa6eed5d6cf3f515704092994d)

Signed-off-by: Peter Marko <peter.marko@siemens.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
This commit is contained in:
Peter Marko
2025-10-08 20:49:01 +02:00
committed by Steve Sakoman
parent 9fb26deedd
commit da7d29485c
4 changed files with 231 additions and 0 deletions

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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/"