mirror of
https://github.com/XRPLF/rippled.git
synced 2026-03-12 07:42:25 +00:00
Compare commits
1 Commits
develop
...
pratik/Fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe8403f9c4 |
12
.github/scripts/strategy-matrix/generate.py
vendored
12
.github/scripts/strategy-matrix/generate.py
vendored
@@ -236,10 +236,12 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
# names get truncated.
|
||||
# Add Address and Thread (both coupled with UB) sanitizers for specific bookworm distros.
|
||||
# GCC-Asan rippled-embedded tests are failing because of https://github.com/google/sanitizers/issues/856
|
||||
if (
|
||||
os["distro_version"] == "bookworm"
|
||||
and f"{os['compiler_name']}-{os['compiler_version']}" == "clang-20"
|
||||
):
|
||||
if os[
|
||||
"distro_version"
|
||||
] == "bookworm" and f"{os['compiler_name']}-{os['compiler_version']}" in [
|
||||
"gcc-13",
|
||||
"clang-20",
|
||||
]:
|
||||
# Add ASAN + UBSAN configuration.
|
||||
configurations.append(
|
||||
{
|
||||
@@ -250,7 +252,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
"build_type": build_type,
|
||||
"os": os,
|
||||
"architecture": architecture,
|
||||
"sanitizers": "address,undefinedbehavior",
|
||||
"sanitizers": "undefinedbehavior",
|
||||
}
|
||||
)
|
||||
# TSAN is deactivated due to seg faults with latest compilers.
|
||||
|
||||
@@ -207,7 +207,6 @@ jobs:
|
||||
run: |
|
||||
echo "ASAN_OPTIONS=print_stacktrace=1:detect_container_overflow=0:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp" >> ${GITHUB_ENV}
|
||||
echo "TSAN_OPTIONS=second_deadlock_stack=1:halt_on_error=0:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV}
|
||||
echo "UBSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV}
|
||||
echo "LSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV}
|
||||
|
||||
- name: Run the separate tests
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import re
|
||||
import os
|
||||
|
||||
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
||||
|
||||
@@ -126,6 +127,12 @@ class Xrpl(ConanFile):
|
||||
if self.settings.compiler in ["clang", "gcc"]:
|
||||
self.options["boost"].without_cobalt = True
|
||||
|
||||
# Check if environment variable exists
|
||||
if "SANITIZERS" in os.environ:
|
||||
sanitizers = os.environ["SANITIZERS"]
|
||||
if "Address" in sanitizers:
|
||||
self.default_options["fPIC"] = False
|
||||
|
||||
def requirements(self):
|
||||
# Conan 2 requires transitive headers to be specified
|
||||
transitive_headers_opt = (
|
||||
|
||||
@@ -67,8 +67,11 @@ private:
|
||||
}
|
||||
else
|
||||
{
|
||||
while (elapsed--)
|
||||
while (elapsed > 0)
|
||||
{
|
||||
--elapsed;
|
||||
m_value -= (m_value + Window - 1) / Window;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
: work_(boost::asio::make_work_guard(ios_))
|
||||
{
|
||||
threads_.reserve(concurrency);
|
||||
while (concurrency--)
|
||||
for (std::size_t i = 0; i < concurrency; ++i)
|
||||
threads_.emplace_back([&] { ios_.run(); });
|
||||
}
|
||||
|
||||
|
||||
@@ -53,8 +53,9 @@ read_varint(void const* buf, std::size_t buflen, std::size_t& t)
|
||||
return 1;
|
||||
}
|
||||
auto const used = n;
|
||||
while (n--)
|
||||
while (n > 0)
|
||||
{
|
||||
--n;
|
||||
auto const d = p[n];
|
||||
auto const t0 = t;
|
||||
t *= 127;
|
||||
|
||||
@@ -127,7 +127,8 @@ IOUAmount::operator-=(IOUAmount const& other)
|
||||
inline IOUAmount
|
||||
IOUAmount::operator-() const
|
||||
{
|
||||
return {-mantissa_, exponent_};
|
||||
// Negate in unsigned domain to avoid UB when mantissa_ == INT64_MIN
|
||||
return {static_cast<mantissa_type>(-static_cast<std::uint64_t>(mantissa_)), exponent_};
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
||||
@@ -112,7 +112,8 @@ public:
|
||||
XRPAmount
|
||||
operator-() const
|
||||
{
|
||||
return XRPAmount{-drops_};
|
||||
// Negate in unsigned domain to avoid UB when drops_ == INT64_MIN
|
||||
return XRPAmount{static_cast<value_type>(-static_cast<std::uint64_t>(drops_))};
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -11,7 +11,6 @@ float-cast-overflow:external
|
||||
float-divide-by-zero:external
|
||||
function:external
|
||||
implicit-integer-sign-change:external
|
||||
implicit-signed-integer-truncation::external
|
||||
implicit-signed-integer-truncation:external
|
||||
implicit-unsigned-integer-truncation:external
|
||||
integer-divide-by-zero:external
|
||||
@@ -71,134 +70,15 @@ vla-bound:boost
|
||||
vptr_check:boost
|
||||
vptr:boost
|
||||
|
||||
# Google protobuf
|
||||
# Google protobuf - intentional overflows in hash functions
|
||||
undefined:protobuf
|
||||
|
||||
# Suppress UBSan errors in rippled code by source file path
|
||||
undefined:src/libxrpl/basics/base64.cpp
|
||||
undefined:src/libxrpl/basics/Number.cpp
|
||||
undefined:src/libxrpl/beast/utility/beast_Journal.cpp
|
||||
undefined:src/libxrpl/crypto/RFC1751.cpp
|
||||
undefined:src/libxrpl/ledger/ApplyView.cpp
|
||||
undefined:src/libxrpl/ledger/View.cpp
|
||||
undefined:src/libxrpl/protocol/Permissions.cpp
|
||||
undefined:src/libxrpl/protocol/STAmount.cpp
|
||||
undefined:src/libxrpl/protocol/STPathSet.cpp
|
||||
undefined:src/libxrpl/protocol/tokens.cpp
|
||||
undefined:src/libxrpl/shamap/SHAMap.cpp
|
||||
undefined:src/test/app/Batch_test.cpp
|
||||
undefined:src/test/app/Invariants_test.cpp
|
||||
undefined:src/test/app/NFToken_test.cpp
|
||||
undefined:src/test/app/Offer_test.cpp
|
||||
undefined:src/test/app/Path_test.cpp
|
||||
undefined:src/test/basics/XRPAmount_test.cpp
|
||||
undefined:src/test/beast/LexicalCast_test.cpp
|
||||
undefined:src/test/jtx/impl/acctdelete.cpp
|
||||
undefined:src/test/ledger/SkipList_test.cpp
|
||||
undefined:src/test/rpc/Subscribe_test.cpp
|
||||
undefined:src/tests/libxrpl/basics/RangeSet.cpp
|
||||
undefined:src/xrpld/app/main/BasicApp.cpp
|
||||
undefined:src/xrpld/app/main/BasicApp.cpp
|
||||
undefined:src/xrpld/app/misc/detail/AmendmentTable.cpp
|
||||
undefined:src/xrpld/app/misc/NetworkOPs.cpp
|
||||
undefined:src/libxrpl/json/json_value.cpp
|
||||
undefined:src/xrpld/app/paths/detail/StrandFlow.h
|
||||
undefined:src/xrpld/app/tx/detail/NFTokenMint.cpp
|
||||
undefined:src/xrpld/app/tx/detail/SetOracle.cpp
|
||||
undefined:src/xrpld/core/detail/JobQueue.cpp
|
||||
undefined:src/xrpld/core/detail/Workers.cpp
|
||||
undefined:src/xrpld/rpc/detail/Role.cpp
|
||||
undefined:src/xrpld/rpc/handlers/GetAggregatePrice.cpp
|
||||
undefined:xrpl/basics/base_uint.h
|
||||
undefined:xrpl/basics/DecayingSample.h
|
||||
undefined:xrpl/beast/test/yield_to.h
|
||||
undefined:xrpl/beast/xor_shift_engine.h
|
||||
undefined:xrpl/nodestore/detail/varint.h
|
||||
undefined:xrpl/peerfinder/detail/Counts.h
|
||||
undefined:xrpl/protocol/nft.h
|
||||
|
||||
# basic_string.h:483:51: runtime error: unsigned integer overflow
|
||||
unsigned-integer-overflow:basic_string.h
|
||||
unsigned-integer-overflow:bits/chrono.h
|
||||
unsigned-integer-overflow:bits/random.h
|
||||
unsigned-integer-overflow:bits/random.tcc
|
||||
unsigned-integer-overflow:bits/stl_algobase.h
|
||||
unsigned-integer-overflow:bits/uniform_int_dist.h
|
||||
unsigned-integer-overflow:string_view
|
||||
|
||||
# runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'std::size_t' (aka 'unsigned long')
|
||||
unsigned-integer-overflow:src/libxrpl/basics/base64.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/basics/Number.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/crypto/RFC1751.cpp
|
||||
unsigned-integer-overflow:rc/libxrpl/json/json_value.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/ledger/ApplyView.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/ledger/View.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/protocol/Permissions.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/protocol/STAmount.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/protocol/STPathSet.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/protocol/tokens.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/shamap/SHAMap.cpp
|
||||
unsigned-integer-overflow:src/test/app/Batch_test.cpp
|
||||
unsigned-integer-overflow:src/test/app/Invariants_test.cpp
|
||||
unsigned-integer-overflow:src/test/app/NFToken_test.cpp
|
||||
unsigned-integer-overflow:src/test/app/Offer_test.cpp
|
||||
unsigned-integer-overflow:src/test/app/Path_test.cpp
|
||||
unsigned-integer-overflow:src/test/basics/XRPAmount_test.cpp
|
||||
unsigned-integer-overflow:src/test/beast/LexicalCast_test.cpp
|
||||
unsigned-integer-overflow:src/test/jtx/impl/acctdelete.cpp
|
||||
unsigned-integer-overflow:src/test/ledger/SkipList_test.cpp
|
||||
unsigned-integer-overflow:src/test/rpc/Subscribe_test.cpp
|
||||
unsigned-integer-overflow:src/tests/libxrpl/basics/RangeSet.cpp
|
||||
unsigned-integer-overflow:src/xrpld/app/main/BasicApp.cpp
|
||||
unsigned-integer-overflow:src/xrpld/app/misc/detail/AmendmentTable.cpp
|
||||
unsigned-integer-overflow:src/xrpld/app/misc/NetworkOPs.cpp
|
||||
unsigned-integer-overflow:src/xrpld/app/paths/detail/StrandFlow.h
|
||||
unsigned-integer-overflow:src/xrpld/app/tx/detail/NFTokenMint.cpp
|
||||
unsigned-integer-overflow:src/xrpld/app/tx/detail/SetOracle.cpp
|
||||
unsigned-integer-overflow:src/xrpld/rpc/detail/Role.cpp
|
||||
unsigned-integer-overflow:src/xrpld/rpc/handlers/GetAggregatePrice.cpp
|
||||
unsigned-integer-overflow:xrpl/basics/base_uint.h
|
||||
unsigned-integer-overflow:xrpl/basics/DecayingSample.h
|
||||
unsigned-integer-overflow:xrpl/beast/test/yield_to.h
|
||||
unsigned-integer-overflow:xrpl/beast/xor_shift_engine.h
|
||||
unsigned-integer-overflow:xrpl/nodestore/detail/varint.h
|
||||
unsigned-integer-overflow:xrpl/peerfinder/detail/Counts.h
|
||||
unsigned-integer-overflow:xrpl/protocol/nft.h
|
||||
|
||||
# Rippled intentional overflows and operations
|
||||
# STAmount uses intentional negation of INT64_MIN and overflow in arithmetic
|
||||
signed-integer-overflow:src/libxrpl/protocol/STAmount.cpp
|
||||
unsigned-integer-overflow:src/libxrpl/protocol/STAmount.cpp
|
||||
|
||||
# XRPAmount test intentional overflows
|
||||
signed-integer-overflow:src/test/basics/XRPAmount_test.cpp
|
||||
|
||||
# Peerfinder intentional overflow in counter arithmetic
|
||||
unsigned-integer-overflow:src/xrpld/peerfinder/detail/Counts.h
|
||||
|
||||
# Signed integer overflow suppressions
|
||||
signed-integer-overflow:src/test/beast/LexicalCast_test.cpp
|
||||
|
||||
# External library suppressions
|
||||
unsigned-integer-overflow:nudb/detail/xxhash.hpp
|
||||
|
||||
# Protobuf intentional overflows in hash functions
|
||||
# Protobuf uses intentional unsigned overflow for hash computation (stringpiece.h:393)
|
||||
unsigned-integer-overflow:google/protobuf/stubs/stringpiece.h
|
||||
|
||||
# gRPC intentional overflows
|
||||
# gRPC uses intentional overflow in timer calculations
|
||||
# gRPC intentional overflows in timer calculations
|
||||
unsigned-integer-overflow:grpc
|
||||
unsigned-integer-overflow:timer_manager.cc
|
||||
|
||||
# Standard library intentional overflows
|
||||
# These are intentional overflows in random number generation and character conversion
|
||||
unsigned-integer-overflow:__random/seed_seq.h
|
||||
unsigned-integer-overflow:__charconv/traits.h
|
||||
|
||||
|
||||
# Suppress errors in RocksDB
|
||||
# RocksDB uses intentional unsigned integer overflows in hash functions and CRC calculations
|
||||
# RocksDB intentional unsigned integer overflows in hash functions and CRC calculations
|
||||
unsigned-integer-overflow:rocks*/*/util/xxhash.h
|
||||
unsigned-integer-overflow:rocks*/*/util/xxph3.h
|
||||
unsigned-integer-overflow:rocks*/*/util/hash.cc
|
||||
@@ -210,13 +90,14 @@ unsigned-integer-overflow:rocks*/*/table/format.cc
|
||||
unsigned-integer-overflow:rocks*/*/table/block_based/block_based_table_builder.cc
|
||||
unsigned-integer-overflow:rocks*/*/table/block_based/reader_common.cc
|
||||
unsigned-integer-overflow:rocks*/*/db/version_set.cc
|
||||
|
||||
# RocksDB misaligned loads (intentional for performance on ARM64)
|
||||
alignment:rocks*/*/util/crc32c_arm64.cc
|
||||
undefined:rocks.*/*/util/crc32c_arm64.cc
|
||||
undefined:rocks.*/*/util/xxhash.h
|
||||
|
||||
# nudb intentional overflows in hash functions
|
||||
unsigned-integer-overflow:nudb/detail/xxhash.hpp
|
||||
alignment:nudb/detail/xxhash.hpp
|
||||
undefined:nudb
|
||||
|
||||
# Snappy compression library intentional overflows
|
||||
unsigned-integer-overflow:snappy.cc
|
||||
@@ -228,10 +109,28 @@ unsigned-integer-overflow:absl/base/internal/low_level_alloc.cc
|
||||
unsigned-integer-overflow:absl/hash/internal/hash.h
|
||||
unsigned-integer-overflow:absl/container/internal/raw_hash_set.h
|
||||
|
||||
# Standard library intentional overflows in chrono duration arithmetic
|
||||
# Standard library intentional overflows
|
||||
unsigned-integer-overflow:basic_string.h
|
||||
unsigned-integer-overflow:bits/chrono.h
|
||||
unsigned-integer-overflow:bits/random.h
|
||||
unsigned-integer-overflow:bits/random.tcc
|
||||
unsigned-integer-overflow:bits/stl_algobase.h
|
||||
unsigned-integer-overflow:bits/uniform_int_dist.h
|
||||
unsigned-integer-overflow:string_view
|
||||
unsigned-integer-overflow:__random/seed_seq.h
|
||||
unsigned-integer-overflow:__charconv/traits.h
|
||||
unsigned-integer-overflow:__chrono/duration.h
|
||||
|
||||
# Suppress undefined errors in RocksDB and nudb
|
||||
undefined:rocks.*/*/util/crc32c_arm64.cc
|
||||
undefined:rocks.*/*/util/xxhash.h
|
||||
undefined:nudb
|
||||
# =============================================================================
|
||||
# Rippled code suppressions
|
||||
# =============================================================================
|
||||
|
||||
# STAmount::operator+ uses unsigned domain for addition to avoid signed overflow
|
||||
unsigned-integer-overflow:operator+*STAmount*
|
||||
# STAmount::getRate uses unsigned shift and addition
|
||||
unsigned-integer-overflow:getRate*
|
||||
# STAmount::serialize uses unsigned bitwise operations
|
||||
unsigned-integer-overflow:*STAmount*serialize*
|
||||
|
||||
# nft::cipheredTaxon uses intentional uint32 wraparound (LCG permutation)
|
||||
unsigned-integer-overflow:cipheredTaxon
|
||||
|
||||
@@ -107,7 +107,7 @@ encode(void* dest, void const* src, std::size_t len)
|
||||
char const* in = static_cast<char const*>(src);
|
||||
auto const tab = base64::get_alphabet();
|
||||
|
||||
for (auto n = len / 3; n--;)
|
||||
for (auto n = len / 3; n > 0; --n)
|
||||
{
|
||||
*out++ = tab[(in[0] & 0xfc) >> 2];
|
||||
*out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
|
||||
|
||||
@@ -91,7 +91,8 @@ IOUAmount::normalize()
|
||||
bool const negative = (mantissa_ < 0);
|
||||
|
||||
if (negative)
|
||||
mantissa_ = -mantissa_;
|
||||
// Negate in unsigned domain to avoid UB when mantissa_ == INT64_MIN
|
||||
mantissa_ = static_cast<mantissa_type>(-static_cast<std::uint64_t>(mantissa_));
|
||||
|
||||
while ((mantissa_ < minMantissa) && (exponent_ > minExponent))
|
||||
{
|
||||
@@ -118,7 +119,8 @@ IOUAmount::normalize()
|
||||
Throw<std::overflow_error>("value overflow");
|
||||
|
||||
if (negative)
|
||||
mantissa_ = -mantissa_;
|
||||
// Negate back in unsigned domain to restore sign
|
||||
mantissa_ = static_cast<mantissa_type>(-static_cast<std::uint64_t>(mantissa_));
|
||||
}
|
||||
|
||||
IOUAmount::IOUAmount(Number const& other) : IOUAmount(fromNumber(other))
|
||||
|
||||
@@ -19,7 +19,8 @@ MPTAmount::operator-=(MPTAmount const& other)
|
||||
MPTAmount
|
||||
MPTAmount::operator-() const
|
||||
{
|
||||
return MPTAmount{-value_};
|
||||
// Negate in unsigned domain to avoid UB when value_ == INT64_MIN
|
||||
return MPTAmount{static_cast<value_type>(-static_cast<std::uint64_t>(value_))};
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -63,16 +63,18 @@ getInt64Value(STAmount const& amount, bool valid, char const* error)
|
||||
Throw<std::runtime_error>(error);
|
||||
XRPL_ASSERT(amount.exponent() == 0, "xrpl::getInt64Value : exponent is zero");
|
||||
|
||||
auto ret = static_cast<std::int64_t>(amount.mantissa());
|
||||
|
||||
// Verify mantissa fits in int64_t (roundtrip check)
|
||||
XRPL_ASSERT(
|
||||
static_cast<std::uint64_t>(ret) == amount.mantissa(),
|
||||
static_cast<std::uint64_t>(static_cast<std::int64_t>(amount.mantissa())) ==
|
||||
amount.mantissa(),
|
||||
"xrpl::getInt64Value : mantissa must roundtrip");
|
||||
|
||||
// Negate in unsigned domain then cast to signed to avoid undefined
|
||||
// behavior when mantissa would represent INT64_MIN as signed
|
||||
if (amount.negative())
|
||||
ret = -ret;
|
||||
return static_cast<std::int64_t>(-amount.mantissa());
|
||||
|
||||
return ret;
|
||||
return static_cast<std::int64_t>(amount.mantissa());
|
||||
}
|
||||
|
||||
static std::int64_t
|
||||
@@ -222,10 +224,12 @@ STAmount::STAmount(std::uint64_t mantissa, bool negative)
|
||||
STAmount::STAmount(XRPAmount const& amount)
|
||||
: mAsset(xrpIssue()), mOffset(0), mIsNegative(amount < beast::zero)
|
||||
{
|
||||
// Negate in unsigned domain to avoid undefined behavior when
|
||||
// amount.drops() == INT64_MIN
|
||||
if (mIsNegative)
|
||||
mValue = unsafe_cast<std::uint64_t>(-amount.drops());
|
||||
mValue = -static_cast<std::uint64_t>(amount.drops());
|
||||
else
|
||||
mValue = unsafe_cast<std::uint64_t>(amount.drops());
|
||||
mValue = static_cast<std::uint64_t>(amount.drops());
|
||||
|
||||
canonicalize();
|
||||
}
|
||||
@@ -259,11 +263,12 @@ STAmount::xrp() const
|
||||
if (!native())
|
||||
Throw<std::logic_error>("Cannot return non-native STAmount as XRPAmount");
|
||||
|
||||
auto drops = static_cast<XRPAmount::value_type>(mValue);
|
||||
XRPL_ASSERT(mOffset == 0, "xrpl::STAmount::xrp : amount is canonical");
|
||||
|
||||
if (mIsNegative)
|
||||
drops = -drops;
|
||||
// Cast to unsigned, negate if needed, then cast to signed to avoid
|
||||
// undefined behavior when mValue would represent INT64_MIN as signed
|
||||
auto drops = mIsNegative ? static_cast<XRPAmount::value_type>(-mValue)
|
||||
: static_cast<XRPAmount::value_type>(mValue);
|
||||
|
||||
return XRPAmount{drops};
|
||||
}
|
||||
@@ -274,11 +279,12 @@ STAmount::iou() const
|
||||
if (integral())
|
||||
Throw<std::logic_error>("Cannot return non-IOU STAmount as IOUAmount");
|
||||
|
||||
auto mantissa = static_cast<std::int64_t>(mValue);
|
||||
auto exponent = mOffset;
|
||||
|
||||
if (mIsNegative)
|
||||
mantissa = -mantissa;
|
||||
// Negate in unsigned domain then cast to signed to avoid undefined
|
||||
// behavior when mValue would represent INT64_MIN as signed
|
||||
auto mantissa =
|
||||
mIsNegative ? static_cast<std::int64_t>(-mValue) : static_cast<std::int64_t>(mValue);
|
||||
|
||||
return {mantissa, exponent};
|
||||
}
|
||||
@@ -289,11 +295,12 @@ STAmount::mpt() const
|
||||
if (!holds<MPTIssue>())
|
||||
Throw<std::logic_error>("Cannot return STAmount as MPTAmount");
|
||||
|
||||
auto value = static_cast<MPTAmount::value_type>(mValue);
|
||||
XRPL_ASSERT(mOffset == 0, "xrpl::STAmount::mpt : amount is canonical");
|
||||
|
||||
if (mIsNegative)
|
||||
value = -value;
|
||||
// Negate in unsigned domain then cast to signed to avoid undefined
|
||||
// behavior when mValue would represent INT64_MIN as signed
|
||||
auto value = mIsNegative ? static_cast<MPTAmount::value_type>(-mValue)
|
||||
: static_cast<MPTAmount::value_type>(mValue);
|
||||
|
||||
return MPTAmount{value};
|
||||
}
|
||||
@@ -304,8 +311,9 @@ STAmount::operator=(IOUAmount const& iou)
|
||||
XRPL_ASSERT(integral() == false, "xrpl::STAmount::operator=(IOUAmount) : is not integral");
|
||||
mOffset = iou.exponent();
|
||||
mIsNegative = iou < beast::zero;
|
||||
// Negate in unsigned domain to avoid UB when mantissa == INT64_MIN
|
||||
if (mIsNegative)
|
||||
mValue = static_cast<std::uint64_t>(-iou.mantissa());
|
||||
mValue = -static_cast<std::uint64_t>(iou.mantissa());
|
||||
else
|
||||
mValue = static_cast<std::uint64_t>(iou.mantissa());
|
||||
return *this;
|
||||
@@ -323,7 +331,9 @@ STAmount::operator=(Number const& number)
|
||||
{
|
||||
auto const originalMantissa = number.mantissa();
|
||||
mIsNegative = originalMantissa < 0;
|
||||
mValue = mIsNegative ? -originalMantissa : originalMantissa;
|
||||
// Negate in unsigned domain to avoid UB when mantissa == INT64_MIN
|
||||
mValue = mIsNegative ? -static_cast<std::uint64_t>(originalMantissa)
|
||||
: static_cast<std::uint64_t>(originalMantissa);
|
||||
mOffset = number.exponent();
|
||||
}
|
||||
canonicalize();
|
||||
@@ -366,9 +376,22 @@ operator+(STAmount const& v1, STAmount const& v2)
|
||||
}
|
||||
|
||||
if (v1.native())
|
||||
return {v1.getFName(), getSNValue(v1) + getSNValue(v2)};
|
||||
{
|
||||
// Perform addition in unsigned domain to avoid signed overflow UB,
|
||||
// then cast back to signed for the STAmount constructor
|
||||
auto const sum = static_cast<std::int64_t>(
|
||||
static_cast<std::uint64_t>(getSNValue(v1)) +
|
||||
static_cast<std::uint64_t>(getSNValue(v2)));
|
||||
return {v1.getFName(), sum};
|
||||
}
|
||||
if (v1.holds<MPTIssue>())
|
||||
return {v1.mAsset, v1.mpt().value() + v2.mpt().value()};
|
||||
{
|
||||
// Perform addition in unsigned domain to avoid signed overflow UB
|
||||
auto const sum = static_cast<MPTAmount::value_type>(
|
||||
static_cast<std::uint64_t>(v1.mpt().value()) +
|
||||
static_cast<std::uint64_t>(v2.mpt().value()));
|
||||
return {v1.mAsset, sum};
|
||||
}
|
||||
|
||||
if (getSTNumberSwitchover())
|
||||
{
|
||||
@@ -381,11 +404,13 @@ operator+(STAmount const& v1, STAmount const& v2)
|
||||
std::int64_t vv1 = static_cast<std::int64_t>(v1.mantissa());
|
||||
std::int64_t vv2 = static_cast<std::int64_t>(v2.mantissa());
|
||||
|
||||
// Negate in unsigned domain then cast back to signed to avoid UB
|
||||
// when vv1 or vv2 would represent INT64_MIN
|
||||
if (v1.negative())
|
||||
vv1 = -vv1;
|
||||
vv1 = static_cast<std::int64_t>(-static_cast<std::uint64_t>(vv1));
|
||||
|
||||
if (v2.negative())
|
||||
vv2 = -vv2;
|
||||
vv2 = static_cast<std::int64_t>(-static_cast<std::uint64_t>(vv2));
|
||||
|
||||
while (ov1 < ov2)
|
||||
{
|
||||
@@ -410,7 +435,7 @@ operator+(STAmount const& v1, STAmount const& v2)
|
||||
if (fv >= 0)
|
||||
return STAmount{v1.getFName(), v1.asset(), static_cast<std::uint64_t>(fv), ov1, false};
|
||||
|
||||
return STAmount{v1.getFName(), v1.asset(), static_cast<std::uint64_t>(-fv), ov1, true};
|
||||
return STAmount{v1.getFName(), v1.asset(), -static_cast<std::uint64_t>(fv), ov1, true};
|
||||
}
|
||||
|
||||
STAmount
|
||||
@@ -836,7 +861,9 @@ STAmount::canonicalize()
|
||||
auto set = [&](auto const& val) {
|
||||
auto const value = val.value();
|
||||
mIsNegative = value < 0;
|
||||
mValue = mIsNegative ? -value : value;
|
||||
// Negate in unsigned domain to avoid UB when value == INT64_MIN
|
||||
mValue = mIsNegative ? -static_cast<std::uint64_t>(value)
|
||||
: static_cast<std::uint64_t>(value);
|
||||
};
|
||||
if (native())
|
||||
set(XRPAmount{num});
|
||||
@@ -931,7 +958,9 @@ STAmount::set(std::int64_t v)
|
||||
if (v < 0)
|
||||
{
|
||||
mIsNegative = true;
|
||||
mValue = static_cast<std::uint64_t>(-v);
|
||||
// Cast to unsigned before negating to avoid undefined behavior
|
||||
// when v == INT64_MIN (negating INT64_MIN in signed is UB)
|
||||
mValue = -static_cast<std::uint64_t>(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -155,7 +155,7 @@ private:
|
||||
std::vector<Validator> list;
|
||||
std::string uri;
|
||||
FetchListConfig const& cfg;
|
||||
bool isRetry;
|
||||
bool isRetry{false};
|
||||
};
|
||||
std::vector<publisher> servers;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
testInteger(IntType in)
|
||||
{
|
||||
std::string s;
|
||||
IntType out(in + 1);
|
||||
IntType out{}; // Initialize to avoid relying on overflow behavior
|
||||
|
||||
expect(lexicalCastChecked(s, in));
|
||||
expect(lexicalCastChecked(out, s));
|
||||
|
||||
@@ -9,10 +9,10 @@ BasicApp::BasicApp(std::size_t numberOfThreads)
|
||||
work_.emplace(boost::asio::make_work_guard(io_context_));
|
||||
threads_.reserve(numberOfThreads);
|
||||
|
||||
while (numberOfThreads--)
|
||||
for (std::size_t i = 0; i < numberOfThreads; ++i)
|
||||
{
|
||||
threads_.emplace_back([this, numberOfThreads]() {
|
||||
beast::setCurrentThreadName("io svc #" + std::to_string(numberOfThreads));
|
||||
threads_.emplace_back([this, i]() {
|
||||
beast::setCurrentThreadName("io svc #" + std::to_string(i));
|
||||
this->io_context_.run();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -229,15 +229,29 @@ public:
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
private:
|
||||
// Helper to safely adjust a size_t counter by a signed value.
|
||||
// Direct `counter += n` triggers UBSAN unsigned-integer-overflow
|
||||
// when n is negative, because the implicit conversion of a negative
|
||||
// int to size_t wraps.
|
||||
static void
|
||||
adjustCounter(std::size_t& counter, int const n)
|
||||
{
|
||||
if (n >= 0)
|
||||
counter += static_cast<std::size_t>(n);
|
||||
else
|
||||
// Widen to int64_t before negating to avoid UB if n == INT_MIN
|
||||
counter -= static_cast<std::size_t>(-static_cast<std::int64_t>(n));
|
||||
}
|
||||
|
||||
// Adjusts counts based on the specified slot, in the direction indicated.
|
||||
void
|
||||
adjust(Slot const& s, int const n)
|
||||
{
|
||||
if (s.fixed())
|
||||
m_fixed += n;
|
||||
adjustCounter(m_fixed, n);
|
||||
|
||||
if (s.reserved())
|
||||
m_reserved += n;
|
||||
adjustCounter(m_reserved, n);
|
||||
|
||||
switch (s.state())
|
||||
{
|
||||
@@ -257,15 +271,15 @@ private:
|
||||
|
||||
case Slot::active:
|
||||
if (s.fixed())
|
||||
m_fixed_active += n;
|
||||
adjustCounter(m_fixed_active, n);
|
||||
if (!s.fixed() && !s.reserved())
|
||||
{
|
||||
if (s.inbound())
|
||||
m_in_active += n;
|
||||
adjustCounter(m_in_active, n);
|
||||
else
|
||||
m_out_active += n;
|
||||
adjustCounter(m_out_active, n);
|
||||
}
|
||||
m_active += n;
|
||||
adjustCounter(m_active, n);
|
||||
break;
|
||||
|
||||
case Slot::closing:
|
||||
|
||||
Reference in New Issue
Block a user