mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-29 07:25:51 +00:00
Properly handle edge-cases when parsing JSON integers (RIPD-470):
* Properly handle both unsigned and signed integers * Return parsing error for overlong JSON numbers * Implement unit test checking the edge cases that are of interest
This commit is contained in:
@@ -38,6 +38,40 @@ public:
|
|||||||
pass ();
|
pass ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void testMaxInts ()
|
||||||
|
{
|
||||||
|
char const* s1 (
|
||||||
|
"{\"max_uint\":4294967295"
|
||||||
|
",\"min_int\":-2147483648"
|
||||||
|
",\"max_int\":2147483647"
|
||||||
|
",\"an_int\":2147483646"
|
||||||
|
",\"a_uint\":2147483648}"
|
||||||
|
);
|
||||||
|
Json::Value j1;
|
||||||
|
Json::Reader r1;
|
||||||
|
|
||||||
|
expect (r1.parse (s1, j1), "parsing integer edge cases");
|
||||||
|
expect (j1["max_uint"].asUInt() == 4294967295, "max_uint");
|
||||||
|
expect (j1["min_int"].asInt() == -2147483648, "min_int");
|
||||||
|
expect (j1["max_int"].asInt() == 2147483647, "max_int");
|
||||||
|
expect (j1["an_int"].asInt() == 2147483646, "an_int");
|
||||||
|
expect (j1["a_uint"].asUInt() == 2147483648, "a_uint");
|
||||||
|
|
||||||
|
char const* s2 ("{\"overflow_uint\":4294967296}");
|
||||||
|
Json::Value j2;
|
||||||
|
Json::Reader r2;
|
||||||
|
|
||||||
|
expect (!r2.parse (s2, j2), "parsing unsigned integer that overflows");
|
||||||
|
|
||||||
|
char const* s3 ("{\"underflow_int\":-2147483649}");
|
||||||
|
Json::Value j3;
|
||||||
|
Json::Reader r3;
|
||||||
|
|
||||||
|
expect (!r3.parse (s3, j3), "parsing signed integer that underflows");
|
||||||
|
|
||||||
|
pass ();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
test_copy ()
|
test_copy ()
|
||||||
{
|
{
|
||||||
@@ -86,6 +120,7 @@ public:
|
|||||||
|
|
||||||
void run ()
|
void run ()
|
||||||
{
|
{
|
||||||
|
testMaxInts ();
|
||||||
testBadJson ();
|
testBadJson ();
|
||||||
test_copy ();
|
test_copy ();
|
||||||
test_move ();
|
test_move ();
|
||||||
|
|||||||
@@ -682,29 +682,57 @@ Reader::decodeNumber ( Token& token )
|
|||||||
if ( isNegative )
|
if ( isNegative )
|
||||||
++current;
|
++current;
|
||||||
|
|
||||||
Value::UInt threshold = (isNegative ? Value::UInt (-Value::minInt)
|
std::int64_t value = 0;
|
||||||
: Value::maxUInt) / 10;
|
|
||||||
Value::UInt value = 0;
|
|
||||||
|
|
||||||
while ( current < token.end_ )
|
static_assert(sizeof(value) > sizeof(Value::maxUInt),
|
||||||
|
"The JSON integer overflow logic will need to be reworked.");
|
||||||
|
|
||||||
|
while (current < token.end_ && (value <= Value::maxUInt))
|
||||||
{
|
{
|
||||||
Char c = *current++;
|
Char c = *current++;
|
||||||
|
|
||||||
if ( c < '0' || c > '9' )
|
if ( c < '0' || c > '9' )
|
||||||
return addError ( "'" + std::string ( token.start_, token.end_ ) + "' is not a number.", token );
|
{
|
||||||
|
return addError ( "'" + std::string ( token.start_, token.end_ ) +
|
||||||
|
"' is not a number.", token );
|
||||||
|
}
|
||||||
|
|
||||||
if ( value >= threshold )
|
value = (value * 10) + (c - '0');
|
||||||
return decodeDouble ( token );
|
}
|
||||||
|
|
||||||
value = value * 10 + Value::UInt (c - '0');
|
// More tokens left -> input is larger than largest possible return value
|
||||||
|
if (current != token.end_)
|
||||||
|
{
|
||||||
|
return addError ( "'" + std::string ( token.start_, token.end_ ) +
|
||||||
|
"' exceeds the allowable range.", token );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isNegative )
|
if ( isNegative )
|
||||||
currentValue () = -Value::Int ( value );
|
{
|
||||||
else if ( value <= Value::UInt (Value::maxInt) )
|
value = -value;
|
||||||
currentValue () = Value::Int ( value );
|
|
||||||
|
if (value < Value::minInt || value > Value::maxInt)
|
||||||
|
{
|
||||||
|
return addError ( "'" + std::string ( token.start_, token.end_ ) +
|
||||||
|
"' exceeds the allowable range.", token );
|
||||||
|
}
|
||||||
|
|
||||||
|
currentValue () = static_cast<Value::Int>( value );
|
||||||
|
}
|
||||||
else
|
else
|
||||||
currentValue () = value;
|
{
|
||||||
|
if (value > Value::maxUInt)
|
||||||
|
{
|
||||||
|
return addError ( "'" + std::string ( token.start_, token.end_ ) +
|
||||||
|
"' exceeds the allowable range.", token );
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's representable as a signed integer, construct it as one.
|
||||||
|
if ( value <= Value::maxInt )
|
||||||
|
currentValue () = static_cast<Value::Int>( value );
|
||||||
|
else
|
||||||
|
currentValue () = static_cast<Value::UInt>( value );
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user