Skip to content

Commit c482536

Browse files
author
Tim Aitken
committed
careful handling of long numbers
1 parent 18dc444 commit c482536

File tree

2 files changed

+47
-35
lines changed

2 files changed

+47
-35
lines changed

src/lib_json/json_reader.cpp

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,12 +1440,14 @@ bool OurReader::readNumber(bool checkInf) {
14401440

14411441
bool OurReader::readHexadecimal(void) {
14421442
Location p = current_;
1443-
char c = '0'; // stopgap for already consumed character
1444-
// integral part
1445-
while ((c >= '0' && c <= '9')
1446-
|| (c >= 'a' && c <= 'f')
1447-
|| (c >= 'A' && c <= 'F'))
1448-
c = (current_ = p) < end_ ? *p++ : '\0';
1443+
for (; p < end_; ++p)
1444+
{
1445+
char c = *p;
1446+
if ( (c < '0' || c > '9')
1447+
&& (c < 'a' || c > 'f')
1448+
&& (c < 'A' || c > 'F') ) break;
1449+
}
1450+
current_ = p;
14491451
return true;
14501452
}
14511453

@@ -1680,23 +1682,24 @@ bool OurReader::decodeHexadecimal(Token& token) {
16801682
}
16811683

16821684
bool OurReader::decodeHexadecimal(Token& token, Value& decoded) {
1685+
Location current = token.start_;
1686+
if (current < token.end_ && *current == '0') ++current;
1687+
if (current < token.end_ && *current == 'x') ++current;
16831688
Json::LargestUInt value = 0;
1684-
constexpr Json::LargestUInt top =
1685-
Json::LargestUInt(0xF) << ((sizeof(top) * 8) - 4);
1686-
1687-
Location current = token.start_ + 2;
1688-
while (current < token.end_) {
1689-
Char c = *current++;
1689+
if (current >= token.end_)
1690+
return addError("Zero hexadecimal digits.", token);
1691+
if (current + (sizeof(value) * 2) < token.end_)
1692+
return addError("Token too long to be unsigned integer.", token, current);
1693+
for (; current < token.end_; ++current) {
1694+
Char c = *current;
16901695
if (c >= 'a')
16911696
c -= 'a' - 10;
16921697
else if (c >= 'A')
16931698
c -= 'A' - 10;
16941699
else if (c >= '0')
16951700
c -= '0';
1696-
else return addError(
1697-
"Contains non-hexadecimal digits.", token, current);
1698-
if (value & top) return addError(
1699-
"Number is too large for unsigned integer.", token, current);
1701+
else
1702+
return addError("Contains non-hexadecimal digits.", token, current);
17001703
value = value << 4 | static_cast<Value::UInt>(c);
17011704
}
17021705
decoded = value;

src/test_lib_json/main.cpp

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3564,7 +3564,7 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowHexadecimal, disallowHex) {
35643564
Json::Value root;
35653565
Json::String errs;
35663566
{
3567-
char const doc[] = R"({"a": 0x01})";
3567+
char const doc[] = R"({ "a":0x9, "b":0xf, "c":0xF })";
35683568
bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
35693569
JSONTEST_ASSERT(!ok);
35703570
JSONTEST_ASSERT_STRING_EQUAL(
@@ -3583,11 +3583,7 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowHexadecimal, hexObject) {
35833583
{
35843584
Json::Value root;
35853585
Json::String errs;
3586-
char const doc[] = R"({
3587-
"a":0x9,
3588-
"b":0xf,
3589-
"c":0xF
3590-
})";
3586+
char const doc[] = R"({ "a":0x9, "b":0xf, "c":0xF })";
35913587
bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
35923588
JSONTEST_ASSERT(ok);
35933589
JSONTEST_ASSERT_STRING_EQUAL("", errs);
@@ -3605,18 +3601,31 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowHexadecimal, hexNumbers) {
36053601

36063602
struct TestData {
36073603
bool ok;
3608-
Json::String in;
36093604
Json::LargestUInt out;
3605+
Json::String in;
36103606
};
3607+
constexpr int _ = 0; // ignored
36113608
const TestData test_data[] = {
3612-
{true, "9", 9}, // regular number
3613-
{true, "0x00", 0x00}, // zero
3614-
{true, "0x0123456789", 0x0123456789}, // numeric hex
3615-
{true, "0xABCDEF", 0xABCDEF}, // uppercase-letter hex
3616-
{true, "0xabcdef", 0xabcdef}, // lowercase-letter hex
3617-
{false, "x", 0 }, // leading x
3618-
{false, "0xx", 0 }, // extra x
3619-
{false, "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0} // too long
3609+
{true, 99, "99"}, // regular number
3610+
{true, 0x99, "0x99"}, // hexadecimal number
3611+
{false, _, "AA"}, // missing prefix
3612+
{false, _, "xAA"}, // partial prefix
3613+
{true, 0xAA, "0xAA"}, // with prefix
3614+
{true, 0x00, "0x00"}, // zero
3615+
{true, 0x0123456789, "0x0123456789"}, // numeric hex
3616+
{true, 0xABCDEF, "0xABCDEF"}, // uppercase-letter hex
3617+
{true, 0xabcdef, "0xabcdef"}, // lowercase-letter hex
3618+
#ifdef JSON_HAS_INT64
3619+
{true, 0xFfffFfffFfffFfff, "0xFfffFfffFfffFfff"}, // max
3620+
{false, _, "0x1FfffFfffFfffFfff"}, // too long
3621+
#else
3622+
{true, 0xFfffFfff, "0xFfffFfff"}, // max
3623+
{false, _, "0x1FfffFfff"}, // too long
3624+
#endif
3625+
{false, _, "0x000000000000000000000000000000000000000"}, // too long
3626+
{false, _, "x"}, // leading x
3627+
{false, _, "0x"}, // empty number
3628+
{false, _, "0xx"} // extra x
36203629
};
36213630
for (const auto& td : test_data) {
36223631
Json::Value root;
@@ -3627,10 +3636,10 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowHexadecimal, hexNumbers) {
36273636
JSONTEST_ASSERT(td.ok == ok) << "in: " << td.in;
36283637
if (td.ok)
36293638
{
3630-
JSONTEST_ASSERT_EQUAL(0u, errs.size());
3631-
JSONTEST_ASSERT(root.isConvertibleTo(Json::ValueType::uintValue));
3632-
if (root.isConvertibleTo(Json::ValueType::uintValue))
3633-
JSONTEST_ASSERT_EQUAL(root.asLargestUInt(), td.out);
3639+
JSONTEST_ASSERT_STRING_EQUAL("", errs);
3640+
JSONTEST_ASSERT(root.isUInt64());
3641+
if (root.isUInt64())
3642+
JSONTEST_ASSERT_EQUAL(td.out, root.asLargestUInt());
36343643
}
36353644
else
36363645
{

0 commit comments

Comments
 (0)