diff --git a/meta/recipes-core/expat/expat/CVE-2026-117610_p1.patch b/meta/recipes-core/expat/expat/CVE-2026-117610_p1.patch
new file mode 100644
index 0000000000..fd645dae66
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2026-117610_p1.patch
@@ -0,0 +1,30 @@
+From bfc28346fa3efeb8ca56c17437d24a7344e5feab Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping <sebastian@pipping.org>
+Date: Mon, 26 Oct 2026 17:39:32 +0100
+Subject: [PATCH] lib: Fix initial non-final empty subparser call with a
+ handler
+
+CVE: CVE-2026-117610
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/bfc28346fa3efeb8ca56c17437d24a7344e5feab]
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ lib/xmlparse.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/lib/xmlparse.c b/lib/xmlparse.c
+index a0946be..b17f5e0 100644
+--- a/lib/xmlparse.c
++++ b/lib/xmlparse.c
+@@ -3233,6 +3233,11 @@ externalEntityInitProcessor2(XML_Parser parser, const char *start,
+     }
+     parser->m_eventPtr = start;
+     return XML_ERROR_PARTIAL_CHAR;
++  case XML_TOK_NONE:
++    if (! parser->m_parsingStatus.finalBuffer) {
++      *endPtr = start;
++    }
++    return XML_ERROR_NONE;
+   }
+   parser->m_processor = externalEntityInitProcessor3;
+   return externalEntityInitProcessor3(parser, start, end, endPtr);
diff --git a/meta/recipes-core/expat/expat/CVE-2026-117610_p2.patch b/meta/recipes-core/expat/expat/CVE-2026-117610_p2.patch
new file mode 100644
index 0000000000..2ebe6446c4
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2026-117610_p2.patch
@@ -0,0 +1,92 @@
+From cedb704d2cad9d6ef77f8f172104d3e751eccfd2 Mon Sep 17 00:00:00 2001
+From: Sebastian Pipping <sebastian@pipping.org>
+Date: Mon, 26 Oct 2026 17:17:57 +0100
+Subject: [PATCH] tests: Add a test for a initial non-final empty subparser
+ parse
+
+.. with a handler call
+
+CVE: CVE-2026-117610
+Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/cedb704d2cad9d6ef77f8f172104d3e751eccfd2]
+
+Backport Changes:
+- Compare handler data with the existing XCS macro and xcstrcmp test
+  helper in Expat 2.8.3. This replaces xcsdup_str allocations,
+  allocation assertions and frees, avoiding the newer xcs helpers.
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ tests/misc_tests.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 55 insertions(+)
+
+diff --git a/tests/misc_tests.c b/tests/misc_tests.c
+index 04538c2..6c82715 100644
+--- a/tests/misc_tests.c
++++ b/tests/misc_tests.c
+@@ -957,6 +957,60 @@ START_TEST(test_misc_input_2gb) {
+ }
+ END_TEST
+ 
++static void
++comparingPiHandler(void *userData, const XML_Char *target,
++                   const XML_Char *data) {
++  UNUSED_P(userData);
++
++  assert_true(xcstrcmp(target, XCS("target")) == 0);
++  assert_true(xcstrcmp(data, XCS("data")) == 0);
++}
++
++static int XMLCALL
++piParsingExternalEntityHandler(XML_Parser parent, const XML_Char *context,
++                               const XML_Char *base, const XML_Char *systemId,
++                               const XML_Char *publicId) {
++  UNUSED_P(base);
++  UNUSED_P(systemId);
++  UNUSED_P(publicId);
++
++  const char *const doc = "<?target data?>";
++
++  XML_Parser subParser = XML_ExternalEntityParserCreate(parent, context, NULL);
++  assert_true(subParser != NULL);
++
++  // NOTE: Related test "test_empty_ext_param_entity_in_value" could be of
++  // interest
++  assert_true(_XML_Parse_SINGLE_BYTES(subParser, "", 0, /*isFinal=*/XML_FALSE)
++              == XML_STATUS_OK);
++  assert_true(_XML_Parse_SINGLE_BYTES(subParser, doc, (int)strlen(doc),
++                                      /*isFinal=*/XML_TRUE)
++              == XML_STATUS_OK);
++
++  XML_ParserFree(subParser);
++
++  return XML_STATUS_OK;
++}
++
++START_TEST(test_misc_empty_nonfinal_subparser_parse) {
++  const char *const doc = "<!DOCTYPE root [\n"
++                          "  <!ENTITY ext SYSTEM 'external.dtd'>\n"
++                          "]>\n"
++                          "<root>&ext;</root>\n";
++
++  XML_Parser parser = XML_ParserCreate(NULL);
++  assert_true(parser != NULL);
++
++  XML_SetProcessingInstructionHandler(parser, comparingPiHandler);
++  XML_SetExternalEntityRefHandler(parser, piParsingExternalEntityHandler);
++
++  assert_true(_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc),
++                                      /*isFinal=*/XML_TRUE));
++
++  XML_ParserFree(parser);
++}
++END_TEST
++
+ void
+ make_miscellaneous_test_case(Suite *s) {
+   TCase *tc_misc = tcase_create("miscellaneous tests");
+@@ -993,4 +1047,5 @@ make_miscellaneous_test_case(Suite *s) {
+   tcase_add_test(tc_misc, test_misc_unknown_encoding_callbacks_protected);
+   tcase_add_test(tc_misc, test_misc_input_2gb);
+   tcase_add_test(tc_misc, test_misc_low_surrogate_mozilla_bug_2053153);
++  tcase_add_test(tc_misc, test_misc_empty_nonfinal_subparser_parse);
+ }
diff --git a/meta/recipes-core/expat/expat_2.8.3.bb b/meta/recipes-core/expat/expat_2.8.3.bb
index 4f2672c955..b6911e79de 100644
--- a/meta/recipes-core/expat/expat_2.8.3.bb
+++ b/meta/recipes-core/expat/expat_2.8.3.bb
@@ -25,6 +25,8 @@ SRC_URI = "${GITHUB_BASE_URI}/download/R_${VERSION_TAG}/expat-${PV}.tar.bz2 \
            file://CVE-2026-113351_p3.patch \
            file://CVE-2026-113351_p4.patch \
            file://CVE-2026-113351_p5.patch \
+           file://CVE-2026-117610_p1.patch \
+           file://CVE-2026-117610_p2.patch \
            "
 
 GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/"
