Skip to content

Commit bc1f4f4

Browse files
committed
Fix issues with signed/unsigned overflows
1 parent aebc7fa commit bc1f4f4

File tree

1 file changed

+31
-10
lines changed

1 file changed

+31
-10
lines changed

src/lib_json/json_reader.cpp

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <json/value.h>
1212
#endif // if !defined(JSON_IS_AMALGAMATION)
1313
#include <cassert>
14+
#include <cinttypes>
1415
#include <cstring>
1516
#include <istream>
1617
#include <limits>
@@ -1528,15 +1529,35 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) {
15281529
if (isNegative)
15291530
++current;
15301531

1531-
static constexpr auto positive_threshold = Value::maxLargestUInt / 10;
1532-
static constexpr auto positive_last_digit = Value::maxLargestUInt % 10;
1533-
static constexpr auto negative_threshold =
1534-
Value::LargestUInt(Value::minLargestInt) / 10;
1535-
static constexpr auto negative_last_digit =
1536-
Value::LargestUInt(Value::minLargestInt) % 10;
1537-
1538-
const auto threshold = isNegative ? negative_threshold : positive_threshold;
1539-
const auto last_digit =
1532+
// We assume we can represent the largest and smallest integer types as
1533+
// unsigned integers with separate sign. This is only true if they can fit
1534+
// into an unsigned integer.
1535+
static_assert(Value::maxLargestInt <= Value::maxLargestUInt);
1536+
1537+
// We need to convert minLargestInt into a positive number. The easiest way
1538+
// to do this conversion is to assume our "threshold" value of minLargestInt
1539+
// divided by 10 can fit in maxLargestInt when absolute valued. This should
1540+
// be a safe assumption.
1541+
static_assert(Value::minLargestInt <= -Value::maxLargestInt);
1542+
static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt);
1543+
1544+
static constexpr Value::LargestUInt positive_threshold =
1545+
Value::maxLargestInt / 10;
1546+
static constexpr Value::UInt positive_last_digit = Value::maxLargestInt % 10;
1547+
1548+
// For the negative values, we have to be more careful. Since typically
1549+
// -Value::minLargestInt will cause an overflow, we first divide by 10 and
1550+
// then take the inverse. This assumes that minLargestInt is only a single
1551+
// power of 10 different in magnitude, which we check above. For the last
1552+
// digit, we take the modulus before negating for the same reason.
1553+
static const Value::LargestUInt negative_threshold =
1554+
Value::LargestUInt(-(Value::minLargestInt / 10));
1555+
static const Value::UInt negative_last_digit =
1556+
Value::UInt(-(Value::minLargestInt % 10));
1557+
1558+
const Value::LargestUInt threshold =
1559+
isNegative ? negative_threshold : positive_threshold;
1560+
const Value::UInt last_digit =
15401561
isNegative ? negative_last_digit : positive_last_digit;
15411562

15421563
Value::LargestUInt value = 0;
@@ -1545,7 +1566,7 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) {
15451566
if (c < '0' || c > '9')
15461567
return decodeDouble(token, decoded);
15471568

1548-
const auto digit(static_cast<Value::UInt>(c - '0'));
1569+
const Value::UInt digit(static_cast<Value::UInt>(c - '0'));
15491570
if (value >= threshold) {
15501571
// We've hit or exceeded the max value divided by 10 (rounded down). If
15511572
// a) we've only just touched the limit, meaing value == threshold,

0 commit comments

Comments
 (0)