Repository navigation
Commit 3f91317
Fix integer overflow in the number parser digit counter
json_parse_digits returns the number of digits consumed as an
(int)(cursor - start). A JSON number whose digit run is 2^31 or
longer (a >= 2GiB document) wraps the counter negative, and the
mantissa_digits <= MAX_FAST_INTEGER_SIZE fast-path check on a long
wrapped uint64 accumulator then returns a small garbage Integer
instead of the number's true value:
$ ruby -rjson -e 'p JSON.parse(File.read("huge.json"))[0].bit_length'
63 # a 2_147_483_664 digit number cannot be 63 bits
The same wrap also defeats the exponent_digits >= 20 guard, letting
a wrapped exponent accumulator reach the exponent.
Widen the digit counters from int to ssize_t, so the routing
comparisons (<= 19, == 20, > 18, >= 20) see the true count and
route huge numbers to the existing large-number paths, which
already derive their lengths from the raw text. ssize_t keeps the
signed semantics the int counters relied on -- decimal_point_pos
starts at -1, and json_decode_float does signed arithmetic in
mantissa_digits + exponent < -307 with a negative int64 exponent --
which an unsigned size_t would break, while still being wide enough
to hold the true digit count.
Not feasible to regression-test from Ruby: the wrap needs a
>= 2GiB digit run, and the corrected path converts it through
rb_cstr2inum which is quadratic in the digit count. Validated
empirically against a 2_147_483_664 digit document: before this
change it returns a 63 bit Integer in 0.5s; after it correctly
enters the large-number path. All numeric boundary probes
(uint64 max, 19/20/21 digits, subnormals, 100k digit bignum) are
byte-identical before and after, and the full test suite passes.
Note: java/src/json/ext/Parser.java keeps the same int counters;
I could not build JRuby here so I left it out of this change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>1 parent cb82efc commit 3f91317
1 file changed
Lines changed: 9 additions & 9 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1120 | 1120 | | |
1121 | 1121 | | |
1122 | 1122 | | |
1123 | | - | |
| 1123 | + | |
1124 | 1124 | | |
1125 | 1125 | | |
1126 | 1126 | | |
| |||
1164 | 1164 | | |
1165 | 1165 | | |
1166 | 1166 | | |
1167 | | - | |
| 1167 | + | |
1168 | 1168 | | |
1169 | 1169 | | |
1170 | 1170 | | |
| |||
1421 | 1421 | | |
1422 | 1422 | | |
1423 | 1423 | | |
1424 | | - | |
| 1424 | + | |
1425 | 1425 | | |
1426 | 1426 | | |
1427 | 1427 | | |
| |||
1454 | 1454 | | |
1455 | 1455 | | |
1456 | 1456 | | |
1457 | | - | |
| 1457 | + | |
1458 | 1458 | | |
1459 | 1459 | | |
1460 | 1460 | | |
| |||
1463 | 1463 | | |
1464 | 1464 | | |
1465 | 1465 | | |
1466 | | - | |
| 1466 | + | |
1467 | 1467 | | |
1468 | 1468 | | |
1469 | 1469 | | |
| |||
1473 | 1473 | | |
1474 | 1474 | | |
1475 | 1475 | | |
1476 | | - | |
| 1476 | + | |
1477 | 1477 | | |
1478 | 1478 | | |
1479 | 1479 | | |
1480 | | - | |
| 1480 | + | |
1481 | 1481 | | |
1482 | 1482 | | |
1483 | 1483 | | |
| |||
1489 | 1489 | | |
1490 | 1490 | | |
1491 | 1491 | | |
1492 | | - | |
| 1492 | + | |
1493 | 1493 | | |
1494 | 1494 | | |
1495 | 1495 | | |
| |||
1510 | 1510 | | |
1511 | 1511 | | |
1512 | 1512 | | |
1513 | | - | |
| 1513 | + | |
1514 | 1514 | | |
1515 | 1515 | | |
1516 | 1516 | | |
| |||
0 commit comments