new file mode 100644
@@ -0,0 +1,124 @@
+From 29dfca6ad4d2a9dfd5ce72efd72e1b67d598ab4c Mon Sep 17 00:00:00 2001
+From: netliomax25-code <netliomax25@gmail.com>
+Date: Sat, 6 Jun 2026 20:03:53 +0530
+Subject: [PATCH] lib: protect XML_ResumeParser from being called from a
+ handler
+
+The handler-reentrancy guards from CVE-2026-50219 cover XML_Parse, XML_ParseBuffer, XML_GetBuffer, XML_ParserFree and XML_ParserReset but not XML_ResumeParser, which drives the parser through callProcessor in the same way.
+
+(cherry picked from commit d5a654b4881f450827af5b3b7b72370a3bbf9a8f)
+
+CVE: CVE-2026-56131
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/pull/1267/commits/d5a654b4881f450827af5b3b7b72370a3bbf9a8f]
+Signed-off-by: Jakub Szczudlo <jakub.szczudlo@nokia.com>
+---
+ lib/xmlparse.c | 2 +-
+ tests/handlers.c | 24 ++++++++++++++++++++++++
+ tests/handlers.h | 9 +++++++++
+ tests/misc_tests.c | 20 ++++++++++++++++++++
+ 4 files changed, 54 insertions(+), 1 deletion(-)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index cc1d550..a5fa256 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -2778,7 +2778,7 @@ enum XML_Status XMLCALL
+ XML_ResumeParser(XML_Parser parser) {
+ enum XML_Status result = XML_STATUS_OK;
+
+- if (parser == NULL)
++ if ((parser == NULL) || isCalledFromInsideHandler(parser))
+ return XML_STATUS_ERROR;
+ if (parser->m_parsingStatus.parsing != XML_SUSPENDED) {
+ parser->m_errorCode = XML_ERROR_NOT_SUSPENDED;
+diff --git a/tests/handlers.c b/tests/handlers.c
+index c7d6578..5a85f6d 100644
+--- a/tests/handlers.c
++++ b/tests/handlers.c
+@@ -2015,3 +2015,27 @@ forbidden_calls_character_handler(void *userData, const XML_Char *s, int len) {
+
+ assert_true(XML_GetErrorCode(parser) == XML_ERROR_NONE);
+ }
++
++void XMLCALL
++suspend_then_resume_character_handler(void *userData, const XML_Char *s,
++ int len) {
++ UNUSED_P(s);
++ UNUSED_P(len);
++ ResumeFromHandlerData *const data = (ResumeFromHandlerData *)userData;
++
++ data->callCount++;
++ if (data->callCount > 1) {
++ // Reached only if the guard under test is missing: XML_ResumeParser would
++ // then have driven the parser re-entrantly and called us again. Bail out
++ // so the test fails by assertion below rather than recursing without bound.
++ return;
++ }
++
++ // Put the parser into XML_SUSPENDED so that, without the guard,
++ // XML_ResumeParser would proceed into a re-entrant parse.
++ assert_true(XML_StopParser(data->parser, /*resumable=*/XML_TRUE)
++ == XML_STATUS_OK);
++
++ // Resuming the parser from inside a handler must be rejected.
++ assert_true(XML_ResumeParser(data->parser) == XML_STATUS_ERROR);
++}
+diff --git a/tests/handlers.h b/tests/handlers.h
+index de28392..507ad8b 100644
+--- a/tests/handlers.h
++++ b/tests/handlers.h
+@@ -616,6 +616,15 @@ extern void XMLCALL forbidden_calls_character_handler(void *userData,
+ const XML_Char *s,
+ int len);
+
++typedef struct {
++ XML_Parser parser;
++ int callCount;
++} ResumeFromHandlerData;
++
++extern void XMLCALL suspend_then_resume_character_handler(void *userData,
++ const XML_Char *s,
++ int len);
++
+ #endif /* XML_HANDLERS_H */
+
+ #ifdef __cplusplus
+diff --git a/tests/misc_tests.c b/tests/misc_tests.c
+index b9053fe..265e7cb 100644
+--- a/tests/misc_tests.c
++++ b/tests/misc_tests.c
+@@ -815,6 +815,25 @@ START_TEST(test_misc_calls_forbidden_from_handlers) {
+ }
+ END_TEST
+
++START_TEST(test_misc_resume_parser_forbidden_from_handler) {
++ const char *const doc = "<doc>Hello world!</doc>";
++
++ XML_Parser parser = XML_ParserCreate(NULL);
++ ResumeFromHandlerData data = {parser, 0};
++ XML_SetUserData(parser, &data);
++ XML_SetCharacterDataHandler(parser, suspend_then_resume_character_handler);
++
++ // The handler suspends the parser, so the top-level parse reports suspension
++ // rather than completion. The handler also asserts that resuming from inside
++ // itself is rejected.
++ assert_true(XML_Parse(parser, doc, (int)strlen(doc), /*isFinal=*/XML_TRUE)
++ == XML_STATUS_SUSPENDED);
++ assert_true(data.callCount == 1);
++
++ XML_ParserFree(parser);
++}
++END_TEST
++
+ void
+ make_miscellaneous_test_case(Suite *s) {
+ TCase *tc_misc = tcase_create("miscellaneous tests");
+@@ -847,4 +866,5 @@ make_miscellaneous_test_case(Suite *s) {
+ tcase_add_test(tc_misc, test_misc_async_entity_rejected);
+ tcase_add_test(tc_misc, test_misc_no_infinite_loop_issue_1161);
+ tcase_add_test(tc_misc, test_misc_calls_forbidden_from_handlers);
++ tcase_add_test(tc_misc, test_misc_resume_parser_forbidden_from_handler);
+ }
+--
+2.34.1
+
@@ -68,6 +68,7 @@ SRC_URI = "${GITHUB_BASE_URI}/download/R_${VERSION_TAG}/expat-${PV}.tar.bz2 \
file://CVE-2026-50219-30.patch \
file://CVE-2026-50219-31.patch \
file://CVE-2026-50219-32.patch \
+ file://CVE-2026-56131.patch \
"
GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/"
Backport patch to fix CVE-2026-56131. References: https://nvd.nist.gov/vuln/detail/CVE-2026-56131 Upstream fix: https://github.com/libexpat/libexpat/commit/d5a654b4881f450827af5b3b7b72370a3bbf9a8f Signed-off-by: Jakub Szczudlo <jakub.szczudlo@nokia.com> --- .../expat/expat/CVE-2026-56131.patch | 124 ++++++++++++++++++ meta/recipes-core/expat/expat_2.7.5.bb | 1 + 2 files changed, 125 insertions(+) create mode 100644 meta/recipes-core/expat/expat/CVE-2026-56131.patch