Package: release.debian.org
Severity: normal
Tags: trixie
User: [email protected]
Usertags: pu
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
[ Reason ]
Address the following issues:
* Fix CVE-2026-6231: bson_validate may skip validation when processing
certain inputs
* Fix CVE-2026-4359: a compromised third party cloud server or
man-in-the-middle attacker could send a malformed HTTP response and cause
an application crash
* Fix: improve handling of corrupt GridFS files (upstream ticket:
https://jira.mongodb.org/browse/CDRIVER-6281)
* Fix CVE-2025-14911: user-controlled chunkSize metadata from lacks
appropriate validation allowing malformed GridFS metadata to overflow the
bounding container
* Fix CVE-2026-6691: Cyrus SASL integration performs unsafe string copying
during username canonicalization, enabling a heap buffer overflow before
any authentication or network traffic
[ Impact ]
Without these fixes, users and applications integrating mongo-c-driver
components may be vulnerable to potential security issues and data loss.
[ Tests ]
The affected/changed code went through upstream code reviews. Also,
accompanying unit tests were implemented and executed in upstream's
extensive CI environment.
[ Risks ]
Code changes are minimal (to the extend possible), extensively
reviewed/tested, and low risk. There are no work arounds.
[ Checklist ]
[x] *all* changes are documented in the d/changelog
[x] I reviewed all changes and I approve them
[x] attach debdiff against the package in (old)stable
[x] the issue is verified as fixed in unstable
[ Changes ]
Backport the following upstream changes:
https://github.com/mongodb/mongo-c-driver/commit/40b673787060a80ef7f4875ae776cb4731dadec9
https://github.com/mongodb/mongo-c-driver/commit/017e630199cd006e01288b241774168b7d2b8bdf
https://github.com/mongodb/mongo-c-driver/commit/754232f3cffe924346dcf327f4a723f1a0839420
https://github.com/mongodb/mongo-c-driver/commit/f70634e1d6d084f3b0f7077d03bde2d4cb95ce37
https://github.com/mongodb/mongo-c-driver/commit/ed8bed47906e37dd27306de0095ccbc56d6ec906
https://github.com/mongodb/mongo-c-driver/commit/bc7645f3a5dea3bd9c4c31d83713cb67978a57d9
https://github.com/mongodb/mongo-c-driver/commit/d2519ea8a403861782d9cde05c71c265bb568bbc
Note that as part of the backporting, I have pruned out the test-related
changes. This is both to reduce the size of the proposed change, as well
as since upstream runs the very extensive test suite in their own CI.
[ Other info ]
Debusine build:
https://debusine.debian.net/debian/developers/work-request/636736/
The only notable failure is the reverse-dep autopkgtest of rsyslog,
which appears to result from some possible new log messages that were
introduced. The failure is triggered by a logcheck run.
Because of the number of changes involved, I am awaiting approval prior
to uploading.
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCgAdFiEEIYZ1DR4ae5UL01q7ldFmTdL1kUIFAmnv/VEACgkQldFmTdL1
kULRxBAAkyL8F9E8exKPVEtCmgIfMHBbUb+YW/Ezv8LOezWQrrBlLTf54fmBupPm
RCivqn1WiZkpPKYSXgbxkqE9V7BsMOytc1WIv6I5TAW9lxzDc0wmsGi0OMMME8um
ms7UJa55DEESH4Ilq169RYWhovxS3jrH3qTALLoaKgJhQhpf/6ip1cIT0GVsJ90+
vq84PgmTQxYhDGo1CzMfmYmqWKYNzai2dPaVBh0oJTtgRCBcTZHR65R0VkhQG82f
x7kQIxBkgrytwhtqYUeIcIGzafqjGFDTDDgSMdmYhsCneYHDKXun9YwlcI0/+jQm
VeYzo/mwbl6XNJwgsOIQrLXM+Y/NDHUzAjQr61DYxt3XLI/PMJ8vr22WErQv7upS
c8nwrrRqAD68QxVmZ3RQhhaGsLuJWTYHpgC4J/BklHXfSMpZ0zG14GX05OTnQiWx
BFF6TOZRCXvRrmGZALrnn59BLK1Tf0XSzKfDoYomLicYnUENW9Tsfyw/nW0ae20X
btVCJZ6dXhrGSJWKdHkAf5QjUE3CiI+IiP+xfR9AhMpcz+QFVEh3tnTBKn2U/6Li
bFY/2gaIgspTucWV9bO2IYjQoOz7SZ+2zLLR8bsA8mLujVP4fpFHEsCTWIqUduCA
4jR7+XVaxKhODekdx3sWZD4R+Krqi+r/begvA1CZd/PM8wkJaos=
=jNnd
-----END PGP SIGNATURE-----
diff -Nru mongo-c-driver-1.30.4/debian/changelog
mongo-c-driver-1.30.4/debian/changelog
--- mongo-c-driver-1.30.4/debian/changelog 2025-12-18 14:50:07.000000000
-0500
+++ mongo-c-driver-1.30.4/debian/changelog 2026-04-27 11:48:27.000000000
-0400
@@ -1,3 +1,21 @@
+mongo-c-driver (1.30.4-1+deb13u2) trixie; urgency=medium
+
+ * Fix CVE-2026-6231: bson_validate may skip validation when processing
+ certain inputs
+ * Fix CVE-2026-4359: a compromised third party cloud server or
+ man-in-the-middle attacker could send a malformed HTTP response and cause
+ an application crash
+ * Fix: improve handling of corrupt GridFS files (upstream ticket:
+ https://jira.mongodb.org/browse/CDRIVER-6281)
+ * Fix CVE-2025-14911: user-controlled chunkSize metadata from lacks
+ appropriate validation allowing malformed GridFS metadata to overflow the
+ bounding container
+ * Fix CVE-2026-6691: Cyrus SASL integration performs unsafe string copying
+ during username canonicalization, enabling a heap buffer overflow before
+ any authentication or network traffic
+
+ -- Roberto C. Sanchez <[email protected]> Mon, 27 Apr 2026 11:48:27 -0400
+
mongo-c-driver (1.30.4-1+deb13u1) trixie; urgency=medium
* Fix CVE-2025-12119: mongoc_bulk_operation_t may read invalid memory if
diff -Nru mongo-c-driver-1.30.4/debian/patches/0002_CVE-2026-6231.patch
mongo-c-driver-1.30.4/debian/patches/0002_CVE-2026-6231.patch
--- mongo-c-driver-1.30.4/debian/patches/0002_CVE-2026-6231.patch
1969-12-31 19:00:00.000000000 -0500
+++ mongo-c-driver-1.30.4/debian/patches/0002_CVE-2026-6231.patch
2026-04-27 11:48:27.000000000 -0400
@@ -0,0 +1,1079 @@
+From 40b673787060a80ef7f4875ae776cb4731dadec9 Mon Sep 17 00:00:00 2001
+From: vector-of-bool <[email protected]>
+Date: Mon, 9 Jun 2025 10:05:58 -0600
+Subject: [PATCH] [CDRIVER-6017] BSON Validation Refactor (#2026) (Cherry-pick
+ for 1.30.x) (#2031)
+
+* [CDRIVER-6017] BSON Validation Refactor (#2026)
+
+* New BSON validation routine rewrite
+
+The new `bson_validate` implementation does not
+make use of the error-prone `bson_visit` APIs. Instead, it is written
+as a simple recursive validator. The new validator respects requests
+for UTF-8 validation properly.
+
+* Stop validating at 1000 depth, preventing stack overflow
+* Replace most BSON validation tests with generated ones
+
+The existing test cases used BSON files, and didn't have
+any commentary on what they were actually testing. New test cases are
+generated from a Python shorthand and contain the tested bytes inline,
+with a distinct test case for each actual validation scenario.
+
+* Disable UTF-8 validation by default on CRUD APIs
+* Document and tweak the value of BSON_VALIDATE_CORRUPT
+* Add test cases related to the overlong null encoding
+* Tweak JS scope validation to permit more obj keys
+* Add a NEWS entry for validation changes.
+* Allow `-private.h` headers to not include the prelude header
+
+---------
+
+Co-authored-by: Kevin Albertson <[email protected]>
+Co-authored-by: Ezra Chung <[email protected]>
+---
+ .evergreen/scripts/check-preludes.py | 6 +-
+ src/libbson/NEWS | 15 +
+ src/libbson/doc/bson_validate_flags_t.rst | 3 +
+ src/libbson/src/bson/bson-types.h | 47 +-
+ src/libbson/src/bson/bson.c | 239 +-
+ src/libbson/src/bson/validate-private.h | 37 +
+ src/libbson/src/bson/validate.c | 569 ++++
+ src/libbson/tests/test-bson.c | 496 +---
+ src/libbson/tests/test-validate.generated.c | 2511 ++++++++++++++++++
+ src/libbson/tests/validate-tests.py | 1479 +++++++++++
+ src/libmongoc/CMakeLists.txt | 1 +
+ src/libmongoc/src/mongoc/mongoc-util.c | 21 +-
+ src/libmongoc/tests/test-libmongoc-main.c | 1 +
+ src/libmongoc/tests/test-mongoc-bulk.c | 22 +-
+ src/libmongoc/tests/test-mongoc-collection.c | 8 +-
+ src/libmongoc/tests/test-mongoc-cursor.c | 10 +-
+ 16 files changed, 4764 insertions(+), 701 deletions(-)
+ create mode 100644 src/libbson/src/bson/validate-private.h
+ create mode 100644 src/libbson/src/bson/validate.c
+ create mode 100644 src/libbson/tests/test-validate.generated.c
+ create mode 100644 src/libbson/tests/validate-tests.py
+
+diff --git a/src/libbson/doc/bson_validate_flags_t.rst
b/src/libbson/doc/bson_validate_flags_t.rst
+index 85140f0310..13607818f0 100644
+--- a/src/libbson/doc/bson_validate_flags_t.rst
++++ b/src/libbson/doc/bson_validate_flags_t.rst
+@@ -19,6 +19,7 @@ Synopsis
+ BSON_VALIDATE_DOT_KEYS = (1 << 2),
+ BSON_VALIDATE_UTF8_ALLOW_NULL = (1 << 3),
+ BSON_VALIDATE_EMPTY_KEYS = (1 << 4),
++ BSON_VALIDATE_CORRUPT = (1 << 5),
+ } bson_validate_flags_t;
+
+ Description
+@@ -40,6 +41,8 @@ Each defined flag aside from ``BSON_VALIDATE_NONE``
describes an optional valida
+ * ``BSON_VALIDATE_DOLLAR_KEYS`` Prohibit keys that start with ``$`` outside
of a "DBRef" subdocument.
+ * ``BSON_VALIDATE_DOT_KEYS`` Prohibit keys that contain ``.`` anywhere in the
string.
+ * ``BSON_VALIDATE_EMPTY_KEYS`` Prohibit zero-length keys.
++* ``BSON_VALIDATE_CORRUPT`` is not a control flag, but is used as an error
code
++ when a validation routine encounters corrupt BSON data.
+
+ .. seealso::
+
+diff --git a/src/libbson/src/bson/bson-types.h
b/src/libbson/src/bson/bson-types.h
+index ad8ba743d8..3a4fcea61a 100644
+--- a/src/libbson/src/bson/bson-types.h
++++ b/src/libbson/src/bson/bson-types.h
+@@ -204,25 +204,54 @@ typedef struct {
+
+
+ /**
+- * bson_validate_flags_t:
++ * @brief Flags and error codes for BSON validation functions.
+ *
+- * This enumeration is used for validation of BSON documents. It allows
+- * selective control on what you wish to validate.
++ * Pass these flags bits to control the behavior of the `bson_validate` family
++ * of functions.
+ *
+- * %BSON_VALIDATE_NONE: No additional validation occurs.
+- * %BSON_VALIDATE_UTF8: Check that strings are valid UTF-8.
+- * %BSON_VALIDATE_DOLLAR_KEYS: Check that keys do not start with $.
+- * %BSON_VALIDATE_DOT_KEYS: Check that keys do not contain a period.
+- * %BSON_VALIDATE_UTF8_ALLOW_NULL: Allow NUL bytes in UTF-8 text.
+- * %BSON_VALIDATE_EMPTY_KEYS: Prohibit zero-length field names
++ * Additionally, if validation fails, then the error code set on a
`bson_error_t`
++ * will have the value corresponding to the reason that validation failed.
+ */
+ typedef enum {
++ /**
++ * @brief No special validation behavior specified.
++ */
+ BSON_VALIDATE_NONE = 0,
++ /**
++ * @brief Check that all text components of the BSON data are valid UTF-8.
++ *
++ * Note that this will also cause validation to reject valid text that
contains
++ * a null character. This can be changed by also passing
++ * `BSON_VALIDATE_UTF8_ALLOW_NULL`
++ */
+ BSON_VALIDATE_UTF8 = (1 << 0),
++ /**
++ * @brief Check that element keys do not begin with an ASCII dollar `$`
++ */
+ BSON_VALIDATE_DOLLAR_KEYS = (1 << 1),
++ /**
++ * @brief Check that element keys do not contain an ASCII period `.`
++ */
+ BSON_VALIDATE_DOT_KEYS = (1 << 2),
++ /**
++ * @brief If set then it is *not* an error for a UTF-8 string to contain
++ * embedded null characters.
++ *
++ * This has no effect unless `BSON_VALIDATE_UTF8` is also passed.
++ */
+ BSON_VALIDATE_UTF8_ALLOW_NULL = (1 << 3),
++ /**
++ * @brief Check that no element key is a zero-length empty string.
++ */
+ BSON_VALIDATE_EMPTY_KEYS = (1 << 4),
++ /**
++ * @brief This is not a flag that controls behavior, but is instead used
to indicate
++ * that a BSON document is corrupted in some way. This is the value that
will
++ * appear as an error code.
++ *
++ * Passing this as a flag has no effect.
++ */
++ BSON_VALIDATE_CORRUPT = (1 << 5),
+ } bson_validate_flags_t;
+
+
+diff --git a/src/libbson/src/bson/bson.c b/src/libbson/src/bson/bson.c
+index bb6dbcfa5c..727994626b 100644
+--- a/src/libbson/src/bson/bson.c
++++ b/src/libbson/src/bson/bson.c
+@@ -15,6 +15,7 @@
+ */
+
+
++#include <bson/validate-private.h>
+ #include <bson/bson.h>
+ #include <bson/bson-config.h>
+ #include <bson/bson-private.h>
+@@ -34,29 +35,6 @@
+ #endif
+
+
+-typedef enum {
+- BSON_VALIDATE_PHASE_START,
+- BSON_VALIDATE_PHASE_TOP,
+- BSON_VALIDATE_PHASE_LF_REF_KEY,
+- BSON_VALIDATE_PHASE_LF_REF_UTF8,
+- BSON_VALIDATE_PHASE_LF_ID_KEY,
+- BSON_VALIDATE_PHASE_LF_DB_KEY,
+- BSON_VALIDATE_PHASE_LF_DB_UTF8,
+- BSON_VALIDATE_PHASE_NOT_DBREF,
+-} bson_validate_phase_t;
+-
+-
+-/*
+- * Structures.
+- */
+-typedef struct {
+- bson_validate_flags_t flags;
+- ssize_t err_offset;
+- bson_validate_phase_t phase;
+- bson_error_t error;
+-} bson_validate_state_t;
+-
+-
+ /*
+ * Globals.
+ */
+@@ -2489,196 +2467,6 @@ bson_array_as_canonical_extended_json (const bson_t
*bson, size_t *length)
+ }
+
+
+-#define VALIDATION_ERR(_flag, _msg, ...) bson_set_error (&state->error,
BSON_ERROR_INVALID, _flag, _msg, __VA_ARGS__)
+-
+-static bool
+-_bson_iter_validate_utf8 (const bson_iter_t *iter, const char *key, size_t
v_utf8_len, const char *v_utf8, void *data)
+-{
+- bson_validate_state_t *state = data;
+- bool allow_null;
+-
+- if ((state->flags & BSON_VALIDATE_UTF8)) {
+- allow_null = !!(state->flags & BSON_VALIDATE_UTF8_ALLOW_NULL);
+-
+- if (!bson_utf8_validate (v_utf8, v_utf8_len, allow_null)) {
+- state->err_offset = iter->off;
+- VALIDATION_ERR (BSON_VALIDATE_UTF8, "invalid utf8 string for key
\"%s\"", key);
+- return true;
+- }
+- }
+-
+- if ((state->flags & BSON_VALIDATE_DOLLAR_KEYS)) {
+- if (state->phase == BSON_VALIDATE_PHASE_LF_REF_UTF8) {
+- state->phase = BSON_VALIDATE_PHASE_LF_ID_KEY;
+- } else if (state->phase == BSON_VALIDATE_PHASE_LF_DB_UTF8) {
+- state->phase = BSON_VALIDATE_PHASE_NOT_DBREF;
+- }
+- }
+-
+- return false;
+-}
+-
+-
+-static void
+-_bson_iter_validate_corrupt (const bson_iter_t *iter, void *data)
+-{
+- bson_validate_state_t *state = data;
+-
+- state->err_offset = iter->err_off;
+- VALIDATION_ERR (BSON_VALIDATE_NONE, "%s", "corrupt BSON");
+-}
+-
+-
+-static bool
+-_bson_iter_validate_before (const bson_iter_t *iter, const char *key, void
*data)
+-{
+- bson_validate_state_t *state = data;
+-
+- if ((state->flags & BSON_VALIDATE_EMPTY_KEYS)) {
+- if (key[0] == '\0') {
+- state->err_offset = iter->off;
+- VALIDATION_ERR (BSON_VALIDATE_EMPTY_KEYS, "%s", "empty key");
+- return true;
+- }
+- }
+-
+- if ((state->flags & BSON_VALIDATE_DOLLAR_KEYS)) {
+- if (key[0] == '$') {
+- if (state->phase == BSON_VALIDATE_PHASE_LF_REF_KEY && strcmp (key,
"$ref") == 0) {
+- state->phase = BSON_VALIDATE_PHASE_LF_REF_UTF8;
+- } else if (state->phase == BSON_VALIDATE_PHASE_LF_ID_KEY && strcmp
(key, "$id") == 0) {
+- state->phase = BSON_VALIDATE_PHASE_LF_DB_KEY;
+- } else if (state->phase == BSON_VALIDATE_PHASE_LF_DB_KEY && strcmp
(key, "$db") == 0) {
+- state->phase = BSON_VALIDATE_PHASE_LF_DB_UTF8;
+- } else {
+- state->err_offset = iter->off;
+- VALIDATION_ERR (BSON_VALIDATE_DOLLAR_KEYS, "keys cannot begin
with \"$\": \"%s\"", key);
+- return true;
+- }
+- } else if (state->phase == BSON_VALIDATE_PHASE_LF_ID_KEY ||
state->phase == BSON_VALIDATE_PHASE_LF_REF_UTF8 ||
+- state->phase == BSON_VALIDATE_PHASE_LF_DB_UTF8) {
+- state->err_offset = iter->off;
+- VALIDATION_ERR (BSON_VALIDATE_DOLLAR_KEYS, "invalid key within DBRef
subdocument: \"%s\"", key);
+- return true;
+- } else {
+- state->phase = BSON_VALIDATE_PHASE_NOT_DBREF;
+- }
+- }
+-
+- if ((state->flags & BSON_VALIDATE_DOT_KEYS)) {
+- if (strstr (key, ".")) {
+- state->err_offset = iter->off;
+- VALIDATION_ERR (BSON_VALIDATE_DOT_KEYS, "keys cannot contain \".\":
\"%s\"", key);
+- return true;
+- }
+- }
+-
+- return false;
+-}
+-
+-
+-static bool
+-_bson_iter_validate_codewscope (
+- const bson_iter_t *iter, const char *key, size_t v_code_len, const char
*v_code, const bson_t *v_scope, void *data)
+-{
+- bson_validate_state_t *state = data;
+- size_t offset = 0;
+-
+- BSON_UNUSED (key);
+- BSON_UNUSED (v_code_len);
+- BSON_UNUSED (v_code);
+-
+- if (!bson_validate (v_scope, state->flags, &offset)) {
+- state->err_offset = iter->off + offset;
+- VALIDATION_ERR (BSON_VALIDATE_NONE, "%s", "corrupt code-with-scope");
+- return false;
+- }
+-
+- return true;
+-}
+-
+-
+-static bool
+-_bson_iter_validate_document (const bson_iter_t *iter, const char *key, const
bson_t *v_document, void *data);
+-
+-
+-static const bson_visitor_t bson_validate_funcs = {
+- _bson_iter_validate_before,
+- NULL, /* visit_after */
+- _bson_iter_validate_corrupt,
+- NULL, /* visit_double */
+- _bson_iter_validate_utf8,
+- _bson_iter_validate_document,
+- _bson_iter_validate_document, /* visit_array */
+- NULL, /* visit_binary */
+- NULL, /* visit_undefined */
+- NULL, /* visit_oid */
+- NULL, /* visit_bool */
+- NULL, /* visit_date_time */
+- NULL, /* visit_null */
+- NULL, /* visit_regex */
+- NULL, /* visit_dbpoint */
+- NULL, /* visit_code */
+- NULL, /* visit_symbol */
+- _bson_iter_validate_codewscope,
+-};
+-
+-
+-static bool
+-_bson_iter_validate_document (const bson_iter_t *iter, const char *key, const
bson_t *v_document, void *data)
+-{
+- bson_validate_state_t *state = data;
+- bson_iter_t child;
+- bson_validate_phase_t phase = state->phase;
+-
+- BSON_UNUSED (key);
+-
+- if (!bson_iter_init (&child, v_document)) {
+- state->err_offset = iter->off;
+- return true;
+- }
+-
+- if (state->phase == BSON_VALIDATE_PHASE_START) {
+- state->phase = BSON_VALIDATE_PHASE_TOP;
+- } else {
+- state->phase = BSON_VALIDATE_PHASE_LF_REF_KEY;
+- }
+-
+- (void) bson_iter_visit_all (&child, &bson_validate_funcs, state);
+-
+- if (state->phase == BSON_VALIDATE_PHASE_LF_ID_KEY || state->phase ==
BSON_VALIDATE_PHASE_LF_REF_UTF8 ||
+- state->phase == BSON_VALIDATE_PHASE_LF_DB_UTF8) {
+- if (state->err_offset <= 0) {
+- state->err_offset = iter->off;
+- }
+-
+- return true;
+- }
+-
+- state->phase = phase;
+-
+- return false;
+-}
+-
+-
+-static void
+-_bson_validate_internal (const bson_t *bson, bson_validate_state_t *state)
+-{
+- bson_iter_t iter;
+-
+- state->err_offset = -1;
+- state->phase = BSON_VALIDATE_PHASE_START;
+- memset (&state->error, 0, sizeof state->error);
+-
+- if (!bson_iter_init (&iter, bson)) {
+- state->err_offset = 0;
+- VALIDATION_ERR (BSON_VALIDATE_NONE, "%s", "corrupt BSON");
+- } else {
+- _bson_iter_validate_document (&iter, NULL, bson, state);
+- }
+-}
+-
+-
+ bool
+ bson_validate (const bson_t *bson, bson_validate_flags_t flags, size_t
*offset)
+ {
+@@ -2692,29 +2480,26 @@ bson_validate_with_error (const bson_t *bson,
bson_validate_flags_t flags, bson_
+ return bson_validate_with_error_and_offset (bson, flags, NULL, error);
+ }
+
+-
+ bool
+ bson_validate_with_error_and_offset (const bson_t *bson,
+ bson_validate_flags_t flags,
+ size_t *offset,
+ bson_error_t *error)
+ {
+- bson_validate_state_t state;
+-
+- state.flags = flags;
+- _bson_validate_internal (bson, &state);
++ BSON_ASSERT_PARAM (bson);
++ BSON_OPTIONAL_PARAM (offset);
++ BSON_OPTIONAL_PARAM (error);
+
+- if (state.err_offset >= 0) {
+- if (offset) {
+- *offset = (size_t) state.err_offset;
+- }
+- if (error) {
+- memcpy (error, &state.error, sizeof *error);
+- }
+- return false;
++ size_t offset_local = 0;
++ if (!offset) {
++ offset = &offset_local;
++ }
++ bson_error_t error_local;
++ if (!error) {
++ error = &error_local;
+ }
+
+- return true;
++ return _bson_validate_impl_v2 (bson, flags, offset, error);
+ }
+
+
+diff --git a/src/libbson/src/bson/validate-private.h
b/src/libbson/src/bson/validate-private.h
+new file mode 100644
+index 0000000000..8774a1e653
+--- /dev/null
++++ b/src/libbson/src/bson/validate-private.h
+@@ -0,0 +1,37 @@
++#ifndef BSON_VALIDATE_PRIVATE_H_INCLUDED
++#define BSON_VALIDATE_PRIVATE_H_INCLUDED
++
++#include <bson/bson-types.h>
++
++enum {
++ /**
++ * @brief This compile-time constant represents the maximum document
nesting
++ * depth permitted by the `bson_validate` family of functions. If the
nesting
++ * depth exceeds this limit, the data will be rejected.
++ *
++ * This limit is intentionally larger than the default limit of MongoDB
++ * server, since we cannot anticipate what a libbson user might actually
want
++ * to do with BSON, and to prevent accidentally rejecting data that the
++ * server might accept. The main purpose of this limit is to prevent stack
++ * overflow, not to reject invalid data.
++ */
++ BSON_VALIDATION_MAX_NESTING_DEPTH = 1000,
++};
++
++/**
++ * @brief Private function backing the implementation of validation.
++ *
++ * Validation was previously defined in the overburdened `bson-iter.c`, but it
++ * is now defined in its own file.
++ *
++ * @param bson The document to validate. Must be non-null.
++ * @param flags Validation control flags
++ * @param offset Receives the offset at which validation failed. Must be
non-null.
++ * @param error Receives the error describing why validation failed. Must be
non-null.
++ * @return true If the given document has no validation errors
++ * @return false Otherwise
++ */
++bool
++_bson_validate_impl_v2 (const bson_t *bson, bson_validate_flags_t flags,
size_t *offset, bson_error_t *error);
++
++#endif // BSON_VALIDATE_PRIVATE_H_INCLUDED
+diff --git a/src/libbson/src/bson/validate.c b/src/libbson/src/bson/validate.c
+new file mode 100644
+index 0000000000..10402df0c0
+--- /dev/null
++++ b/src/libbson/src/bson/validate.c
+@@ -0,0 +1,569 @@
++/**
++ * @file bson/validate.c
++ * @brief Implementation of BSON document validation
++ * @date 2025-05-28
++ *
++ * This file implements the backend for the `bson_validate` family of
functions.
++ *
++ * The `_validate_...` functions all accept `validator* self` as their first
parameter,
++ * and must `return false` AND set `self->error` if-and-only-if they
encounter a validation error.
++ * If a function returns true, it is assumed that validation of that item
succeeded.
++ *
++ * For brevity, the `require...` macros are defined, which check conditions,
set errors,
++ * and `return false` inline.
++ *
++ * @copyright Copyright 2009-present MongoDB, Inc.
++ *
++ * Licensed under the Apache License, Version 2.0 (the "License");
++ * you may not use this file except in compliance with the License.
++ * You may obtain a copy of the License at
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++
++#include <bson/validate-private.h>
++#include <bson/bson.h>
++
++#include <stdbool.h>
++#include <string.h>
++
++/**
++ * @brief User parameters for validation behavior. These correspond to the
various
++ * flags that can be given when the user requests validation
++ */
++typedef struct {
++ /**
++ * @brief Should we allow invalid UTF-8 in string components?
++ *
++ * This affects the behavior of validation of key strings and string-like
++ * elements that require UTF-8 encoding.
++ *
++ * Technically invalid UTF-8 is invalid in BSON, but applications may
already
++ * rely on this being accepted.
++ */
++ bool allow_invalid_utf8;
++ /**
++ * @brief Should we allow a zero-valued codepoint in text?
++ *
++ * Unicode U+0000 is a valid codepoint, but a lot of software doesn't like
++ * it and handles it poorly. By default, we reject it, but the user may
++ * want to allow it.
++ *
++ * Note that because element keys rely on null termination, element keys
++ * cannot contain U+0000 by construction.
++ */
++ bool allow_null_in_utf8;
++ /// Should we allow element key strings to be empty strings?
++ bool allow_empty_keys;
++ /// Should we allow ASCII dot "." in element key strings?
++ bool allow_dot_in_keys;
++ /**
++ * @brief Check for special element keys that begin with an ASCII dollar
"$"
++ *
++ * By default, we ignore them and treat them as regular elements. If this
is
++ * enabled, we reject key strings that start with a dollar, unless it is a
++ * special extended JSON DBRef document.
++ *
++ * This also enables DBRef validation, which checks the structure of a
document
++ * whose first key is "$ref".
++ */
++ bool check_special_dollar_keys;
++} validation_params;
++
++/**
++ * @brief State for a validator.
++ */
++typedef struct {
++ /// The parameters that control validation behavior
++ const validation_params *params;
++ /// Error storage that is updated if any validation encounters an error
++ bson_error_t error;
++ /// The zero-based index of the byte where validation stopped in case of
an error.
++ size_t error_offset;
++} validator;
++
++// Undef these macros, if they are defined.
++#ifdef require_with_error
++#undef require_with_error
++#endif
++#ifdef require
++#undef require
++#endif
++#ifdef require_advance
++#undef require_advance
++#endif
++
++/**
++ * @brief Check that the given condition is satisfied, or set an error and
return `false`
++ *
++ * @param Condition The condition that should evaluate to `true`
++ * @param Offset The byte offset where an error should be indicated.
++ * @param Code The error code that should be set if the condition fails
++ * @param ... The error string and format arguments to be used in the error
message
++ *
++ * This macro assumes a `validator* self` is in scope. This macro will
evaluate `return false`
++ * if the given condition is not true.
++ */
++#define require_with_error(Condition, Offset, Code, ...) \
++ if (!(Condition)) { \
++ self->error_offset = (Offset); \
++ bson_set_error (&self->error, BSON_ERROR_INVALID, Code, __VA_ARGS__); \
++ return false; \
++ } else \
++ ((void) 0)
++
++/**
++ * @brief Check that the given condition is satisfied, or `return false`
immediately.
++ *
++ * This macro does not modify the validator state. It only does an
early-return.
++ */
++#define require(Cond) \
++ if (!(Cond)) { \
++ return false; \
++ } else \
++ ((void) 0)
++
++/**
++ * @brief Advance the pointed-to iterator, check for errors, and test whether
we are done.
++ *
++ * @param DoneVar An l-value of type `bool` that is set to `true` if the
iterator hit the end of
++ * the document, otherwise `false`
++ * @param IteratorPointer An expression of type `bson_iter_t*`, which will be
advanced.
++ *
++ * If advancing the iterator results in a decoding error, then this macro
sets an error
++ * on the `validator* self` that is in scope and will immediately `return
false`.
++ */
++#define require_advance(DoneVar, IteratorPointer)
\
++ if ((DoneVar = !bson_iter_next (IteratorPointer))) {
\
++ /* The iterator indicates that it stopped */
\
++ if ((IteratorPointer)->err_off) {
\
++ /* The iterator stopped because of a decoding error */
\
++ require_with_error (false, (IteratorPointer)->err_off,
BSON_VALIDATE_CORRUPT, "corrupt BSON"); \
++ }
\
++ } else
\
++ ((void) 0)
++
++// Test if the element's key is equal to the given string
++static bool
++_key_is (bson_iter_t const *iter, const char *const key)
++{
++ BSON_ASSERT_PARAM (iter);
++ BSON_ASSERT_PARAM (key);
++ return !strcmp (bson_iter_key (iter), key);
++}
++
++/**
++ * @brief Validate a document or array object, recursively.
++ *
++ * @param self The validator which will be updated and used to do the
validation
++ * @param bson The object to be validated
++ * @param depth The validation depth. We indicate an error if this exceeds a
limit.
++ * @return true If the object is valid
++ * @return false Otherwise
++ */
++static bool
++_validate_doc (validator *self, const bson_t *bson, int depth);
++
++/**
++ * @brief Validate a UTF-8 string, if-and-only-if UTF-8 validation is
requested
++ *
++ * @param self Pointer to the validator object
++ * @param offset The byte-offset of the string, used to set the error offset
++ * @param u8 Pointer to the first byte in a UTF-8 string
++ * @param u8len The length of the array pointed-to by `u8`
++ * @return true If the UTF-8 string is valid, or if UTF-8 validation is
disabled
++ * @return false If UTF-8 validation is requested, AND (the UTF-8 string is
invalid OR (UTF-8 strings should not contain
++ * null characters and the UTF-8 string contains a null character))
++ */
++static bool
++_maybe_validate_utf8 (validator *self, size_t offset, const char *u8, size_t
u8len)
++{
++ BSON_ASSERT_PARAM (self);
++ BSON_ASSERT_PARAM (u8);
++ if (self->params->allow_invalid_utf8) {
++ // We are not doing UTF-8 checks, so always succeed
++ return true;
++ }
++ // Validate UTF-8
++ const bool u8okay = bson_utf8_validate (u8, u8len,
self->params->allow_null_in_utf8);
++ if (u8okay) {
++ // Valid UTF-8, no more checks
++ return true;
++ }
++ // Validation error. It may be invalid UTF-8, or it could be valid UTF-8
with a disallowed null
++ if (!self->params->allow_null_in_utf8) {
++ // We are disallowing null in UTF-8. Check whether it is invalid UTF-8,
or is
++ // valid UTF-8 with a null character
++ const bool u8okay_with_null = bson_utf8_validate (u8, u8len, true);
++ if (u8okay_with_null) {
++ // The UTF-8 is valid, but contains a null character.
++ require_with_error (
++ false, offset, BSON_VALIDATE_UTF8_ALLOW_NULL, "UTF-8 string
contains a U+0000 (null) character");
++ }
++ }
++ // The UTF-8 is invalid, regardless of whether it contains a null character
++ require_with_error (false, offset, BSON_VALIDATE_UTF8, "Text element is
not valid UTF-8");
++}
++
++// Same as `_maybe_validate_u8`, but relies on a null-terminated C string to
get the string length
++static bool
++_maybe_validate_utf8_cstring (validator *self, size_t offset, const char
*const u8)
++{
++ BSON_ASSERT_PARAM (self);
++ BSON_ASSERT_PARAM (u8);
++ return _maybe_validate_utf8 (self, offset, u8, strlen (u8));
++}
++
++/**
++ * @brief Validate a string-like element (UTF-8, Symbol, or Code)
++ *
++ * This function relies on the representation of the text-like elements within
++ * the iterator struct to reduce code dup around text validation.
++ */
++static bool
++_validate_stringlike_element (validator *self, bson_iter_t const *iter)
++{
++ BSON_ASSERT_PARAM (self);
++ BSON_ASSERT_PARAM (iter);
++ // iter->d1 is the offset to the string header. Subtract 1 to exclude the
null terminator
++ uint32_t u8len;
++ memcpy (&u8len, iter->raw + iter->d1, sizeof u8len);
++ u8len = BSON_UINT32_FROM_LE (u8len);
++ u8len -= 1;
++ // iter->d2 is the offset to the first byte of the string
++ const char *u8 = (const char *) iter->raw + iter->d2;
++ return _maybe_validate_utf8 (self, iter->off, u8, u8len);
++}
++
++static bool
++_validate_regex_elem (validator *self, bson_iter_t const *iter)
++{
++ BSON_ASSERT_PARAM (self);
++ BSON_ASSERT_PARAM (iter);
++ BSON_ASSERT (BSON_ITER_HOLDS_REGEX (iter));
++ const char *opts;
++ const char *const rx = bson_iter_regex (iter, &opts);
++ BSON_ASSERT (rx);
++ BSON_ASSERT (opts);
++ return _maybe_validate_utf8_cstring (self, iter->off, rx) //
++ && _maybe_validate_utf8_cstring (self, iter->off, opts);
++}
++
++static bool
++_validate_codewscope_elem (validator *self, bson_iter_t const *iter, int
depth)
++{
++ BSON_ASSERT_PARAM (self);
++ BSON_ASSERT_PARAM (iter);
++ BSON_ASSERT (BSON_ITER_HOLDS_CODEWSCOPE (iter));
++ // Extract the code and the scope object
++ uint8_t const *doc;
++ uint32_t doc_len;
++ uint32_t u8len;
++ const char *const u8 = bson_iter_codewscope (iter, &u8len, &doc_len, &doc);
++ bson_t scope;
++ require_with_error (
++ bson_init_static (&scope, doc, doc_len), iter->off,
BSON_VALIDATE_CORRUPT, "corrupt scope document");
++
++ // Validate the code string
++ require (_maybe_validate_utf8 (self, iter->off, u8, u8len));
++
++ // Now we validate the scope object.
++ // Don't validate the scope document using the parent parameters, because
it should
++ // be treated as an opaque closure of JS variables.
++ validation_params const scope_params = {
++ // JS object keys can contain dots
++ .allow_dot_in_keys = true,
++ // JS object keys can be empty
++ .allow_empty_keys = true,
++ // JS strings can contain null bytes
++ .allow_null_in_utf8 = true,
++ // JS strings need to encode properly
++ .allow_invalid_utf8 = false,
++ // JS allows object keys to have dollars
++ .check_special_dollar_keys = false,
++ };
++ validator scope_validator = {.params = &scope_params};
++ // We could do more validation that the scope keys are valid JS
identifiers,
++ // but that would require using a full Unicode database.
++ if (_validate_doc (&scope_validator, &scope, depth)) {
++ // No error
++ return true;
++ }
++ // Validation error. Copy the error message, adding the name of the bad
element
++ bson_set_error (&self->error,
++ scope_validator.error.domain,
++ scope_validator.error.code,
++ "Error in scope document for element \"%s\": %s",
++ bson_iter_key (iter),
++ scope_validator.error.message);
++ // Adjust the error offset by the offset of the iterator
++ self->error_offset = scope_validator.error_offset + iter->off;
++ return false;
++}
++
++// Validate an element's key string according to the validation rules
++static bool
++_validate_element_key (validator *self, bson_iter_t const *iter)
++{
++ BSON_ASSERT_PARAM (self);
++ BSON_ASSERT_PARAM (iter);
++
++ const char *const key = bson_iter_key (iter);
++ BSON_ASSERT (key);
++ const size_t key_len = bson_iter_key_len (iter);
++
++ // Check the UTF-8 of the key
++ require (_maybe_validate_utf8 (self, iter->off, key, key_len));
++
++ // Check for special keys
++ if (self->params->check_special_dollar_keys) {
++ // dollar-keys are checked during the startup of _validate_doc. If we
get here, there's a problem.
++ require_with_error (
++ key[0] != '$', iter->off, BSON_VALIDATE_DOLLAR_KEYS, "Disallowed '$'
in element key: \"%s\"", key);
++ }
++
++ if (!self->params->allow_empty_keys) {
++ require_with_error (key_len != 0, iter->off, BSON_VALIDATE_EMPTY_KEYS,
"Element key cannot be an empty string");
++ }
++
++ if (!self->params->allow_dot_in_keys) {
++ require_with_error (
++ !strstr (key, "."), iter->off, BSON_VALIDATE_DOT_KEYS, "Disallowed
'.' in element key: \"%s\"", key);
++ }
++
++ return true;
++}
++
++// Extract a document referred-to by the given iterator. It must point to a
++// document or array element. Returns `false` if `bson_init_static` returns
false
++static bool
++_get_subdocument (bson_t *subdoc, bson_iter_t const *iter)
++{
++ BSON_ASSERT_PARAM (subdoc);
++ BSON_ASSERT_PARAM (iter);
++ uint32_t len;
++ memcpy (&len, iter->raw + iter->d1, sizeof len);
++ len = BSON_UINT32_FROM_LE (len);
++ uint8_t const *data = (uint8_t const *) iter->raw + iter->d1;
++ return bson_init_static (subdoc, data, len);
++}
++
++// Validate the value of an element, without checking its key
++static bool
++_validate_element_value (validator *self, bson_iter_t const *iter, int depth)
++{
++ BSON_ASSERT_PARAM (self);
++ BSON_ASSERT_PARAM (iter);
++
++ const bson_type_t type = bson_iter_type (iter);
++ switch (type) {
++ default:
++ case BSON_TYPE_EOD:
++ BSON_UNREACHABLE ("Validation execution encountered an element of type
0x0, but this should not happen as tag "
++ "validation is handled before we get to this point.");
++ case BSON_TYPE_DOUBLE:
++ case BSON_TYPE_NULL:
++ case BSON_TYPE_OID:
++ case BSON_TYPE_INT32:
++ case BSON_TYPE_INT64:
++ case BSON_TYPE_MINKEY:
++ case BSON_TYPE_MAXKEY:
++ case BSON_TYPE_TIMESTAMP:
++ case BSON_TYPE_UNDEFINED:
++ case BSON_TYPE_DECIMAL128:
++ case BSON_TYPE_DATE_TIME:
++ case BSON_TYPE_BOOL:
++ // No validation on these simple scalar elements. `bson_iter_next` does
validation
++ // on these objects for us.
++ return true;
++ case BSON_TYPE_BINARY:
++ // Note: BSON binary validation is handled by bson_iter_next, which
checks the
++ // internal structure properly. If we get here, then the binary data is
okay.
++ return true;
++ case BSON_TYPE_DBPOINTER:
++ // DBPointer contains more than just a string, but we only need to
validate
++ // the string component, which happens to align with the repr of other
stringlike
++ // elements. bson_iter_next will do the validation on the element's
size.
++ //! fallthrough
++ case BSON_TYPE_SYMBOL:
++ case BSON_TYPE_CODE:
++ case BSON_TYPE_UTF8:
++ return _validate_stringlike_element (self, iter);
++ case BSON_TYPE_DOCUMENT:
++ case BSON_TYPE_ARRAY: {
++ bson_t doc;
++ require_with_error (_get_subdocument (&doc, iter), iter->off,
BSON_VALIDATE_CORRUPT, "corrupt BSON");
++ if (_validate_doc (self, &doc, depth)) {
++ // No error
++ return true;
++ }
++ // Error in subdocument. Adjust the error offset for the current
iterator position,
++ // plus the key length, plus 2 for the tag and key's null terminator.
++ self->error_offset += iter->off + bson_iter_key_len (iter) + 2;
++ return false;
++ }
++
++ case BSON_TYPE_REGEX:
++ return _validate_regex_elem (self, iter);
++ case BSON_TYPE_CODEWSCOPE:
++ return _validate_codewscope_elem (self, iter, depth);
++ }
++}
++
++// Validate a single BSON element referred-to by the given iterator
++static bool
++_validate_element (validator *self, bson_iter_t *iter, int depth)
++{
++ BSON_ASSERT_PARAM (self);
++ BSON_ASSERT_PARAM (iter);
++ return _validate_element_key (self, iter) && _validate_element_value
(self, iter, depth);
++}
++
++/**
++ * @brief Validate the elements of a document, beginning with the element
pointed-to
++ * by the given iterator.
++ */
++static bool
++_validate_remaining_elements (validator *self, bson_iter_t *iter, int depth)
++{
++ BSON_ASSERT_PARAM (self);
++ BSON_ASSERT_PARAM (iter);
++ bool done = false;
++ while (!done) {
++ require (_validate_element (self, iter, depth));
++ require_advance (done, iter);
++ }
++ return true;
++}
++
++// Do validation for a DBRef document, indicated by a leading $ref key
++static bool
++_validate_dbref (validator *self, bson_iter_t *iter, int depth)
++{
++ BSON_ASSERT_PARAM (self);
++ BSON_ASSERT_PARAM (iter);
++
++ // The iterator must be pointing to the initial $ref element
++ BSON_ASSERT (_key_is (iter, "$ref"));
++ // Check that $ref is a UTF-8 element
++ require_with_error (
++ BSON_ITER_HOLDS_UTF8 (iter), iter->off, BSON_VALIDATE_DOLLAR_KEYS,
"$ref element must be a UTF-8 element");
++ require (_validate_element_value (self, iter, depth));
++
++ // We require an $id as the next element
++ bool done;
++ require_advance (done, iter);
++ require_with_error (
++ !done && _key_is (iter, "$id"), iter->off, BSON_VALIDATE_DOLLAR_KEYS,
"Expected an $id element following $ref");
++ // While $id is typically a OID value, it is not constraint to any
specific type, so
++ // we just validate it as an arbitrary value.
++ require (_validate_element_value (self, iter, depth));
++
++ // We should stop, or we should have a $db, or we may have other elements
++ require_advance (done, iter);
++ if (done) {
++ // No more elements. Nothing left to check
++ return true;
++ }
++ // If it's a $db, check that it's a UTF-8 string
++ if (_key_is (iter, "$db")) {
++ require_with_error (BSON_ITER_HOLDS_UTF8 (iter),
++ iter->off,
++ BSON_VALIDATE_DOLLAR_KEYS,
++ "$db element in DBRef must be a UTF-8 element");
++ require (_validate_element_value (self, iter, depth));
++ // Advance past the $db
++ require_advance (done, iter);
++ if (done) {
++ // Nothing left to do
++ return true;
++ }
++ }
++ // All subsequent elements should be validated as normal, and we don't
expect
++ // any more $-keys
++ return _validate_remaining_elements (self, iter, depth);
++}
++
++// If we are validating special $-keys, validate a document whose first
element is a $-key
++static bool
++_validate_dollar_doc (validator *self, bson_iter_t *iter, int depth)
++{
++ BSON_ASSERT_PARAM (self);
++ BSON_ASSERT_PARAM (iter);
++ if (_key_is (iter, "$ref")) {
++ return _validate_dbref (self, iter, depth);
++ }
++ // Have the element key validator issue an error message about the bad
$-key
++ bool okay = _validate_element_key (self, iter);
++ BSON_ASSERT (!okay);
++ return false;
++}
++
++static bool
++_validate_doc (validator *self, const bson_t *bson, int depth)
++{
++ BSON_ASSERT_PARAM (self);
++ BSON_ASSERT_PARAM (bson);
++
++ require_with_error (
++ depth <= BSON_VALIDATION_MAX_NESTING_DEPTH, 0, BSON_VALIDATE_CORRUPT,
"BSON document nesting depth is too deep");
++ // We increment the depth here, otherwise we'd have `depth + 1` in several
places.
++ ++depth;
++
++ // Initialize an iterator into the document to be validated
++ bson_iter_t iter;
++ require_with_error (
++ bson_iter_init (&iter, bson), 0, BSON_VALIDATE_CORRUPT, "Document
header corruption, unable to iterate");
++ bool done;
++ require_advance (done, &iter);
++ if (done) {
++ // Nothing to check (empty doc/array)
++ return true;
++ }
++
++ // Check if the first key starts with a dollar
++ if (self->params->check_special_dollar_keys) {
++ const char *const key = bson_iter_key (&iter);
++ if (key[0] == '$') {
++ return _validate_dollar_doc (self, &iter, depth);
++ }
++ }
++
++ return _validate_remaining_elements (self, &iter, depth);
++}
++
++// This private function is called by `bson_validate_with_error_and_offset`
++bool
++_bson_validate_impl_v2 (const bson_t *bson, bson_validate_flags_t flags,
size_t *offset, bson_error_t *error)
++{
++ BSON_ASSERT_PARAM (bson);
++ BSON_ASSERT_PARAM (offset);
++ BSON_ASSERT_PARAM (error);
++
++ // Clear the error
++ *error = (bson_error_t){0};
++
++ // Initialize validation parameters
++ validation_params const params = {
++ .allow_invalid_utf8 = !(flags & BSON_VALIDATE_UTF8),
++ .allow_null_in_utf8 = flags & BSON_VALIDATE_UTF8_ALLOW_NULL,
++ .check_special_dollar_keys = (flags & BSON_VALIDATE_DOLLAR_KEYS),
++ .allow_dot_in_keys = !(flags & BSON_VALIDATE_DOT_KEYS),
++ .allow_empty_keys = !(flags & BSON_VALIDATE_EMPTY_KEYS),
++ };
++
++ // Start the validator on the root document
++ validator v = {.params = ¶ms};
++ bool okay = _validate_doc (&v, bson, 0);
++ *offset = v.error_offset;
++ *error = v.error;
++ BSON_ASSERT (okay == (v.error.code == 0) &&
++ "Validation routine should return `false` if-and-only-if it
sets an error code");
++ return okay;
++}
+diff --git a/src/libmongoc/src/mongoc/mongoc-util.c
b/src/libmongoc/src/mongoc/mongoc-util.c
+index a83d8c5146..37306c4c20 100644
+--- a/src/libmongoc/src/mongoc/mongoc-util.c
++++ b/src/libmongoc/src/mongoc/mongoc-util.c
+@@ -33,14 +33,19 @@
+ #include <mongoc/mongoc-sleep.h>
+ #include <common-cmp-private.h>
+
+-const bson_validate_flags_t _mongoc_default_insert_vflags =
+- BSON_VALIDATE_UTF8 | BSON_VALIDATE_UTF8_ALLOW_NULL |
BSON_VALIDATE_EMPTY_KEYS;
+-
+-const bson_validate_flags_t _mongoc_default_replace_vflags =
+- BSON_VALIDATE_UTF8 | BSON_VALIDATE_UTF8_ALLOW_NULL |
BSON_VALIDATE_EMPTY_KEYS;
+-
+-const bson_validate_flags_t _mongoc_default_update_vflags =
+- BSON_VALIDATE_UTF8 | BSON_VALIDATE_UTF8_ALLOW_NULL |
BSON_VALIDATE_EMPTY_KEYS;
++/**
++ * ! NOTE
++ *
++ * In earlier releases, these flags had `BSON_VALIDATE_UTF8` and
`BSON_VALIDATE_UTF8_ALLOW_NULL`.
++ * Due to a bug, the CRUD APIs did not actually do UTF-8 validation. This
issue has been fixed, but
++ * we want to maintain backward compatibility, so the UTF-8 validation was
removed from these flag
++ * values.
++ *
++ * A future API may add the UTF-8 validation back, but it would be a breaking
change.
++ */
++const bson_validate_flags_t _mongoc_default_insert_vflags =
BSON_VALIDATE_EMPTY_KEYS;
++const bson_validate_flags_t _mongoc_default_replace_vflags =
BSON_VALIDATE_EMPTY_KEYS;
++const bson_validate_flags_t _mongoc_default_update_vflags =
BSON_VALIDATE_EMPTY_KEYS;
+
+ int
+ _mongoc_rand_simple (unsigned int *seed)
+--
+2.39.5
+
diff -Nru mongo-c-driver-1.30.4/debian/patches/0003_CVE-2026-6231.patch
mongo-c-driver-1.30.4/debian/patches/0003_CVE-2026-6231.patch
--- mongo-c-driver-1.30.4/debian/patches/0003_CVE-2026-6231.patch
1969-12-31 19:00:00.000000000 -0500
+++ mongo-c-driver-1.30.4/debian/patches/0003_CVE-2026-6231.patch
2026-04-27 11:48:27.000000000 -0400
@@ -0,0 +1,27 @@
+From 017e630199cd006e01288b241774168b7d2b8bdf Mon Sep 17 00:00:00 2001
+From: Kevin Albertson <[email protected]>
+Date: Mon, 9 Jun 2025 15:16:57 -0400
+Subject: [PATCH] [CDRIVER-6017] Reduce `BSON_VALIDATION_MAX_NESTING_DEPTH` to
+ 500 (#2035)
+
+To fix stack overflow encountered on MSVC on r1.30 branch. Likely caused by
default over-alignment of `bson_t` and `bson_iter_t` removed in 2.0.
+---
+ src/libbson/src/bson/validate-private.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/libbson/src/bson/validate-private.h
b/src/libbson/src/bson/validate-private.h
+index 8774a1e653..f6c8e664ff 100644
+--- a/src/libbson/src/bson/validate-private.h
++++ b/src/libbson/src/bson/validate-private.h
+@@ -15,7 +15,7 @@ enum {
+ * server might accept. The main purpose of this limit is to prevent stack
+ * overflow, not to reject invalid data.
+ */
+- BSON_VALIDATION_MAX_NESTING_DEPTH = 1000,
++ BSON_VALIDATION_MAX_NESTING_DEPTH = 500,
+ };
+
+ /**
+--
+2.39.5
+
diff -Nru mongo-c-driver-1.30.4/debian/patches/0004_CVE-2026-4359.patch
mongo-c-driver-1.30.4/debian/patches/0004_CVE-2026-4359.patch
--- mongo-c-driver-1.30.4/debian/patches/0004_CVE-2026-4359.patch
1969-12-31 19:00:00.000000000 -0500
+++ mongo-c-driver-1.30.4/debian/patches/0004_CVE-2026-4359.patch
2026-04-27 11:48:27.000000000 -0400
@@ -0,0 +1,36 @@
+From 754232f3cffe924346dcf327f4a723f1a0839420 Mon Sep 17 00:00:00 2001
+From: Remi Collet <[email protected]>
+Date: Thu, 19 Mar 2026 13:07:46 +0100
+Subject: [PATCH] CDRIVER-6251 fix handling of HTTP response (#2233) (#2234)
+ (#2254)
+
+(cherry picked from commit b93ebe6b99e614b49a24316c7a295eb3f08af603)
+
+Co-authored-by: Kevin Albertson <[email protected]>
+---
+ src/libmongoc/src/mongoc/mongoc-http.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/src/libmongoc/src/mongoc/mongoc-http.c
b/src/libmongoc/src/mongoc/mongoc-http.c
+index 9284162a17..ec65031eee 100644
+--- a/src/libmongoc/src/mongoc/mongoc-http.c
++++ b/src/libmongoc/src/mongoc/mongoc-http.c
+@@ -213,6 +213,15 @@ _mongoc_http_send (const mongoc_http_request_t *req,
+ goto fail;
+ }
+
++ // Ensure NULL terminator follows content
++ {
++ if (!_mongoc_buffer_append(&http_response_buf, (const uint8_t[]){0u},
1)) {
++ bson_set_error(error, MONGOC_ERROR_STREAM,
MONGOC_ERROR_STREAM_SOCKET, "Failed to buffer HTTP response");
++ goto fail;
++ }
++ http_response_buf.len--;
++ }
++
+ http_response_str = (char *) http_response_buf.data;
+ const char *const resp_end_ptr = http_response_str + http_response_buf.len;
+
+--
+2.39.5
+
diff -Nru mongo-c-driver-1.30.4/debian/patches/0005_CDRIVER-6281.patch
mongo-c-driver-1.30.4/debian/patches/0005_CDRIVER-6281.patch
--- mongo-c-driver-1.30.4/debian/patches/0005_CDRIVER-6281.patch
1969-12-31 19:00:00.000000000 -0500
+++ mongo-c-driver-1.30.4/debian/patches/0005_CDRIVER-6281.patch
2026-04-27 11:48:27.000000000 -0400
@@ -0,0 +1,92 @@
+From f70634e1d6d084f3b0f7077d03bde2d4cb95ce37 Mon Sep 17 00:00:00 2001
+From: Kevin Albertson <[email protected]>
+Date: Fri, 3 Apr 2026 21:16:37 -0400
+Subject: [PATCH] CDRIVER-6281 improve handling of corrupt GridFS files (#2263)
+
+* add regression test for 0 chunk size
+
+* check chunkSize on read
+
+* propagate error in `mongoc_gridfs_file_list_next`
+
+* add regression test for too-small chunk
+
+* fix too-small chunk read
+---
+ .../src/mongoc/mongoc-gridfs-file-list.c | 7 +-
+ .../src/mongoc/mongoc-gridfs-file-page.c | 4 +
+ src/libmongoc/src/mongoc/mongoc-gridfs-file.c | 10 +-
+ src/libmongoc/tests/test-mongoc-gridfs.c | 110 ++++++++++++++++++
+ 4 files changed, 127 insertions(+), 4 deletions(-)
+
+diff --git a/src/libmongoc/src/mongoc/mongoc-gridfs-file-list.c
b/src/libmongoc/src/mongoc/mongoc-gridfs-file-list.c
+index f0b4b19339..058e30d28b 100644
+--- a/src/libmongoc/src/mongoc/mongoc-gridfs-file-list.c
++++ b/src/libmongoc/src/mongoc/mongoc-gridfs-file-list.c
+@@ -99,7 +99,12 @@ mongoc_gridfs_file_list_next (mongoc_gridfs_file_list_t
*list)
+ BSON_ASSERT (list);
+
+ if (mongoc_cursor_next (list->cursor, &bson)) {
+- return _mongoc_gridfs_file_new_from_bson (list->gridfs, bson);
++ mongoc_gridfs_file_t *file = _mongoc_gridfs_file_new_from_bson
(list->gridfs, bson);
++ if (!file) {
++ bson_set_error (
++ &list->cursor->error, MONGOC_ERROR_GRIDFS,
MONGOC_ERROR_GRIDFS_CORRUPT, "Failed to read GridFS file");
++ }
++ return file;
+ } else {
+ return NULL;
+ }
+diff --git a/src/libmongoc/src/mongoc/mongoc-gridfs-file-page.c
b/src/libmongoc/src/mongoc/mongoc-gridfs-file-page.c
+index 161cc32c3c..7606406791 100644
+--- a/src/libmongoc/src/mongoc/mongoc-gridfs-file-page.c
++++ b/src/libmongoc/src/mongoc/mongoc-gridfs-file-page.c
+@@ -72,6 +72,10 @@ _mongoc_gridfs_file_page_read (mongoc_gridfs_file_page_t
*page, void *dst, uint3
+ BSON_ASSERT (page);
+ BSON_ASSERT (dst);
+
++ if (page->offset > page->len) {
++ RETURN (-1);
++ }
++
+ bytes_read = BSON_MIN (len, page->len - page->offset);
+
+ src = page->read_buf ? page->read_buf : page->buf;
+diff --git a/src/libmongoc/src/mongoc/mongoc-gridfs-file.c
b/src/libmongoc/src/mongoc/mongoc-gridfs-file.c
+index 78384f4bf9..c10d7e50ff 100644
+--- a/src/libmongoc/src/mongoc/mongoc-gridfs-file.c
++++ b/src/libmongoc/src/mongoc/mongoc-gridfs-file.c
+@@ -234,7 +234,8 @@ _mongoc_gridfs_file_new_from_bson (mongoc_gridfs_t
*gridfs, const bson_t *data)
+ if (!BSON_ITER_HOLDS_NUMBER (&iter)) {
+ GOTO (failure);
+ }
+- if (bson_iter_as_int64 (&iter) > INT32_MAX) {
++ int64_t as_i64 = bson_iter_as_int64 (&iter);
++ if (as_i64 > INT32_MAX || as_i64 <= 0) {
+ GOTO (failure);
+ }
+ file->chunk_size = (int32_t) bson_iter_as_int64 (&iter);
+@@ -284,7 +285,7 @@ _mongoc_gridfs_file_new_from_bson (mongoc_gridfs_t
*gridfs, const bson_t *data)
+
+ failure:
+ bson_destroy (&file->bson);
+-
++ bson_free (file);
+ RETURN (NULL);
+ }
+
+@@ -450,7 +451,10 @@ mongoc_gridfs_file_readv (
+ for (;;) {
+ r = _mongoc_gridfs_file_page_read (
+ file->page, (uint8_t *) iov[i].iov_base + iov_pos, (uint32_t)
(iov[i].iov_len - iov_pos));
+- BSON_ASSERT (r >= 0);
++ if (r < 0) {
++ bson_set_error (&file->error, MONGOC_ERROR_GRIDFS,
MONGOC_ERROR_GRIDFS_CORRUPT, "GridFS operation failed");
++ return -1;
++ }
+
+ iov_pos += r;
+ file->pos += r;
+--
+2.39.5
+
diff -Nru mongo-c-driver-1.30.4/debian/patches/0006_CVE-2025-14911.patch
mongo-c-driver-1.30.4/debian/patches/0006_CVE-2025-14911.patch
--- mongo-c-driver-1.30.4/debian/patches/0006_CVE-2025-14911.patch
1969-12-31 19:00:00.000000000 -0500
+++ mongo-c-driver-1.30.4/debian/patches/0006_CVE-2025-14911.patch
2026-04-27 11:48:27.000000000 -0400
@@ -0,0 +1,56 @@
+From ed8bed47906e37dd27306de0095ccbc56d6ec906 Mon Sep 17 00:00:00 2001
+From: Kevin Albertson <[email protected]>
+Date: Thu, 16 Oct 2025 14:41:21 -0400
+Subject: [PATCH] CDRIVER-6125 fix GridFS chunk size handling (#2146) (#2150)
+
+* validate chunk size from server document
+* test negative and zero length
+* check for negative length
+ * Not strictly needed. But gives an earlier error.
+---
+ .../src/mongoc/mongoc-gridfs-bucket.c | 21 +++
+ .../tests/test-mongoc-gridfs-bucket.c | 175 ++++++++++++++++++
+ 2 files changed, 196 insertions(+)
+
+diff --git a/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c
b/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c
+index 27bd3ed1af..8439f46df5 100644
+--- a/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c
++++ b/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c
+@@ -179,6 +179,7 @@ mongoc_gridfs_bucket_open_upload_stream_with_id
(mongoc_gridfs_bucket_t *bucket,
+ file->bucket = bucket;
+ file->chunk_size = gridfs_opts.chunkSizeBytes;
+ file->metadata = bson_copy (&gridfs_opts.metadata);
++ BSON_ASSERT (gridfs_opts.chunkSizeBytes > 0); // Validated in
_mongoc_gridfs_bucket_opts_parse.
+ file->buffer = bson_malloc ((size_t) gridfs_opts.chunkSizeBytes);
+ file->in_buffer = 0;
+
+@@ -344,6 +345,26 @@ mongoc_gridfs_bucket_open_download_stream
(mongoc_gridfs_bucket_t *bucket,
+
+ bson_destroy (&file_doc);
+
++ if (file->chunk_size <= 0) {
++ _mongoc_set_error (error,
++ MONGOC_ERROR_GRIDFS,
++ MONGOC_ERROR_GRIDFS_CORRUPT,
++ "File document contains invalid chunk size: %"
PRId32,
++ file->chunk_size);
++ _mongoc_gridfs_bucket_file_destroy (file);
++ return NULL;
++ }
++
++ if (file->length < 0) {
++ _mongoc_set_error (error,
++ MONGOC_ERROR_GRIDFS,
++ MONGOC_ERROR_GRIDFS_CORRUPT,
++ "File document contains invalid length: %" PRId64,
++ file->length);
++ _mongoc_gridfs_bucket_file_destroy (file);
++ return NULL;
++ }
++
+ file->file_id = (bson_value_t *) bson_malloc0 (sizeof *(file->file_id));
+ bson_value_copy (file_id, file->file_id);
+ file->bucket = bucket;
+--
+2.39.5
+
diff -Nru mongo-c-driver-1.30.4/debian/patches/0007_CVE-2025-14911.patch
mongo-c-driver-1.30.4/debian/patches/0007_CVE-2025-14911.patch
--- mongo-c-driver-1.30.4/debian/patches/0007_CVE-2025-14911.patch
1969-12-31 19:00:00.000000000 -0500
+++ mongo-c-driver-1.30.4/debian/patches/0007_CVE-2025-14911.patch
2026-04-27 11:48:27.000000000 -0400
@@ -0,0 +1,49 @@
+From bc7645f3a5dea3bd9c4c31d83713cb67978a57d9 Mon Sep 17 00:00:00 2001
+From: Kevin Albertson <[email protected]>
+Date: Tue, 7 Apr 2026 09:34:54 -0400
+Subject: [PATCH] CDRIVER-6125 replace `_mongoc_set_error` with
+ `bson_set_error`
+
+---
+ .../src/mongoc/mongoc-gridfs-bucket.c | 20 +++++++++----------
+ 1 file changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c
b/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c
+index 8439f46df5..656e55897f 100644
+--- a/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c
++++ b/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c
+@@ -346,21 +346,21 @@ mongoc_gridfs_bucket_open_download_stream
(mongoc_gridfs_bucket_t *bucket,
+ bson_destroy (&file_doc);
+
+ if (file->chunk_size <= 0) {
+- _mongoc_set_error (error,
+- MONGOC_ERROR_GRIDFS,
+- MONGOC_ERROR_GRIDFS_CORRUPT,
+- "File document contains invalid chunk size: %"
PRId32,
+- file->chunk_size);
++ bson_set_error (error,
++ MONGOC_ERROR_GRIDFS,
++ MONGOC_ERROR_GRIDFS_CORRUPT,
++ "File document contains invalid chunk size: %" PRId32,
++ file->chunk_size);
+ _mongoc_gridfs_bucket_file_destroy (file);
+ return NULL;
+ }
+
+ if (file->length < 0) {
+- _mongoc_set_error (error,
+- MONGOC_ERROR_GRIDFS,
+- MONGOC_ERROR_GRIDFS_CORRUPT,
+- "File document contains invalid length: %" PRId64,
+- file->length);
++ bson_set_error (error,
++ MONGOC_ERROR_GRIDFS,
++ MONGOC_ERROR_GRIDFS_CORRUPT,
++ "File document contains invalid length: %" PRId64,
++ file->length);
+ _mongoc_gridfs_bucket_file_destroy (file);
+ return NULL;
+ }
+--
+2.39.5
+
diff -Nru mongo-c-driver-1.30.4/debian/patches/0008_CVE-2026-6691.patch
mongo-c-driver-1.30.4/debian/patches/0008_CVE-2026-6691.patch
--- mongo-c-driver-1.30.4/debian/patches/0008_CVE-2026-6691.patch
1969-12-31 19:00:00.000000000 -0500
+++ mongo-c-driver-1.30.4/debian/patches/0008_CVE-2026-6691.patch
2026-04-27 11:48:27.000000000 -0400
@@ -0,0 +1,39 @@
+From d2519ea8a403861782d9cde05c71c265bb568bbc Mon Sep 17 00:00:00 2001
+From: Kevin Albertson <[email protected]>
+Date: Fri, 24 Oct 2025 13:11:50 -0400
+Subject: [PATCH] CDRIVER-6134 check SASL username length
+
+Cherry-pick b4984965877d559862e225beba09cb4e9d4a56a6 and
d9c26f49e75d3de746a690db9c81ff5b4f6e21b0 and reformat.
+---
+ src/libmongoc/src/mongoc/mongoc-cyrus.c | 13 +++++++++++--
+ 1 file changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/src/libmongoc/src/mongoc/mongoc-cyrus.c
b/src/libmongoc/src/mongoc/mongoc-cyrus.c
+index 10b5dc138a..d4125594b3 100644
+--- a/src/libmongoc/src/mongoc/mongoc-cyrus.c
++++ b/src/libmongoc/src/mongoc/mongoc-cyrus.c
+@@ -112,10 +112,19 @@ _mongoc_cyrus_canon_user (sasl_conn_t *conn,
+ BSON_UNUSED (sasl);
+ BSON_UNUSED (flags);
+ BSON_UNUSED (user_realm);
+- BSON_UNUSED (out_max);
++
++ // `inlen` is a string length (excluding trailing NULL).
++ // Cyrus-SASL passes an `out` buffer of size `out_max + 1`. Assume
`out_max` is the max to be safe.
++ const unsigned inlen_2 = inlen + 2u;
++ if (inlen_2 < inlen || inlen_2 >= out_max) {
++ MONGOC_ERROR ("SASL username too large");
++ return SASL_BUFOVER;
++ }
+
+ TRACE ("Canonicalizing %s (%" PRIu32 ")\n", in, inlen);
+- strcpy (out, in);
++ // Use memmove in case buffers overlap. From Cyrus-SASL: "output buffers
and the input buffers may be the same"
++ memmove (out, in, inlen);
++ out[inlen] = '\0';
+ *out_len = inlen;
+ return SASL_OK;
+ }
+--
+2.39.5
+
diff -Nru mongo-c-driver-1.30.4/debian/patches/series
mongo-c-driver-1.30.4/debian/patches/series
--- mongo-c-driver-1.30.4/debian/patches/series 2025-12-18 14:50:07.000000000
-0500
+++ mongo-c-driver-1.30.4/debian/patches/series 2026-04-27 11:48:27.000000000
-0400
@@ -1,2 +1,9 @@
0001_local_mathjax.diff
CVE-2025-12119.patch
+0002_CVE-2026-6231.patch
+0003_CVE-2026-6231.patch
+0004_CVE-2026-4359.patch
+0005_CDRIVER-6281.patch
+0006_CVE-2025-14911.patch
+0007_CVE-2025-14911.patch
+0008_CVE-2026-6691.patch