Skip to content

Commit c9add86

Browse files
[CDRIVER-3380] Fixup JSON specials and decimal128 parsing (#988)
* Fix JSON parsing of special keys that don't want object values * Fix handling of invalid specials that begin with 'n' * Handle edge case in decimal128 string parsing
1 parent 3ae8825 commit c9add86

File tree

4 files changed

+60
-8
lines changed

4 files changed

+60
-8
lines changed

src/libbson/src/bson/bson-decimal128.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */
542542
continue;
543543
}
544544

545-
if (ndigits_stored < 34) {
545+
if (ndigits_stored < BSON_DECIMAL128_MAX_DIGITS) {
546546
if (*str_read != '0' || found_nonzero) {
547547
if (!found_nonzero) {
548548
first_nonzero = ndigits_read;
@@ -634,7 +634,7 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */
634634
/* Shift exponent to significand and decrease */
635635
last_digit++;
636636

637-
if (last_digit - first_digit > BSON_DECIMAL128_MAX_DIGITS) {
637+
if (last_digit - first_digit >= BSON_DECIMAL128_MAX_DIGITS) {
638638
/* The exponent is too great to shift into the significand. */
639639
if (significant_digits == 0) {
640640
/* Value is zero, we are allowed to clamp the exponent. */

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

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,18 +1174,59 @@ _bson_json_read_start_map (bson_json_reader_t *reader) /* IN */
11741174
BASIC_CB_PREAMBLE;
11751175

11761176
if (bson->read_state == BSON_JSON_IN_BSON_TYPE) {
1177-
if (bson->bson_state == BSON_JSON_LF_DATE) {
1177+
switch (bson->bson_state) {
1178+
case BSON_JSON_LF_DATE:
11781179
bson->read_state = BSON_JSON_IN_BSON_TYPE_DATE_NUMBERLONG;
1179-
} else if (bson->bson_state == BSON_JSON_LF_BINARY) {
1180+
break;
1181+
case BSON_JSON_LF_BINARY:
11801182
bson->read_state = BSON_JSON_IN_BSON_TYPE_BINARY_VALUES;
1181-
} else if (bson->bson_state == BSON_JSON_LF_TYPE) {
1183+
break;
1184+
case BSON_JSON_LF_TYPE:
11821185
/* special case, we started parsing {$type: {$numberInt: "2"}} and we
11831186
* expected a legacy Binary format. now we see the second "{", so
11841187
* backtrack and parse $type query operator. */
11851188
bson->read_state = BSON_JSON_IN_START_MAP;
11861189
STACK_PUSH_DOC (bson_append_document_begin (
11871190
STACK_BSON_PARENT, key, len, STACK_BSON_CHILD));
11881191
_bson_json_save_map_key (bson, (const uint8_t *) "$type", 5);
1192+
break;
1193+
case BSON_JSON_LF_CODE:
1194+
case BSON_JSON_LF_DECIMAL128:
1195+
case BSON_JSON_LF_DOUBLE:
1196+
case BSON_JSON_LF_INT32:
1197+
case BSON_JSON_LF_INT64:
1198+
case BSON_JSON_LF_MAXKEY:
1199+
case BSON_JSON_LF_MINKEY:
1200+
case BSON_JSON_LF_OID:
1201+
case BSON_JSON_LF_OPTIONS:
1202+
case BSON_JSON_LF_REGEX:
1203+
/**
1204+
* NOTE: A read_state of BSON_JSON_IN_BSON_TYPE is used when "$regex" is
1205+
* found, but BSON_JSON_IN_BSON_TYPE_REGEX_STARTMAP is used for
1206+
* "$regularExpression", which will instead go to a below 'if else' branch
1207+
* instead of this switch statement. They're both called "regex" in their
1208+
* respective enumerators, but they behave differently when parsing.
1209+
*/
1210+
// fallthrough
1211+
case BSON_JSON_LF_REGULAR_EXPRESSION_OPTIONS:
1212+
case BSON_JSON_LF_REGULAR_EXPRESSION_PATTERN:
1213+
case BSON_JSON_LF_SYMBOL:
1214+
case BSON_JSON_LF_UNDEFINED:
1215+
case BSON_JSON_LF_UUID:
1216+
// These special keys do not expect objects as their values. Fail.
1217+
_bson_json_read_set_error (
1218+
reader,
1219+
"Unexpected nested object value for \"%s\" key",
1220+
reader->bson.unescaped.buf);
1221+
break;
1222+
case BSON_JSON_LF_DBPOINTER:
1223+
case BSON_JSON_LF_SCOPE:
1224+
case BSON_JSON_LF_TIMESTAMP_I:
1225+
case BSON_JSON_LF_TIMESTAMP_T:
1226+
default:
1227+
// These special LF keys aren't handled with BSON_JSON_IN_BSON_TYPE
1228+
BSON_UNREACHABLE (
1229+
"These LF values are handled with a different read_state");
11891230
}
11901231
} else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_STARTMAP) {
11911232
bson->read_state = BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES;

src/libbson/src/jsonsl/jsonsl.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -457,13 +457,23 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes)
457457
} else if (state->special_flags & JSONSL_SPECIALf_NULL ||
458458
state->special_flags & JSONSL_SPECIALf_NAN) {
459459
/* previous char was "n", are we parsing null or nan? */
460-
if (CUR_CHAR != 'u') {
460+
const bool not_u = CUR_CHAR != 'u';
461+
const bool not_a = tolower (CUR_CHAR) != 'a';
462+
if (not_u) {
461463
state->special_flags &= ~JSONSL_SPECIALf_NULL;
462464
}
463-
464-
if (tolower(CUR_CHAR) != 'a') {
465+
if (not_a) {
465466
state->special_flags &= ~JSONSL_SPECIALf_NAN;
466467
}
468+
if (not_u && not_a) {
469+
/* This verify will always fail, as we have an 'n'
470+
* followed by a character that is neither 'a' nor 'u'
471+
* (and hence cannot be "null"). The purpose of this
472+
* VERIFY_SPECIAL is to generate an error in tokenization
473+
* that stops if a bare 'n' cannot possibly be a "nan" or
474+
* a "null". */
475+
VERIFY_SPECIAL ("null", 4);
476+
}
467477
#endif
468478
}
469479
INCR_METRIC(SPECIAL_FASTPATH);

src/libbson/tests/test-json.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2727,6 +2727,7 @@ test_bson_json_errors (void)
27272727
"Invalid read of boolean in state IN_BSON_TYPE"},
27282728
{"{\"x\": {\"$dbPointer\": true}}",
27292729
"Invalid read of boolean in state IN_BSON_TYPE_DBPOINTER_STARTMAP"},
2730+
{"[{\"$code\": {}}]", "Unexpected nested object value for \"$code\" key"},
27302731
{"{\"x\": {\"$numberInt\": \"8589934592\"}}",
27312732
"Invalid input string \"8589934592\", looking for INT32"},
27322733
{0},

0 commit comments

Comments
 (0)