mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-27 17:10:46 +00:00
Reject non-canonical binaries (#6277)
* Reject non-canonical binaries * Review fixes * Cleanup Number2 class * Use enum instead of 0
This commit is contained in:
@@ -2411,6 +2411,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
{
|
||||
testcase("floatSet");
|
||||
using namespace test::jtx;
|
||||
using namespace wasm_float;
|
||||
|
||||
Env env{*this};
|
||||
OpenView ov{*env.current()};
|
||||
@@ -2435,7 +2436,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
|
||||
{
|
||||
auto const result =
|
||||
hfs.floatSet(1, wasm_float::maxExponent + normalExp + 1, 0);
|
||||
hfs.floatSet(1, wasmMaxExponent + normalExp + 1, 0);
|
||||
BEAST_EXPECT(!result) &&
|
||||
BEAST_EXPECT(
|
||||
result.error() ==
|
||||
@@ -2444,46 +2445,35 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
|
||||
{
|
||||
auto const result =
|
||||
hfs.floatSet(1, wasm_float::maxExponent + normalExp + 1, 0);
|
||||
BEAST_EXPECT(!result) &&
|
||||
BEAST_EXPECT(
|
||||
result.error() ==
|
||||
HostFunctionError::FLOAT_COMPUTATION_ERROR);
|
||||
}
|
||||
|
||||
{
|
||||
auto const result =
|
||||
hfs.floatSet(1, wasm_float::minExponent + normalExp - 1, 0);
|
||||
hfs.floatSet(1, wasmMinExponent + normalExp - 1, 0);
|
||||
BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatIntZero);
|
||||
}
|
||||
|
||||
{
|
||||
auto const result =
|
||||
hfs.floatSet(1, wasm_float::maxExponent + normalExp, 0);
|
||||
auto const result = hfs.floatSet(1, wasmMaxExponent + normalExp, 0);
|
||||
BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMaxExp);
|
||||
}
|
||||
|
||||
{
|
||||
auto const result =
|
||||
hfs.floatSet(-1, wasm_float::maxExponent + normalExp, 0);
|
||||
hfs.floatSet(-1, wasmMaxExponent + normalExp, 0);
|
||||
BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMinusMaxExp);
|
||||
}
|
||||
|
||||
{
|
||||
auto const result =
|
||||
hfs.floatSet(1, wasm_float::maxExponent + normalExp - 1, 0);
|
||||
hfs.floatSet(1, wasmMaxExponent + normalExp - 1, 0);
|
||||
BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatPreMaxExp);
|
||||
}
|
||||
|
||||
{
|
||||
auto const result =
|
||||
hfs.floatSet(STAmount::cMaxValue, wasm_float::maxExponent, 0);
|
||||
hfs.floatSet(STAmount::cMaxValue, wasmMaxExponent, 0);
|
||||
BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMaxIOU);
|
||||
}
|
||||
|
||||
{
|
||||
auto const result =
|
||||
hfs.floatSet(1, wasm_float::minExponent + normalExp, 0);
|
||||
auto const result = hfs.floatSet(1, wasmMinExponent + normalExp, 0);
|
||||
BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMinExp);
|
||||
}
|
||||
|
||||
@@ -3077,7 +3067,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testFloatNonIOU()
|
||||
testFloatSpecialCases()
|
||||
{
|
||||
testcase("float Xrp+Mpt");
|
||||
using namespace test::jtx;
|
||||
@@ -3136,6 +3126,20 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
!result &&
|
||||
result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED);
|
||||
}
|
||||
|
||||
testcase("float non-canonical");
|
||||
|
||||
{ // non-canonical mantissa 10 000 000 000 000 000
|
||||
Bytes x = float1;
|
||||
*reinterpret_cast<uint64_t*>(x.data()) = 0x0000C16FF286A3D4ull;
|
||||
{
|
||||
auto const result =
|
||||
hfs.floatCompare(makeSlice(x), makeSlice(float1));
|
||||
BEAST_EXPECT(
|
||||
!result &&
|
||||
result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -3153,7 +3157,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
testFloatRoot();
|
||||
testFloatPower();
|
||||
testFloatLog();
|
||||
testFloatNonIOU();
|
||||
testFloatSpecialCases();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -293,10 +293,10 @@ public:
|
||||
namespace wasm_float {
|
||||
|
||||
// The range for the mantissa and exponent when normalized
|
||||
static std::int64_t constexpr minMantissa = 1'000'000'000'000'000ll;
|
||||
static std::int64_t constexpr maxMantissa = (1ull << 54) - 1;
|
||||
static int constexpr minExponent = -96;
|
||||
static int constexpr maxExponent = 80;
|
||||
static std::int64_t constexpr wasmMinMantissa = 1'000'000'000'000'000ll;
|
||||
static std::int64_t constexpr wasmMaxMantissa = wasmMinMantissa * 10 - 1;
|
||||
static int constexpr wasmMinExponent = -96;
|
||||
static int constexpr wasmMaxExponent = 80;
|
||||
|
||||
} // namespace wasm_float
|
||||
|
||||
|
||||
@@ -16,17 +16,25 @@ namespace detail {
|
||||
class Number2 : public Number
|
||||
{
|
||||
protected:
|
||||
static Bytes const FLOAT_NULL;
|
||||
static Bytes const floatNull;
|
||||
static unsigned constexpr encodedFloatSize = 8;
|
||||
static int32_t constexpr encodedMantissaBits = 54;
|
||||
static int32_t constexpr encodedExponentBits = 8;
|
||||
|
||||
static_assert(wasmMinExponent < 0);
|
||||
|
||||
static uint64_t constexpr maxEncodedMantissa =
|
||||
(1ull << (encodedMantissaBits + 1)) - 1;
|
||||
|
||||
bool good_;
|
||||
|
||||
public:
|
||||
Number2(Slice const& data) : Number(), good_(false)
|
||||
{
|
||||
if (data.size() != 8)
|
||||
if (data.size() != encodedFloatSize)
|
||||
return;
|
||||
|
||||
if (std::ranges::equal(FLOAT_NULL, data))
|
||||
if (std::ranges::equal(floatNull, data))
|
||||
{
|
||||
good_ = true;
|
||||
return;
|
||||
@@ -36,45 +44,53 @@ public:
|
||||
if (!(v & STAmount::cIssuedCurrency))
|
||||
return;
|
||||
|
||||
int64_t const neg = (v & STAmount::cPositive) ? 1 : -1;
|
||||
int32_t const e = static_cast<uint8_t>((v >> (64 - 10)) & 0xFFull);
|
||||
if (e < 1 || e > 177)
|
||||
int32_t const e =
|
||||
static_cast<int32_t>((v >> encodedMantissaBits) & 0xFFull);
|
||||
int32_t const decodedExponent = e + wasmMinExponent - 1; // e - 97
|
||||
if (decodedExponent < wasmMinExponent ||
|
||||
decodedExponent > wasmMaxExponent)
|
||||
return;
|
||||
|
||||
int64_t const m = neg * static_cast<int64_t>(v & ((1ull << 54) - 1));
|
||||
int64_t const neg = (v & STAmount::cPositive) ? 1 : -1;
|
||||
int64_t const m =
|
||||
neg * static_cast<int64_t>(v & ((1ull << encodedMantissaBits) - 1));
|
||||
if (!m)
|
||||
return;
|
||||
|
||||
Number x(makeNumber(m, e + wasm_float::minExponent - 1));
|
||||
Number x(makeNumber(m, decodedExponent));
|
||||
if (m != x.mantissa() || decodedExponent != x.exponent())
|
||||
return; // not canonical
|
||||
*static_cast<Number*>(this) = x;
|
||||
|
||||
good_ = true;
|
||||
}
|
||||
|
||||
Number2() : Number(), good_(true)
|
||||
template <class T>
|
||||
Number2(T mantissa = 0, int32_t exponent = 0) : Number(), good_(false)
|
||||
{
|
||||
}
|
||||
if (!mantissa)
|
||||
{
|
||||
good_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Number2(int64_t x) : Number(makeNumber(x, 0)), good_(true)
|
||||
{
|
||||
}
|
||||
auto const n = makeNumber(mantissa, exponent);
|
||||
auto const e = n.exponent();
|
||||
if (e < wasmMinExponent)
|
||||
{
|
||||
good_ = true; // value is zero(as in Numbers behavior)
|
||||
return;
|
||||
}
|
||||
|
||||
Number2(uint64_t x) : Number(0), good_(false)
|
||||
{
|
||||
using mtype = std::invoke_result_t<decltype(&Number::mantissa), Number>;
|
||||
if (x <= std::numeric_limits<mtype>::max())
|
||||
*this = makeNumber(x, 0);
|
||||
else
|
||||
*this = makeNumber(x / 10, 1);
|
||||
if (e > wasmMaxExponent)
|
||||
return;
|
||||
|
||||
*static_cast<Number*>(this) = n;
|
||||
good_ = true;
|
||||
}
|
||||
|
||||
Number2(int64_t mantissa, int32_t exponent)
|
||||
: Number(makeNumber(mantissa, exponent)), good_(true)
|
||||
{
|
||||
}
|
||||
|
||||
Number2(Number const& n)
|
||||
: Number(makeNumber(n.mantissa(), n.exponent())), good_(true)
|
||||
: Number2(n.mantissa(), n.exponent()) // ensure Number canonized
|
||||
{
|
||||
}
|
||||
|
||||
@@ -100,35 +116,33 @@ public:
|
||||
Expected<Bytes, HostFunctionError>
|
||||
toBytes() const
|
||||
{
|
||||
uint64_t v = mantissa() >= 0 ? STAmount::cPositive : 0;
|
||||
if (!good_)
|
||||
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
|
||||
|
||||
auto const m = mantissa();
|
||||
auto const e = exponent();
|
||||
|
||||
uint64_t v = m >= 0 ? STAmount::cPositive : 0;
|
||||
v |= STAmount::cIssuedCurrency;
|
||||
|
||||
uint64_t const absM = mantissa() >= 0 ? mantissa() : -mantissa();
|
||||
uint64_t const absM = std::abs(m);
|
||||
if (!absM)
|
||||
{
|
||||
using etype =
|
||||
std::invoke_result_t<decltype(&Number::exponent), Number>;
|
||||
if (exponent() != std::numeric_limits<etype>::lowest())
|
||||
{
|
||||
return Unexpected(
|
||||
HostFunctionError::
|
||||
FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_LINE
|
||||
}
|
||||
return FLOAT_NULL;
|
||||
return floatNull;
|
||||
}
|
||||
else if (absM > wasm_float::maxMantissa)
|
||||
else if (absM > maxEncodedMantissa)
|
||||
{
|
||||
return Unexpected(
|
||||
HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_LINE
|
||||
}
|
||||
else if (exponent() > wasm_float::maxExponent)
|
||||
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
|
||||
else if (exponent() < wasm_float::minExponent)
|
||||
return FLOAT_NULL;
|
||||
|
||||
int const e = exponent() - wasm_float::minExponent + 1; //+97
|
||||
v |= absM;
|
||||
v |= ((uint64_t)e) << 54;
|
||||
|
||||
if (e > wasmMaxExponent)
|
||||
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
|
||||
else if (e < wasmMinExponent)
|
||||
return floatNull;
|
||||
uint64_t const normExp = e - wasmMinExponent + 1; //+97
|
||||
v |= normExp << encodedMantissaBits;
|
||||
|
||||
Serializer msg;
|
||||
msg.add64(v);
|
||||
@@ -147,7 +161,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
Bytes const Number2::FLOAT_NULL =
|
||||
Bytes const Number2::floatNull =
|
||||
{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
struct FloatState
|
||||
@@ -187,6 +201,8 @@ struct FloatState
|
||||
std::string
|
||||
floatToString(Slice const& data)
|
||||
{
|
||||
// set default mode as we don't expect it will be used here
|
||||
detail::FloatState rm(Number::rounding_mode::to_nearest);
|
||||
detail::Number2 const num(data);
|
||||
if (!num)
|
||||
{
|
||||
@@ -251,6 +267,8 @@ floatSetImpl(int64_t mantissa, int32_t exponent, int32_t mode)
|
||||
if (!rm)
|
||||
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
|
||||
detail::Number2 num(mantissa, exponent);
|
||||
if (!num)
|
||||
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
|
||||
return num.toBytes();
|
||||
}
|
||||
catch (...)
|
||||
@@ -264,6 +282,8 @@ floatCompareImpl(Slice const& x, Slice const& y)
|
||||
{
|
||||
try
|
||||
{
|
||||
// set default mode as we don't expect it will be used here
|
||||
detail::FloatState rm(Number::rounding_mode::to_nearest);
|
||||
detail::Number2 xx(x);
|
||||
if (!xx)
|
||||
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
|
||||
@@ -416,7 +436,7 @@ floatPowerImpl(Slice const& x, int32_t n, int32_t mode)
|
||||
{
|
||||
try
|
||||
{
|
||||
if ((n < 0) || (n > wasm_float::maxExponent))
|
||||
if ((n < 0) || (n > wasmMaxExponent))
|
||||
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
|
||||
|
||||
detail::FloatState rm(mode);
|
||||
|
||||
Reference in New Issue
Block a user