From ffa5de9dd8f53de62155fa2d9a821fc5d35aca67 Mon Sep 17 00:00:00 2001 From: Sabith Saheb Date: Fri, 3 Jul 2026 19:37:36 +0530 Subject: [PATCH] reject a lone sign token as a number --- src/lib_json/json_reader.cpp | 10 ++++++++++ src/test_lib_json/main.cpp | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 164d41d6f..1d9d1a5a0 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -530,6 +530,11 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { bool isNegative = *current == '-'; if (isNegative) ++current; + // A number token that is only a sign (e.g. "-") carries no digits; the loop + // below never runs and would otherwise decode it as zero. Reject it via + // decodeDouble, matching how a non-digit inside the token is handled. + if (current == token.end_) + return decodeDouble(token, decoded); // TODO: Help the compiler do the div and mod at compile time or get rid of // them. Value::LargestUInt maxIntegerValue = @@ -1575,6 +1580,11 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) { if (isNegative) { ++current; } + // A number token that is only a sign (e.g. "-") carries no digits; the loop + // below never runs and would otherwise decode it as zero. Reject it via + // decodeDouble, matching how a non-digit inside the token is handled. + if (current == token.end_) + return decodeDouble(token, decoded); // We assume we can represent the largest and smallest integer types as // unsigned integers with separate sign. This is only true if they can fit diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index 0d1c33064..137661949 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -3251,6 +3251,24 @@ JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseNumber) { } } +JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseBareSign) { + // A lone sign is not a valid number and used to be silently decoded as 0. + Json::CharReaderBuilder b; + CharReaderPtr reader(b.newCharReader()); + Json::String errs; + for (const char* doc : {"-", "[-]", "{\"a\":-}", "-x"}) { + Json::Value root; + JSONTEST_ASSERT(!reader->parse(doc, doc + std::strlen(doc), &root, &errs)); + } + // A sign followed by digits still parses. + { + Json::Value root; + char const doc[] = "-0"; + JSONTEST_ASSERT(reader->parse(doc, doc + std::strlen(doc), &root, &errs)); + JSONTEST_ASSERT_EQUAL(0, root.asInt()); + } +} + JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseSubnormal) { // Regression test for #1427: subnormal doubles make operator>> set failbit // even though it produced the correctly-rounded value, so they used to fail