From d336d15b6f13f8e85946d0cd19f3fc7271dbde01 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Fri, 10 Oct 2025 14:52:04 -0400 Subject: [PATCH 1/4] CDRIVER-6125 add regression test for bad chunk sizes --- .../tests/test-mongoc-gridfs-bucket.c | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/libmongoc/tests/test-mongoc-gridfs-bucket.c b/src/libmongoc/tests/test-mongoc-gridfs-bucket.c index c12f8d7b823..3d342ad2fcf 100644 --- a/src/libmongoc/tests/test-mongoc-gridfs-bucket.c +++ b/src/libmongoc/tests/test-mongoc-gridfs-bucket.c @@ -1048,6 +1048,110 @@ test_gridfs_bucket_opts(void) mongoc_client_destroy(client); } +// Regression test for CDRIVER-6125 +static void +test_bad_sizes(void) +{ + mongoc_client_t *client = test_framework_new_default_client(); + bson_error_t error; + + mongoc_database_t *db = mongoc_client_get_database(client, "test_bad_sizes"); + mongoc_database_drop(db, NULL); + + // Test negative chunkSize: + { + bson_t *doc = tmp_bson(BSON_STR({ + "_id" : 0, + "filename" : "foo.txt", + "length" : 1000, + "chunkSize" : -1, // Negative! + "uploadDate" : {"$date" : 1234567890000} + })); + + bson_iter_t id_iter; + ASSERT(bson_iter_init_find(&id_iter, doc, "_id")); + const bson_value_t *id_value = bson_iter_value(&id_iter); + + // Insert manually: + { + mongoc_collection_t *files = mongoc_database_get_collection(db, "fs.files"); + mongoc_collection_insert_one(files, doc, NULL, NULL, &error); + mongoc_collection_destroy(files); + } + + // Try to read: + { + mongoc_gridfs_bucket_t *bucket = mongoc_gridfs_bucket_new(db, NULL, NULL, &error); + ASSERT_OR_PRINT(bucket, error); + mongoc_stream_t *stream = mongoc_gridfs_bucket_open_download_stream(bucket, id_value, &error); + ASSERT(!stream); + ASSERT_ERROR_CONTAINS(error, MONGOC_ERROR_GRIDFS, MONGOC_ERROR_GRIDFS_CORRUPT, "invalid chunk size"); + mongoc_gridfs_bucket_destroy(bucket); + } + + // Try to write: + { + mongoc_gridfs_bucket_t *bucket = mongoc_gridfs_bucket_new(db, NULL, NULL, &error); + ASSERT_OR_PRINT(bucket, error); + bson_t *opts = tmp_bson(BSON_STR({"chunkSizeBytes" : 0})); + mongoc_stream_t *stream = mongoc_gridfs_bucket_open_upload_stream(bucket, "foo.txt", opts, NULL, &error); + ASSERT(!stream); + ASSERT_ERROR_CONTAINS( + error, MONGOC_ERROR_COMMAND, MONGOC_ERROR_COMMAND_INVALID_ARG, "should be greater than 0"); + mongoc_gridfs_bucket_destroy(bucket); + } + } + + mongoc_database_drop(db, NULL); + + // Test zero chunkSize: + { + bson_t *doc = tmp_bson(BSON_STR({ + "_id" : 0, + "filename" : "foo.txt", + "length" : 1000, + "chunkSize" : 0, // Zero! + "uploadDate" : {"$date" : 1234567890000} + })); + + bson_iter_t id_iter; + ASSERT(bson_iter_init_find(&id_iter, doc, "_id")); + const bson_value_t *id_value = bson_iter_value(&id_iter); + + // Insert manually: + { + mongoc_collection_t *files = mongoc_database_get_collection(db, "fs.files"); + mongoc_collection_insert_one(files, doc, NULL, NULL, &error); + mongoc_collection_destroy(files); + } + + // Try to read: + { + mongoc_gridfs_bucket_t *bucket = mongoc_gridfs_bucket_new(db, NULL, NULL, &error); + ASSERT_OR_PRINT(bucket, error); + mongoc_stream_t *stream = mongoc_gridfs_bucket_open_download_stream(bucket, id_value, &error); + ASSERT(!stream); + ASSERT_ERROR_CONTAINS(error, MONGOC_ERROR_GRIDFS, MONGOC_ERROR_GRIDFS_CORRUPT, "invalid chunk size"); + mongoc_gridfs_bucket_destroy(bucket); + } + + // Try to write: + { + mongoc_gridfs_bucket_t *bucket = mongoc_gridfs_bucket_new(db, NULL, NULL, &error); + ASSERT_OR_PRINT(bucket, error); + bson_t *opts = tmp_bson(BSON_STR({"chunkSizeBytes" : -1})); + mongoc_stream_t *stream = mongoc_gridfs_bucket_open_upload_stream(bucket, "foo.txt", opts, NULL, &error); + ASSERT(!stream); + ASSERT_ERROR_CONTAINS( + error, MONGOC_ERROR_COMMAND, MONGOC_ERROR_COMMAND_INVALID_ARG, "should be greater than 0"); + mongoc_gridfs_bucket_destroy(bucket); + } + } + + mongoc_database_destroy(db); + mongoc_client_destroy(client); +} + void test_gridfs_bucket_install(TestSuite *suite) { @@ -1070,4 +1174,5 @@ test_gridfs_bucket_install(TestSuite *suite) test_framework_skip_if_no_sessions, test_framework_skip_if_no_crypto); TestSuite_AddLive(suite, "/gridfs/options", test_gridfs_bucket_opts); + TestSuite_AddLive(suite, "/gridfs/bad_sizes", test_bad_sizes); } From 2c9c9dfd6d378280096fb12685ae5757ab8110b7 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Fri, 10 Oct 2025 14:52:38 -0400 Subject: [PATCH 2/4] validate chunk size from server document --- src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c b/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c index d61f7d4384c..c3683039d6b 100644 --- a/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c +++ b/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c @@ -182,6 +182,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; @@ -347,6 +348,16 @@ 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; + } + file->file_id = (bson_value_t *)bson_malloc0(sizeof *(file->file_id)); bson_value_copy(file_id, file->file_id); file->bucket = bucket; From d51b462cda645f24324b67db6faab615738d0b1f Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Fri, 10 Oct 2025 15:07:17 -0400 Subject: [PATCH 3/4] test negative and zero length --- .../tests/test-mongoc-gridfs-bucket.c | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/libmongoc/tests/test-mongoc-gridfs-bucket.c b/src/libmongoc/tests/test-mongoc-gridfs-bucket.c index 3d342ad2fcf..3cd48cdc79c 100644 --- a/src/libmongoc/tests/test-mongoc-gridfs-bucket.c +++ b/src/libmongoc/tests/test-mongoc-gridfs-bucket.c @@ -1148,6 +1148,76 @@ test_bad_sizes(void) } } + mongoc_database_drop(db, NULL); + + // Test negative length: + { + bson_t *doc = tmp_bson(BSON_STR({ + "_id" : 0, + "filename" : "foo.txt", + "length" : -1, // Negative! + "chunkSize" : 10, + "uploadDate" : {"$date" : 1234567890000} + })); + + bson_iter_t id_iter; + ASSERT(bson_iter_init_find(&id_iter, doc, "_id")); + const bson_value_t *id_value = bson_iter_value(&id_iter); + + // Insert manually: + { + mongoc_collection_t *files = mongoc_database_get_collection(db, "fs.files"); + mongoc_collection_insert_one(files, doc, NULL, NULL, &error); + mongoc_collection_destroy(files); + } + + // Try to read: + { + mongoc_gridfs_bucket_t *bucket = mongoc_gridfs_bucket_new(db, NULL, NULL, &error); + mongoc_stream_t *stream = mongoc_gridfs_bucket_open_download_stream(bucket, id_value, &error); + ASSERT(!stream); + ASSERT_ERROR_CONTAINS(error, MONGOC_ERROR_GRIDFS, MONGOC_ERROR_GRIDFS_CORRUPT, "invalid length"); + mongoc_gridfs_bucket_destroy(bucket); + } + } + + mongoc_database_drop(db, NULL); + + // Test a zero length (OK): + { + bson_t *doc = tmp_bson(BSON_STR({ + "_id" : 0, + "filename" : "foo.txt", + "length" : 0, // Zero! + "chunkSize" : 10, + "uploadDate" : {"$date" : 1234567890000} + })); + + bson_iter_t id_iter; + ASSERT(bson_iter_init_find(&id_iter, doc, "_id")); + const bson_value_t *id_value = bson_iter_value(&id_iter); + + // Insert manually: + { + mongoc_collection_t *files = mongoc_database_get_collection(db, "fs.files"); + mongoc_collection_insert_one(files, doc, NULL, NULL, &error); + mongoc_collection_destroy(files); + } + + // Try to read: + { + mongoc_gridfs_bucket_t *bucket = mongoc_gridfs_bucket_new(db, NULL, NULL, &error); + mongoc_stream_t *stream = mongoc_gridfs_bucket_open_download_stream(bucket, id_value, &error); + ASSERT(stream); + // OK. Gets back an empty read. + uint8_t buf[64]; + ssize_t r = mongoc_stream_read(stream, buf, sizeof buf, 0, 0); + ASSERT_CMPINT(r, ==, 0); + mongoc_stream_destroy(stream); + mongoc_gridfs_bucket_destroy(bucket); + } + } + mongoc_database_destroy(db); mongoc_client_destroy(client); } From 63e4b6241f3586fbec49e407595057abe7265b62 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Fri, 10 Oct 2025 15:07:34 -0400 Subject: [PATCH 4/4] check for negative length Not strictly needed. But gives an earlier error. --- src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c b/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c index c3683039d6b..1f8f95cfe05 100644 --- a/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c +++ b/src/libmongoc/src/mongoc/mongoc-gridfs-bucket.c @@ -358,6 +358,16 @@ mongoc_gridfs_bucket_open_download_stream(mongoc_gridfs_bucket_t *bucket, 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;