Skip to content

Commit 1c03078

Browse files
eramongodbkevinAlbs
authored andcommitted
CDRIVER-5667 Add missing BSON type checks in special key handlers (#1693)
* Use X macro for special keys * Use consistent key comparison expressions * CDRIVER-5667 Add missing BSON type checks in special key handlers * Add regression test to /bson/json/errors
1 parent 0832ea5 commit 1c03078

File tree

2 files changed

+78
-39
lines changed

2 files changed

+78
-39
lines changed

src/libbson/src/bson/bson-json.c

+70-33
Original file line numberDiff line numberDiff line change
@@ -362,20 +362,27 @@ _noop (void)
362362
return; \
363363
} else \
364364
(void) 0
365-
#define HANDLE_OPTION(_selection_statement, _key, _type, _state) \
366-
_selection_statement (len == strlen (_key) && strncmp ((const char *) val, (_key), len) == 0) \
367-
{ \
368-
if (bson->bson_type && bson->bson_type != (_type)) { \
369-
_bson_json_read_set_error (reader, \
370-
"Invalid key \"%s\". Looking for values " \
371-
"for type \"%s\", got \"%s\"", \
372-
(_key), \
373-
_bson_json_type_name (bson->bson_type), \
374-
_bson_json_type_name (_type)); \
375-
return; \
376-
} \
377-
bson->bson_type = (_type); \
378-
bson->bson_state = (_state); \
365+
366+
#define HANDLE_OPTION_KEY_COMPARE(_key) (len == strlen (_key) && memcmp (key, (_key), len) == 0)
367+
368+
#define HANDLE_OPTION_TYPE_CHECK(_key, _type) \
369+
if (bson->bson_type && bson->bson_type != (_type)) { \
370+
_bson_json_read_set_error (reader, \
371+
"Invalid key \"%s\". Looking for values " \
372+
"for type \"%s\", got \"%s\"", \
373+
(_key), \
374+
_bson_json_type_name (bson->bson_type), \
375+
_bson_json_type_name (_type)); \
376+
return; \
377+
} \
378+
((void) 0)
379+
380+
#define HANDLE_OPTION(_selection_statement, _key, _type, _state) \
381+
_selection_statement (HANDLE_OPTION_KEY_COMPARE (_key)) \
382+
{ \
383+
HANDLE_OPTION_TYPE_CHECK (_key, _type); \
384+
bson->bson_type = (_type); \
385+
bson->bson_state = (_state); \
379386
}
380387

381388

@@ -1159,23 +1166,40 @@ _bson_json_read_start_map (bson_json_reader_t *reader) /* IN */
11591166
}
11601167

11611168

