Compare commits

...

1 Commits

Author SHA1 Message Date
Bronek Kozicki
55d5655ff3 Handler int overflow from JSON parsed values 2025-11-03 16:59:21 +00:00
4 changed files with 219 additions and 2 deletions

View File

@@ -63,6 +63,7 @@
#include <memory>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
@@ -1106,7 +1107,8 @@ amountFromJson(SField const& name, Json::Value const& v)
}
else
{
parts.mantissa = -value.asInt();
std::int64_t const temp = value.asInt();
parts.mantissa = -temp;
parts.negative = true;
}
}

View File

@@ -29,8 +29,10 @@
#include <boost/regex.hpp>
#include <cstddef>
#include <limits>
#include <ostream>
#include <string>
#include <type_traits>
#include <utility>
namespace ripple {
@@ -188,7 +190,8 @@ numberFromJson(SField const& field, Json::Value const& value)
}
else
{
parts.mantissa = -value.asInt();
std::int64_t const temp = value.asInt();
parts.mantissa = -temp;
parts.negative = true;
}
}

View File

@@ -22,6 +22,7 @@
#include <xrpl/basics/random.h>
#include <xrpl/beast/unit_test.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/XRPAmount.h>
namespace ripple {
@@ -604,6 +605,207 @@ public:
#endif
}
void
testParseJson()
{
static_assert(!std::is_convertible_v<STAmount*, Number*>);
{
STAmount const stnum{sfNumber};
BEAST_EXPECT(stnum.getSType() == STI_AMOUNT);
BEAST_EXPECT(stnum.getText() == "0");
BEAST_EXPECT(stnum.isDefault() == true);
BEAST_EXPECT(stnum.value() == Number{0});
}
{
BEAST_EXPECT(
amountFromJson(sfNumber, Json::Value(42)) == XRPAmount(42));
BEAST_EXPECT(
amountFromJson(sfNumber, Json::Value(-42)) == XRPAmount(-42));
BEAST_EXPECT(
amountFromJson(sfNumber, Json::UInt(42)) == XRPAmount(42));
BEAST_EXPECT(amountFromJson(sfNumber, "-123") == XRPAmount(-123));
BEAST_EXPECT(amountFromJson(sfNumber, "123") == XRPAmount(123));
BEAST_EXPECT(amountFromJson(sfNumber, "-123") == XRPAmount(-123));
BEAST_EXPECT(amountFromJson(sfNumber, "3.14e2") == XRPAmount(314));
BEAST_EXPECT(
amountFromJson(sfNumber, "-3.14e2") == XRPAmount(-314));
BEAST_EXPECT(amountFromJson(sfNumber, "0") == XRPAmount(0));
BEAST_EXPECT(amountFromJson(sfNumber, "-0") == XRPAmount(0));
BEAST_EXPECT(
amountFromJson(sfNumber, std::numeric_limits<int>::min()) ==
XRPAmount(std::numeric_limits<int>::min()));
BEAST_EXPECT(
amountFromJson(
sfNumber,
std::to_string(std::numeric_limits<int>::min())) ==
XRPAmount(std::numeric_limits<int>::min()));
// XRP does not handle fractional part
try
{
auto _ = amountFromJson(sfNumber, "0.0");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected =
"XRP and MPT must be specified as integral amount.";
BEAST_EXPECT(e.what() == expected);
}
// XRP does not handle fractional part
try
{
auto _ = amountFromJson(sfNumber, "1000e-2");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected =
"XRP and MPT must be specified as integral amount.";
BEAST_EXPECT(e.what() == expected);
}
// Obvious non-numbers tested here
try
{
auto _ = amountFromJson(sfNumber, "");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'' is not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = amountFromJson(sfNumber, "e");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'e' is not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = amountFromJson(sfNumber, "1e");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'1e' is not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = amountFromJson(sfNumber, "e2");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'e2' is not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = amountFromJson(sfNumber, Json::Value());
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected =
"XRP may not be specified with a null Json value";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = amountFromJson(
sfNumber,
"123456789012345678901234567890123456789012345678901234"
"5678"
"901234567890123456789012345678901234567890123456789012"
"3456"
"78901234567890123456789012345678901234567890");
BEAST_EXPECT(false);
}
catch (std::bad_cast const& e)
{
BEAST_EXPECT(true);
}
// We do not handle leading zeros
try
{
auto _ = amountFromJson(sfNumber, "001");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'001' is not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = amountFromJson(sfNumber, "000.0");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'000.0' is not a number";
BEAST_EXPECT(e.what() == expected);
}
// We do not handle dangling dot
try
{
auto _ = amountFromJson(sfNumber, ".1");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'.1' is not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = amountFromJson(sfNumber, "1.");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'1.' is not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = amountFromJson(sfNumber, "1.e3");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'1.e3' is not a number";
BEAST_EXPECT(e.what() == expected);
}
}
}
void
testConvertXRP()
{
@@ -1041,6 +1243,7 @@ public:
testArithmetic();
testUnderflow();
testRounding();
testParseJson();
testConvertXRP();
testConvertIOU();
testCanAddXRP();

View File

@@ -21,6 +21,7 @@
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/json/json_forwards.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STNumber.h>
@@ -144,6 +145,14 @@ struct STNumber_test : public beast::unit_test::suite
numberFromJson(sfNumber, "-0.0e6") == STNumber(sfNumber, 0));
BEAST_EXPECT(
numberFromJson(sfNumber, "-0.000e6") == STNumber(sfNumber, 0));
BEAST_EXPECT(
numberFromJson(sfNumber, std::numeric_limits<int>::min()) ==
STNumber(sfNumber, Number(std::numeric_limits<int>::min(), 0)));
BEAST_EXPECT(
numberFromJson(
sfNumber,
std::to_string(std::numeric_limits<int>::min())) ==
STNumber(sfNumber, Number(std::numeric_limits<int>::min(), 0)));
// Obvious non-numbers tested here
try