new file mode 100644
@@ -0,0 +1,92 @@
+From b5a36ccbdb97298fbf9b5b5767affe92c534461f Mon Sep 17 00:00:00 2001
+From: Steven Silvester <steve.silvester@mongodb.com>
+Date: Tue, 13 Oct 2026 09:30:43 -0500
+Subject: [PATCH] PYTHON-6111 C extension accepts BSON documents where element
+ data overlaps the document terminator (#3113)
+
+CVE: CVE-2026-106435
+Upstream-Status: Backport [https://github.com/mongodb/mongo-python-driver/commit/b5a36ccbdb97298fbf9b5b5767affe92c534461f]
+
+Backport Changes:
+- Omitted doc/changelog.rst because the release note is documentation-only.
+
+(cherry picked from commit b5a36ccbdb97298fbf9b5b5767affe92c534461f)
+Signed-off-by: Yogita Urade <yurade@cisco.com>
+---
+ bson/_cbsonmodule.c | 7 ++++++-
+ test/test_bson.py | 34 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 40 insertions(+), 1 deletion(-)
+
+diff --git a/bson/_cbsonmodule.c b/bson/_cbsonmodule.c
+index 3f141040..44cc9375 100644
+--- a/bson/_cbsonmodule.c
++++ b/bson/_cbsonmodule.c
+@@ -2221,6 +2221,9 @@ static PyObject* get_value(PyObject* self, PyObject* name, const char* buffer,
+ }
+ case 8:
+ {
++ if (max < 1) {
++ goto invalid;
++ }
+ char boolean_raw = buffer[(*position)++];
+ if (0 == boolean_raw) {
+ value = Py_False;
+@@ -2274,7 +2277,9 @@ static PyObject* get_value(PyObject* self, PyObject* name, const char* buffer,
+ }
+ *position += (unsigned)pattern_length + 1;
+ start += pattern_length + 1;
+- const char* flags_nul = memchr(start, 0, max - pattern_length);
++ /* PYTHON-6111: account for the pattern NUL consumed above so
++ * the flags terminator cannot overlap the document terminator. */
++ const char* flags_nul = memchr(start, 0, max - pattern_length - 1);
+ if (!flags_nul) {
+ Py_DECREF(pattern);
+ goto invalid;
+diff --git a/test/test_bson.py b/test/test_bson.py
+index bc367bc2..47511567 100644
+--- a/test/test_bson.py
++++ b/test/test_bson.py
+@@ -446,6 +446,40 @@ class TestBSON(unittest.TestCase):
+ self.assertEqual(decode(mm), expected)
+ self.assertEqual(decode_all(mm), [expected])
+
++ def test_element_overlaps_document_terminator(self):
++ # PYTHON-6111: an element whose value would consume the document
++ # terminator byte must be rejected, matching the pure-Python decoder.
++ bad_bsons = [
++ # Boolean value byte is the document terminator.
++ ("boolean value", b"\x08\x00\x00\x00\x08a\x00\x00"),
++ # Regex pattern terminator is the document terminator.
++ ("regex pattern", b"\x08\x00\x00\x00\x0ba\x00\x00"),
++ # Regex flags terminator is the document terminator.
++ ("regex flags", b"\x0a\x00\x00\x00\x0ba\x00b\x00\x00"),
++ ]
++ for description, data in bad_bsons:
++ with self.subTest(description, bad_bson=data):
++ self.assertFalse(is_valid(data))
++ with self.assertRaises(InvalidBSON):
++ decode(data)
++ with self.assertRaises(InvalidBSON):
++ decode(bytearray(data))
++ with self.assertRaises(InvalidBSON):
++ decode(memoryview(data))
++ with self.assertRaises(InvalidBSON):
++ decode(array.array("B", data))
++ with self.assertRaises(InvalidBSON):
++ list(decode_iter(data))
++ with self.assertRaises(InvalidBSON):
++ decode_all(data)
++ with mmap.mmap(-1, len(data)) as mm:
++ mm.write(data)
++ mm.seek(0)
++ with self.assertRaises(InvalidBSON):
++ decode(mm)
++ with self.assertRaises(InvalidBSON):
++ decode_all(mm)
++
+ def test_data_timestamp(self):
+ self.assertEqual(
+ {"test": Timestamp(4, 20)},
+--
+2.35.6
+
new file mode 100644
@@ -0,0 +1,122 @@
+From bb8d4e195275e75bd94ebeffb53cc747d357fcf4 Mon Sep 17 00:00:00 2001
+From: Jeffrey 'Alex' Clark <aclark@aclark.net>
+Date: Thu, 8 Oct 2026 14:45:52 -0400
+Subject: [PATCH] PYTHON-6110 Bound Regex element scans to the remaining
+ document bytes (#3109)
+
+Co-authored-by: Steven Silvester <steve.silvester@mongodb.com>
+
+CVE: CVE-2026-106435
+Upstream-Status: Backport [https://github.com/mongodb/mongo-python-driver/commit/bb8d4e195275e75bd94ebeffb53cc747d357fcf4]
+
+(cherry picked from commit bb8d4e195275e75bd94ebeffb53cc747d357fcf4)
+Signed-off-by: Yogita Urade <yurade@cisco.com>
+---
+ bson/_cbsonmodule.c | 23 ++++++++++++++++-------
+ test/test_bson.py | 45 +++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 61 insertions(+), 7 deletions(-)
+
+diff --git a/bson/_cbsonmodule.c b/bson/_cbsonmodule.c
+index 7d184641..3f141040 100644
+--- a/bson/_cbsonmodule.c
++++ b/bson/_cbsonmodule.c
+@@ -2255,24 +2255,33 @@ static PyObject* get_value(PyObject* self, PyObject* name, const char* buffer,
+ PyObject* pattern;
+ int flags;
+ size_t flags_length, i;
+- size_t pattern_length = strlen(buffer + *position);
+- if (pattern_length > BSON_MAX_SIZE || max < pattern_length) {
++ /* PYTHON-6110: Search for each NUL within the bytes remaining
++ * in the document rather than scanning the buffer. */
++ const char* start = buffer + *position;
++ const char* pattern_nul = memchr(start, 0, max);
++ if (!pattern_nul) {
++ goto invalid;
++ }
++ size_t pattern_length = (size_t)(pattern_nul - start);
++ if (pattern_length > BSON_MAX_SIZE) {
+ goto invalid;
+ }
+ pattern = PyUnicode_DecodeUTF8(
+- buffer + *position, pattern_length,
++ start, pattern_length,
+ options->unicode_decode_error_handler);
+ if (!pattern) {
+ goto invalid;
+ }
+ *position += (unsigned)pattern_length + 1;
+- flags_length = strlen(buffer + *position);
+- if (flags_length > BSON_MAX_SIZE ||
+- (BSON_MAX_SIZE - pattern_length) < flags_length) {
++ start += pattern_length + 1;
++ const char* flags_nul = memchr(start, 0, max - pattern_length);
++ if (!flags_nul) {
+ Py_DECREF(pattern);
+ goto invalid;
+ }
+- if (max < pattern_length + flags_length) {
++ flags_length = (size_t)(flags_nul - start);
++ if (flags_length > BSON_MAX_SIZE ||
++ (BSON_MAX_SIZE - pattern_length) < flags_length) {
+ Py_DECREF(pattern);
+ goto invalid;
+ }
+diff --git a/test/test_bson.py b/test/test_bson.py
+index ffc02965..bc367bc2 100644
+--- a/test/test_bson.py
++++ b/test/test_bson.py
+@@ -401,6 +401,51 @@ class TestBSON(unittest.TestCase):
+ # Assert that the InvalidBSON error message is not empty.
+ self.assertTrue(str(ctx.exception))
+
++ def test_malformed_regex(self):
++ # PYTHON-6110: decoding a malformed Regex element must raise
++ # InvalidBSON on every accepted buffer type.
++ malformed = []
++ for pattern in ("", "x", "xx"):
++ doc = bytearray(encode({"a": Regex(pattern, "")}))
++ # Drop the trailing flags NUL and document EOO bytes, leaving
++ # the flags string unterminated after the pattern's NUL.
++ del doc[-2:]
++ # Keep the length prefix consistent with the shorter document.
++ doc[:4] = struct.pack("<i", len(doc))
++ malformed.append(bytes(doc))
++ for data in malformed:
++ with self.assertRaises(InvalidBSON):
++ decode(data)
++ with self.assertRaises(InvalidBSON):
++ decode(bytearray(data))
++ with self.assertRaises(InvalidBSON):
++ decode(memoryview(data))
++ with self.assertRaises(InvalidBSON):
++ decode(array.array("B", data))
++ with self.assertRaises(InvalidBSON):
++ list(decode_iter(data))
++ with self.assertRaises(InvalidBSON):
++ decode_all(data)
++ with mmap.mmap(-1, len(data)) as mm:
++ mm.write(data)
++ mm.seek(0)
++ with self.assertRaises(InvalidBSON):
++ decode(mm)
++ with self.assertRaises(InvalidBSON):
++ decode_all(mm)
++
++ def test_regex_empty_flags(self):
++ # PYTHON-6110: a valid regex with an empty flags string must
++ # continue to decode.
++ valid = encode({"a": Regex("b", "")})
++ expected = {"a": Regex("b", 0)}
++ self.assertEqual(decode(valid), expected)
++ with mmap.mmap(-1, len(valid)) as mm:
++ mm.write(valid)
++ mm.seek(0)
++ self.assertEqual(decode(mm), expected)
++ self.assertEqual(decode_all(mm), [expected])
++
+ def test_data_timestamp(self):
+ self.assertEqual(
+ {"test": Timestamp(4, 20)},
+--
+2.35.6
+
@@ -8,7 +8,10 @@ HOMEPAGE = "https://github.com/mongodb/mongo-python-driver"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://LICENSE;md5=86d3f3a95c324c9479bd8986968f4327"
-SRC_URI += "file://CVE-2026-88029.patch"
+SRC_URI += "file://CVE-2026-88029.patch \
+ file://CVE-2026-106435.patch \
+ file://CVE-2026-106435-regression.patch \
+ "
SRC_URI[sha256sum] = "8ba8405065f6e258a6f872fe62d797a28f383a12178c7153c01ed04e845c600c"
This patch applies the upstream fix as referenced in [3], using the commit shown in [1] and regression commit from [2]. Follow up the PYTHON-6110 CVE fix: its regex flags scan leaves a one-byte boundary error after consuming the pattern terminator. Apply PYTHON-6111's correction and terminator-overlap regression test. [1] https://github.com/mongodb/mongo-python-driver/commit/bb8d4e195275e75bd94ebeffb53cc747d357fcf4 [2] https://github.com/mongodb/mongo-python-driver/commit/b5a36ccbdb97298fbf9b5b5767affe92c534461f [3] https://jira.mongodb.org/browse/PYTHON-6110 [4] https://jira.mongodb.org/browse/PYTHON-6111 Signed-off-by: Yogita Urade <yurade@cisco.com> --- .../CVE-2026-106435-regression.patch | 92 +++++++++++++ .../python3-pymongo/CVE-2026-106435.patch | 122 ++++++++++++++++++ .../python/python3-pymongo_4.16.0.bb | 5 +- 3 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 meta-python/recipes-devtools/python/python3-pymongo/CVE-2026-106435-regression.patch create mode 100644 meta-python/recipes-devtools/python/python3-pymongo/CVE-2026-106435.patch