From c48253682fce86589bc2cc96944d9a2f7feb1cf9 Mon Sep 17 00:00:00 2001 From: Tim Aitken Date: Sat, 16 Sep 2023 14:36:28 -0700 Subject: [PATCH] careful handling of long numbers --- src/lib_json/json_reader.cpp | 35 +++++++++++++++------------ src/test_lib_json/main.cpp | 47 +++++++++++++++++++++--------------- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index a43b932..0b4dde2 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -1440,12 +1440,14 @@ bool OurReader::readNumber(bool checkInf) { bool OurReader::readHexadecimal(void) { Location p = current_; - char c = '0'; // stopgap for already consumed character - // integral part - while ((c >= '0' && c <= '9') - || (c >= 'a' && c <= 'f') - || (c >= 'A' && c <= 'F')) - c = (current_ = p) < end_ ? *p++ : '\0'; + for (; p < end_; ++p) + { + char c = *p; + if ( (c < '0' || c > '9') + && (c < 'a' || c > 'f') + && (c < 'A' || c > 'F') ) break; + } + current_ = p; return true; } @@ -1680,23 +1682,24 @@ bool OurReader::decodeHexadecimal(Token& token) { } bool OurReader::decodeHexadecimal(Token& token, Value& decoded) { + Location current = token.start_; + if (current < token.end_ && *current == '0') ++current; + if (current < token.end_ && *current == 'x') ++current; Json::LargestUInt value = 0; - constexpr Json::LargestUInt top = - Json::LargestUInt(0xF) << ((sizeof(top) * 8) - 4); - - Location current = token.start_ + 2; - while (current < token.end_) { - Char c = *current++; + if (current >= token.end_) + return addError("Zero hexadecimal digits.", token); + if (current + (sizeof(value) * 2) < token.end_) + return addError("Token too long to be unsigned integer.", token, current); + for (; current < token.end_; ++current) { + Char c = *current; if (c >= 'a') c -= 'a' - 10; else if (c >= 'A') c -= 'A' - 10; else if (c >= '0') c -= '0'; - else return addError( - "Contains non-hexadecimal digits.", token, current); - if (value & top) return addError( - "Number is too large for unsigned integer.", token, current); + else + return addError("Contains non-hexadecimal digits.", token, current); value = value << 4 | static_cast(c); } decoded = value; diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index ac5fc42..b676092 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -3564,7 +3564,7 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowHexadecimal, disallowHex) { Json::Value root; Json::String errs; { - char const doc[] = R"({"a": 0x01})"; + char const doc[] = R"({ "a":0x9, "b":0xf, "c":0xF })"; bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs); JSONTEST_ASSERT(!ok); JSONTEST_ASSERT_STRING_EQUAL( @@ -3583,11 +3583,7 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowHexadecimal, hexObject) { { Json::Value root; Json::String errs; - char const doc[] = R"({ - "a":0x9, - "b":0xf, - "c":0xF - })"; + char const doc[] = R"({ "a":0x9, "b":0xf, "c":0xF })"; bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs); JSONTEST_ASSERT(ok); JSONTEST_ASSERT_STRING_EQUAL("", errs); @@ -3605,18 +3601,31 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowHexadecimal, hexNumbers) { struct TestData { bool ok; - Json::String in; Json::LargestUInt out; + Json::String in; }; + constexpr int _ = 0; // ignored const TestData test_data[] = { - {true, "9", 9}, // regular number - {true, "0x00", 0x00}, // zero - {true, "0x0123456789", 0x0123456789}, // numeric hex - {true, "0xABCDEF", 0xABCDEF}, // uppercase-letter hex - {true, "0xabcdef", 0xabcdef}, // lowercase-letter hex - {false, "x", 0 }, // leading x - {false, "0xx", 0 }, // extra x - {false, "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0} // too long + {true, 99, "99"}, // regular number + {true, 0x99, "0x99"}, // hexadecimal number + {false, _, "AA"}, // missing prefix + {false, _, "xAA"}, // partial prefix + {true, 0xAA, "0xAA"}, // with prefix + {true, 0x00, "0x00"}, // zero + {true, 0x0123456789, "0x0123456789"}, // numeric hex + {true, 0xABCDEF, "0xABCDEF"}, // uppercase-letter hex + {true, 0xabcdef, "0xabcdef"}, // lowercase-letter hex +#ifdef JSON_HAS_INT64 + {true, 0xFfffFfffFfffFfff, "0xFfffFfffFfffFfff"}, // max + {false, _, "0x1FfffFfffFfffFfff"}, // too long +#else + {true, 0xFfffFfff, "0xFfffFfff"}, // max + {false, _, "0x1FfffFfff"}, // too long +#endif + {false, _, "0x000000000000000000000000000000000000000"}, // too long + {false, _, "x"}, // leading x + {false, _, "0x"}, // empty number + {false, _, "0xx"} // extra x }; for (const auto& td : test_data) { Json::Value root; @@ -3627,10 +3636,10 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowHexadecimal, hexNumbers) { JSONTEST_ASSERT(td.ok == ok) << "in: " << td.in; if (td.ok) { - JSONTEST_ASSERT_EQUAL(0u, errs.size()); - JSONTEST_ASSERT(root.isConvertibleTo(Json::ValueType::uintValue)); - if (root.isConvertibleTo(Json::ValueType::uintValue)) - JSONTEST_ASSERT_EQUAL(root.asLargestUInt(), td.out); + JSONTEST_ASSERT_STRING_EQUAL("", errs); + JSONTEST_ASSERT(root.isUInt64()); + if (root.isUInt64()) + JSONTEST_ASSERT_EQUAL(td.out, root.asLargestUInt()); } else {