1169+
#define BSON_PRIVATE_SPECIAL_KEYS_XMACRO(X) \
1170+
X (binary) \
1171+
X (code) \
1172+
X (date) \
1173+
X (dbPointer) \
1174+
X (maxKey) \
1175+
X (minKey) \
1176+
X (numberDecimal) \
1177+
X (numberDouble) \
1178+
X (numberInt) \
1179+
X (numberLong) \
1180+
X (oid) \
1181+
X (options) \
1182+
X (regex) \
1183+
X (regularExpression) \
1184+
X (scope) \
1185+
X (symbol) \
1186+
X (timestamp) \
1187+
X (type) \
1188+
X (undefined) \
1189+
X (uuid)
1190+
1191+
11621192
static bool
11631193
_is_known_key (const char *key, size_t len)
11641194
{
1165-
bool ret;
1166-
1167-
#define IS_KEY(k) (len == strlen (k) && (0 == memcmp (k, key, len)))
1168-
1169-
ret = (IS_KEY ("$regularExpression") || IS_KEY ("$regex") || IS_KEY ("$options") || IS_KEY ("$code") ||
1170-
IS_KEY ("$scope") || IS_KEY ("$oid") || IS_KEY ("$binary") || IS_KEY ("$type") || IS_KEY ("$date") ||
1171-
IS_KEY ("$undefined") || IS_KEY ("$maxKey") || IS_KEY ("$minKey") || IS_KEY ("$timestamp") ||
1172-
IS_KEY ("$numberInt") || IS_KEY ("$numberLong") || IS_KEY ("$numberDouble") || IS_KEY ("$numberDecimal") ||
1173-
IS_KEY ("$numberInt") || IS_KEY ("$numberLong") || IS_KEY ("$numberDouble") || IS_KEY ("$numberDecimal") ||
1174-
IS_KEY ("$dbPointer") || IS_KEY ("$symbol") || IS_KEY ("$uuid"));
1175-
1195+
#define IS_KEY(k) \
1196+
if (len == strlen ("$" #k) && (0 == memcmp ("$" #k, key, len))) { \
1197+
return true; \
1198+
}
1199+
BSON_PRIVATE_SPECIAL_KEYS_XMACRO (IS_KEY)
11761200
#undef IS_KEY
11771201

1178-
return ret;
1202+
return false;
11791203
}
11801204

11811205
static void
@@ -1242,9 +1266,10 @@ _bson_json_read_map_key (bson_json_reader_t *reader, /* IN */
12421266
return;
12431267
}
12441268

1269+
const char *const key = (const char *) val;
1270+
12451271
if (bson->read_state == BSON_JSON_IN_START_MAP) {
1246-
if (len > 0 && val[0] == '$' && _is_known_key ((const char *) val, len) &&
1247-
bson->n >= 0 /* key is in subdocument */) {
1272+
if (len > 0 && key[0] == '$' && _is_known_key (key, len) && bson->n >= 0 /* key is in subdocument */) {
12481273
bson->read_state = BSON_JSON_IN_BSON_TYPE;
12491274
bson->bson_type = (bson_type_t) 0;
12501275
memset (&bson->bson_type_data, 0, sizeof bson->bson_type_data);
@@ -1281,30 +1306,42 @@ _bson_json_read_map_key (bson_json_reader_t *reader, /* IN */
12811306
HANDLE_OPTION (else if, "$numberDouble", BSON_TYPE_DOUBLE, BSON_JSON_LF_DOUBLE)
12821307
HANDLE_OPTION (else if, "$symbol", BSON_TYPE_SYMBOL, BSON_JSON_LF_SYMBOL)
12831308
HANDLE_OPTION (else if, "$numberDecimal", BSON_TYPE_DECIMAL128, BSON_JSON_LF_DECIMAL128)
1284-
else if (!strcmp ("$timestamp", (const char *) val))
1309+
else if (HANDLE_OPTION_KEY_COMPARE ("$timestamp"))
12851310
{
1311+
HANDLE_OPTION_TYPE_CHECK ("$timestamp", BSON_TYPE_TIMESTAMP);
12861312
bson->bson_type = BSON_TYPE_TIMESTAMP;
12871313
bson->read_state = BSON_JSON_IN_BSON_TYPE_TIMESTAMP_STARTMAP;
12881314
}
1289-
else if (!strcmp ("$regularExpression", (const char *) val))
1315+
else if (HANDLE_OPTION_KEY_COMPARE ("$regularExpression"))
12901316
{
1317+
HANDLE_OPTION_TYPE_CHECK ("$regularExpression", BSON_TYPE_REGEX);
12911318
bson->bson_type = BSON_TYPE_REGEX;
12921319
bson->read_state = BSON_JSON_IN_BSON_TYPE_REGEX_STARTMAP;
12931320
}
1294-
else if (!strcmp ("$dbPointer", (const char *) val))
1321+
else if (HANDLE_OPTION_KEY_COMPARE ("$dbPointer"))
12951322
{
1323+
HANDLE_OPTION_TYPE_CHECK ("$dbPointer", BSON_TYPE_DBPOINTER);
1324+
12961325
/* start parsing "key": {"$dbPointer": {...}}, save "key" for later */
12971326
_bson_json_buf_set (&bson->dbpointer_key, bson->key, bson->key_buf.len);
12981327

12991328
bson->bson_type = BSON_TYPE_DBPOINTER;
13001329
bson->read_state = BSON_JSON_IN_BSON_TYPE_DBPOINTER_STARTMAP;
13011330
}
1302-
else if (!strcmp ("$code", (const char *) val))
1331+
else if (HANDLE_OPTION_KEY_COMPARE ("$code"))
13031332
{
1333+
// "$code" may come after "$scope".
1334+
if (bson->bson_type != BSON_TYPE_CODEWSCOPE) {
1335+
HANDLE_OPTION_TYPE_CHECK ("$code", BSON_TYPE_CODE);
1336+
}
13041337
_bson_json_read_code_or_scope_key (bson, false /* is_scope */, val, len);
13051338
}
1306-
else if (!strcmp ("$scope", (const char *) val))
1339+
else if (HANDLE_OPTION_KEY_COMPARE ("$scope"))
13071340
{
1341+
// "$scope" may come after "$code".
1342+
if (bson->bson_type != BSON_TYPE_CODE) {
1343+
HANDLE_OPTION_TYPE_CHECK ("$scope", BSON_TYPE_CODEWSCOPE);
1344+
}
13081345
_bson_json_read_code_or_scope_key (bson, true /* is_scope */, val, len);
13091346
}
13101347
else

src/libbson/tests/test-json.c

+8-6
Original file line numberDiff line numberDiff line change
@@ -2551,12 +2551,14 @@ test_bson_json_errors (void)
25512551
{
25522552
typedef const char *test_bson_json_error_t[2];
25532553
test_bson_json_error_t tests[] = {
2554-
{"{\"x\": {\"$numberLong\": 1}}", "Invalid state for integer read: INT64"},
2555-
{"{\"x\": {\"$binary\": 1}}", "Unexpected integer 1 in type \"binary\""},
2556-
{"{\"x\": {\"$numberInt\": true}}", "Invalid read of boolean in state IN_BSON_TYPE"},
2557-
{"{\"x\": {\"$dbPointer\": true}}", "Invalid read of boolean in state IN_BSON_TYPE_DBPOINTER_STARTMAP"},
2558-
{"[{\"$code\": {}}]", "Unexpected nested object value for \"$code\" key"},
2559-
{"{\"x\": {\"$numberInt\": \"8589934592\"}}", "Invalid input string \"8589934592\", looking for INT32"},
2554+
{BSON_STR ({"x" : {"$numberLong" : 1}}), "Invalid state for integer read: INT64"},
2555+
{BSON_STR ({"x" : {"$binary" : 1}}), "Unexpected integer 1 in type \"binary\""},
2556+
{BSON_STR ({"x" : {"$numberInt" : true}}), "Invalid read of boolean in state IN_BSON_TYPE"},
2557+
{BSON_STR ({"x" : {"$dbPointer" : true}}), "Invalid read of boolean in state IN_BSON_TYPE_DBPOINTER_STARTMAP"},
2558+
{BSON_STR ([ {"$code" : {}} ]), "Unexpected nested object value for \"$code\" key"},
2559+
{BSON_STR ([ {"$scope" : {}, "$dbPointer" : {"" : {"$code" : ""}}} ]),
2560+
"Invalid key \"$dbPointer\". Looking for values for type \"code\", got \"dbpointer\""},
2561+
{BSON_STR ({"x" : {"$numberInt" : "8589934592"}}), "Invalid input string \"8589934592\", looking for INT32"},
25602562
{0},
25612563
};
25622564

0 commit comments

Comments
 (0)