Skip to content

Commit a65cfc4

Browse files
committed
CDRIVER-5735 Improve validation of uncompressedSize for OP_COMPRESSED (#1747)
1 parent 44037dd commit a65cfc4

File tree

2 files changed

+58
-14
lines changed

2 files changed

+58
-14
lines changed

src/libmongoc/src/mongoc/mongoc-cluster.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3597,7 +3597,19 @@ mcd_rpc_message_decompress (mcd_rpc_message *rpc, void **data, size_t *data_len)
35973597
// msgHeader consists of four int32 fields.
35983598
const size_t message_header_length = 4u * sizeof (int32_t);
35993599

3600-
const size_t uncompressed_size = (size_t) mcd_rpc_op_compressed_get_uncompressed_size (rpc);
3600+
const int32_t uncompressed_size_raw = mcd_rpc_op_compressed_get_uncompressed_size (rpc);
3601+
3602+
// Malformed message: invalid uncompressedSize.
3603+
if (BSON_UNLIKELY (uncompressed_size_raw < 0)) {
3604+
return false;
3605+
}
3606+
3607+
const size_t uncompressed_size = (size_t) uncompressed_size_raw;
3608+
3609+
// Malformed message: original message length is not representable.
3610+
if (BSON_UNLIKELY (uncompressed_size > SIZE_MAX - message_header_length)) {
3611+
return false;
3612+
}
36013613

36023614
// uncompressedSize does not include msgHeader fields.
36033615
const size_t original_message_length = message_header_length + uncompressed_size;
@@ -3643,7 +3655,11 @@ mcd_rpc_message_decompress (mcd_rpc_message *rpc, void **data, size_t *data_len)
36433655
return false;
36443656
}
36453657

3646-
BSON_ASSERT (uncompressed_size == actual_uncompressed_size);
3658+
// Malformed message: size inconsistency.
3659+
if (BSON_UNLIKELY (uncompressed_size != actual_uncompressed_size)) {
3660+
bson_free (ptr);
3661+
return false;
3662+
}
36473663

36483664
*data_len = original_message_length;
36493665
*data = ptr; // Ownership transfer.

src/libmongoc/src/mongoc/mongoc-compression.c

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -137,20 +137,27 @@ mongoc_compressor_name_to_id (const char *compressor)
137137
return -1;
138138
}
139139

140+
// To support unchecked casts from `unsigned long` to `size_t`.
141+
BSON_STATIC_ASSERT2 (size_t_gte_ulong, SIZE_MAX >= ULONG_MAX);
142+
140143
bool
141144
mongoc_uncompress (int32_t compressor_id,
142145
const uint8_t *compressed,
143146
size_t compressed_len,
144147
uint8_t *uncompressed,
145148
size_t *uncompressed_len)
146149
{
150+
BSON_ASSERT_PARAM (compressed);
151+
BSON_ASSERT_PARAM (uncompressed);
152+
BSON_ASSERT_PARAM (uncompressed_len);
153+
147154
TRACE ("Uncompressing with '%s' (%d)", mongoc_compressor_id_to_name (compressor_id), compressor_id);
148155

149156
switch (compressor_id) {
150157
case MONGOC_COMPRESSOR_SNAPPY_ID: {
151158
#ifdef MONGOC_ENABLE_COMPRESSION_SNAPPY
152-
snappy_status status;
153-
status = snappy_uncompress ((const char *) compressed, compressed_len, (char *) uncompressed, uncompressed_len);
159+
const snappy_status status =
160+
snappy_uncompress ((const char *) compressed, compressed_len, (char *) uncompressed, uncompressed_len);
154161

155162
return status == SNAPPY_OK;
156163
#else
@@ -162,11 +169,28 @@ mongoc_uncompress (int32_t compressor_id,
162169

163170
case MONGOC_COMPRESSOR_ZLIB_ID: {
164171
#ifdef MONGOC_ENABLE_COMPRESSION_ZLIB
165-
BSON_ASSERT (bson_in_range_unsigned (unsigned_long, compressed_len));
166-
const int ok =
167-
uncompress (uncompressed, (unsigned long *) uncompressed_len, compressed, (unsigned long) compressed_len);
172+
// Malformed message: unrepresentable.
173+
if (BSON_UNLIKELY (!bson_in_range_unsigned (unsigned_long, compressed_len))) {
174+
return false;
175+
}
176+
177+
// Malformed message: unrepresentable.
178+
if (BSON_UNLIKELY (!bson_in_range_unsigned (unsigned_long, *uncompressed_len))) {
179+
return false;
180+
}
181+
182+
uLong actual_uncompressed_len = (uLong) *uncompressed_len;
183+
184+
const int res =
185+
uncompress (uncompressed, &actual_uncompressed_len, (const Bytef *) compressed, (uLong) compressed_len);
186+
187+
if (BSON_UNLIKELY (res != Z_OK)) {
188+
return false;
189+
}
168190

169-
return ok == Z_OK;
191+
*uncompressed_len = (size_t) actual_uncompressed_len;
192+
193+
return true;
170194
#else
171195
MONGOC_WARNING ("Received zlib compressed opcode, but zlib "
172196
"compression is not compiled in");
@@ -176,22 +200,26 @@ mongoc_uncompress (int32_t compressor_id,
176200

177201
case MONGOC_COMPRESSOR_ZSTD_ID: {
178202
#ifdef MONGOC_ENABLE_COMPRESSION_ZSTD
179-
int ok;
203+
const size_t res = ZSTD_decompress (uncompressed, *uncompressed_len, compressed, compressed_len);
180204

181-
ok = ZSTD_decompress ((void *) uncompressed, *uncompressed_len, (const void *) compressed, compressed_len);
182-
183-
if (!ZSTD_isError (ok)) {
184-
*uncompressed_len = ok;
205+
if (BSON_UNLIKELY (ZSTD_isError (res))) {
206+
return false;
185207
}
186208

187-
return !ZSTD_isError (ok);
209+
*uncompressed_len = res;
210+
211+
return true;
188212
#else
189213
MONGOC_WARNING ("Received zstd compressed opcode, but zstd "
190214
"compression is not compiled in");
191215
return false;
192216
#endif
193217
}
194218
case MONGOC_COMPRESSOR_NOOP_ID:
219+
// Malformed message: not enough space.
220+
if (BSON_UNLIKELY (*uncompressed_len < compressed_len)) {
221+
return false;
222+
}
195223
memcpy (uncompressed, compressed, compressed_len);
196224
*uncompressed_len = compressed_len;
197225
return true;

0 commit comments

Comments
 (0)