diff mbox series

[meta-python,scarthgap,2/2] python3-pymongo: Fix CVE-2026-106435

Message ID 20261105071424.2092617-2-yurade@cisco.com
State New
Headers show
Series [meta-python,scarthgap,1/2] python3-pymongo: Fix CVE-2026-88029 | expand

Commit Message

Yogita Urade Nov. 5, 2026, 7:14 a.m. UTC
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          |  79 +++++++++++
 .../python3-pymongo/CVE-2026-106435.patch     | 134 ++++++++++++++++++
 .../python/python3-pymongo_4.6.3.bb           |   2 +
 3 files changed, 215 insertions(+)
 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
diff mbox series

Patch

diff --git a/meta-python/recipes-devtools/python/python3-pymongo/CVE-2026-106435-regression.patch b/meta-python/recipes-devtools/python/python3-pymongo/CVE-2026-106435-regression.patch
new file mode 100644
index 0000000000..2f3e0ded99
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-pymongo/CVE-2026-106435-regression.patch
@@ -0,0 +1,79 @@ 
+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 file changes.
+
+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
+--- a/bson/_cbsonmodule.c
++++ b/bson/_cbsonmodule.c
+@@ -2102,5 +2102,8 @@
+     case 8:
+         {
++            if (max < 1) {
++                goto invalid;
++            }
+             char boolean_raw = buffer[(*position)++];
+             if (0 == boolean_raw) {
+                 value = Py_False;
+@@ -2290,7 +2290,9 @@
+             *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
+--- a/test/test_bson.py
++++ b/test/test_bson.py
+@@ -438,0 +439,34 @@ class TestBSON(unittest.TestCase):
++    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)
++
diff --git a/meta-python/recipes-devtools/python/python3-pymongo/CVE-2026-106435.patch b/meta-python/recipes-devtools/python/python3-pymongo/CVE-2026-106435.patch
new file mode 100644
index 0000000000..688c8fc5b6
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-pymongo/CVE-2026-106435.patch
@@ -0,0 +1,134 @@ 
+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]
+
+Backport Changes:
+- Added the `struct` import used to update the malformed-regex test's BSON
+  length prefix.
+- Adapted the C hunk to 4.6.3: the new flags-NUL failure path uses the
+  `Py_DECREF(pattern); goto invalid;` cleanup, and the existing flags-length
+  size/overflow check remains after the bounded length is computed. Upstream
+  also removes the `max < pattern_length + flags_length` check.
+
+Signed-off-by: Yogita Urade <yurade@cisco.com>
+---
+ bson/_cbsonmodule.c | 25 ++++++++++++++++++--------
+ test/test_bson.py   | 46 +++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 63 insertions(+), 8 deletions(-)
+
+diff --git a/bson/_cbsonmodule.c b/bson/_cbsonmodule.c
+index da86cd8..6868308 100644
+--- a/bson/_cbsonmodule.c
++++ b/bson/_cbsonmodule.c
+@@ -2270,18 +2270,31 @@ 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);
++            start += pattern_length + 1;
++            const char* flags_nul = memchr(start, 0, max - pattern_length);
++            if (!flags_nul) {
++                Py_DECREF(pattern);
++                goto invalid;
++            }
++            flags_length = (size_t)(flags_nul - start);
+             if (flags_length > BSON_MAX_SIZE ||
+                     (BSON_MAX_SIZE - pattern_length) < flags_length) {
+                 Py_DECREF(pattern);
+@@ -2288,10 +2301,6 @@
+                 goto invalid;
+             }
+-            if (max < pattern_length + flags_length) {
+-                Py_DECREF(pattern);
+-                goto invalid;
+-            }
+             flags = 0;
+             for (i = 0; i < flags_length; i++) {
+                 if (buffer[*position + i] == 'i') {
+                     flags |= 2;
+diff --git a/test/test_bson.py b/test/test_bson.py
+index 749c63b..483facf 100644
+--- a/test/test_bson.py
++++ b/test/test_bson.py
+@@ -23,6 +23,7 @@ import mmap
+ import os
+ import pickle
+ import re
++import struct
+ import sys
+ import tempfile
+ import uuid
+@@ -392,0 +393,45 @@ class TestBSON(unittest.TestCase):
++    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])
++
diff --git a/meta-python/recipes-devtools/python/python3-pymongo_4.6.3.bb b/meta-python/recipes-devtools/python/python3-pymongo_4.6.3.bb
index b2d4d72f85..005607f27f 100644
--- a/meta-python/recipes-devtools/python/python3-pymongo_4.6.3.bb
+++ b/meta-python/recipes-devtools/python/python3-pymongo_4.6.3.bb
@@ -9,6 +9,8 @@  LICENSE = "Apache-2.0"
 LIC_FILES_CHKSUM = "file://LICENSE;md5=86d3f3a95c324c9479bd8986968f4327"
 
 SRC_URI = "file://CVE-2026-88029.patch \
+           file://CVE-2026-106435.patch \
+           file://CVE-2026-106435-regression.patch \
           "
 
 SRC_URI[sha256sum] = "400074090b9a631f120b42c61b222fd743490c133a5d2f99c0208cefcccc964e"