From 16f8cbec55db34b5af27fb8861233f6df7ea018b Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Wed, 1 Oct 2025 10:04:04 -0400 Subject: [PATCH 1/4] add failing test --- src/libmongoc/tests/test-mongoc-bulk.c | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/libmongoc/tests/test-mongoc-bulk.c b/src/libmongoc/tests/test-mongoc-bulk.c index 15f79c5b07b..b6de23053bd 100644 --- a/src/libmongoc/tests/test-mongoc-bulk.c +++ b/src/libmongoc/tests/test-mongoc-bulk.c @@ -4800,6 +4800,53 @@ test_multiple_execution(void) mongoc_client_destroy(client); } +// `test_bulk_big_let` tests a bulk operation with a large let document to reproduce CDRIVER-6112: +static void +test_bulk_big_let(void) +{ + mongoc_client_t *client = test_framework_new_default_client(); + mongoc_collection_t *coll = get_test_collection(client, "test_big_let"); + bson_error_t error; + + // Create bulk operation similar to PHP driver: + mongoc_bulk_operation_t *bulk = mongoc_bulk_operation_new(true /* ordered */); + + // Set a large `let`: { "testDocument": { "a": "aaa..." } } + { + bson_t let = BSON_INITIALIZER, testDocument; + bson_append_document_begin(&let, "testDocument", -1, &testDocument); + + // Append big string: + { + // size_t num_chars = 78; // OK + size_t num_chars = 79; // Crash + char *big_string = bson_malloc0(num_chars + 1); + memset(big_string, 'a', num_chars); + BSON_APPEND_UTF8(&testDocument, "a", big_string); + bson_free(big_string); + } + + bson_append_document_end(&let, &testDocument); + mongoc_bulk_operation_set_let(bulk, &let); + bson_destroy(&let); + } + + + mongoc_bulk_operation_set_client(bulk, client); + mongoc_bulk_operation_set_database(bulk, "db"); + mongoc_bulk_operation_set_collection(bulk, "coll"); + + mongoc_bulk_operation_update( + bulk, tmp_bson("{'_id': 1}"), tmp_bson("{'$set': {'document': '$$testDocument'}}"), true); + + + ASSERT_OR_PRINT(mongoc_bulk_operation_execute(bulk, NULL, &error), error); + + mongoc_bulk_operation_destroy(bulk); + mongoc_collection_destroy(coll); + mongoc_client_destroy(client); +} + void test_bulk_install(TestSuite *suite) @@ -4978,4 +5025,5 @@ test_bulk_install(TestSuite *suite) "/BulkOperation/set_client_updates_operation_id_when_client_changes", test_bulk_write_set_client_updates_operation_id_when_client_changes); TestSuite_AddLive(suite, "/BulkOperation/multiple_execution", test_multiple_execution); + TestSuite_AddLive(suite, "/BulkOperation/big_let", test_bulk_big_let); } From abcb38732709ffc05647cd99b17ccf9157a72d1b Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Wed, 1 Oct 2025 14:15:23 -0400 Subject: [PATCH 2/4] do not memcpy `bson_t` struct in array The memcpy does not correctly transfer ownership of the `bson_t` struct. Instead: heap allocate the `bson_t` struct. --- src/libmongoc/src/mongoc/mongoc-write-command-private.h | 2 +- src/libmongoc/src/mongoc/mongoc-write-command.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-write-command-private.h b/src/libmongoc/src/mongoc/mongoc-write-command-private.h index f4c02ba49a6..924b587430a 100644 --- a/src/libmongoc/src/mongoc/mongoc-write-command-private.h +++ b/src/libmongoc/src/mongoc/mongoc-write-command-private.h @@ -62,7 +62,7 @@ typedef struct { uint32_t n_documents; mongoc_bulk_write_flags_t flags; int64_t operation_id; - bson_t cmd_opts; + bson_t *cmd_opts; } mongoc_write_command_t; diff --git a/src/libmongoc/src/mongoc/mongoc-write-command.c b/src/libmongoc/src/mongoc/mongoc-write-command.c index dc2f616a428..036feeeaff6 100644 --- a/src/libmongoc/src/mongoc/mongoc-write-command.c +++ b/src/libmongoc/src/mongoc/mongoc-write-command.c @@ -144,9 +144,9 @@ _mongoc_write_command_init_bulk( command->flags = flags; command->operation_id = operation_id; if (!bson_empty0(opts)) { - bson_copy_to(opts, &command->cmd_opts); + command->cmd_opts = bson_copy(opts); } else { - bson_init(&command->cmd_opts); + command->cmd_opts = bson_new(); } _mongoc_buffer_init(&command->payload, NULL, 0, NULL, NULL); @@ -668,7 +668,7 @@ _mongoc_write_opmsg(mongoc_write_command_t *command, ? MONGOC_CMD_PARTS_ALLOW_TXN_NUMBER_NO : MONGOC_CMD_PARTS_ALLOW_TXN_NUMBER_YES; - BSON_ASSERT(bson_iter_init(&iter, &command->cmd_opts)); + BSON_ASSERT(bson_iter_init(&iter, command->cmd_opts)); if (!mongoc_cmd_parts_append_opts(&parts, &iter, error)) { bson_destroy(&cmd); mongoc_cmd_parts_cleanup(&parts); @@ -937,7 +937,7 @@ _mongoc_write_command_destroy(mongoc_write_command_t *command) ENTRY; if (command) { - bson_destroy(&command->cmd_opts); + bson_destroy(command->cmd_opts); _mongoc_buffer_destroy(&command->payload); } From 4a96d0896aaabce4c70bc0b0252e9a8c41e2396b Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Wed, 1 Oct 2025 14:40:32 -0400 Subject: [PATCH 3/4] add skip --- src/libmongoc/tests/test-mongoc-bulk.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/libmongoc/tests/test-mongoc-bulk.c b/src/libmongoc/tests/test-mongoc-bulk.c index b6de23053bd..701f89f471b 100644 --- a/src/libmongoc/tests/test-mongoc-bulk.c +++ b/src/libmongoc/tests/test-mongoc-bulk.c @@ -4802,8 +4802,10 @@ test_multiple_execution(void) // `test_bulk_big_let` tests a bulk operation with a large let document to reproduce CDRIVER-6112: static void -test_bulk_big_let(void) +test_bulk_big_let(void *unused) { + BSON_UNUSED(unused); + mongoc_client_t *client = test_framework_new_default_client(); mongoc_collection_t *coll = get_test_collection(client, "test_big_let"); bson_error_t error; @@ -4818,8 +4820,7 @@ test_bulk_big_let(void) // Append big string: { - // size_t num_chars = 78; // OK - size_t num_chars = 79; // Crash + size_t num_chars = 79; char *big_string = bson_malloc0(num_chars + 1); memset(big_string, 'a', num_chars); BSON_APPEND_UTF8(&testDocument, "a", big_string); @@ -5025,5 +5026,11 @@ test_bulk_install(TestSuite *suite) "/BulkOperation/set_client_updates_operation_id_when_client_changes", test_bulk_write_set_client_updates_operation_id_when_client_changes); TestSuite_AddLive(suite, "/BulkOperation/multiple_execution", test_multiple_execution); - TestSuite_AddLive(suite, "/BulkOperation/big_let", test_bulk_big_let); + TestSuite_AddFull( + suite, + "/BulkOperation/big_let", + test_bulk_big_let, + NULL, + NULL, + test_framework_skip_if_max_wire_version_less_than_13 /* 5.0+ for 'let' support in CRUD commands */); } From 38e3655a6a9a54cbbf313e80e7b67923b7dd8c1e Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Thu, 2 Oct 2025 09:47:18 -0400 Subject: [PATCH 4/4] warn against using `bson_t` in `mongoc_array_t` --- src/libmongoc/src/mongoc/mongoc-array-private.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libmongoc/src/mongoc/mongoc-array-private.h b/src/libmongoc/src/mongoc/mongoc-array-private.h index 89567c9c3c7..995da89106a 100644 --- a/src/libmongoc/src/mongoc/mongoc-array-private.h +++ b/src/libmongoc/src/mongoc/mongoc-array-private.h @@ -25,6 +25,9 @@ BSON_BEGIN_DECLS +// mongoc_array_t stores an array of objects of type T. +// +// T must be trivially relocatable. In particular, `bson_t` is not trivially relocatable (CDRIVER-6113). typedef struct _mongoc_array_t mongoc_array_t;