mirror of
https://github.com/XRPLF/rippled.git
synced 2026-03-23 05:02:30 +00:00
Compare commits
93 Commits
develop
...
pratik/Fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d494ee2a3 | ||
|
|
5a0b032925 | ||
|
|
6d0d0b7a9c | ||
|
|
f6cb683ab2 | ||
|
|
c5d650418c | ||
|
|
d9f54113b3 | ||
|
|
b22038d92f | ||
|
|
2f60ce71ff | ||
|
|
c9ff20d729 | ||
|
|
c2fd60dc46 | ||
|
|
ae4ab7d462 | ||
|
|
95a43f3f90 | ||
|
|
96187a0da8 | ||
|
|
e4831258b6 | ||
|
|
7a7a864611 | ||
|
|
aa3c4fbcc4 | ||
|
|
c4a94bb000 | ||
|
|
5e3342c48d | ||
|
|
ac7ddd0cef | ||
|
|
eebdb58107 | ||
|
|
4d43f2e083 | ||
|
|
b08b14c2cd | ||
|
|
a8ee7ef316 | ||
|
|
e75ffc7c25 | ||
|
|
ea2ab17b95 | ||
|
|
358c6d95bf | ||
|
|
f8fcccd684 | ||
|
|
484fc4ce9a | ||
|
|
81a18efb9e | ||
|
|
d70ac27f0c | ||
|
|
496354f1c9 | ||
|
|
eb2b421cd6 | ||
|
|
1a737ebb49 | ||
|
|
d6e9986502 | ||
|
|
253fbf6e83 | ||
|
|
e6455035d5 | ||
|
|
6029c65aa1 | ||
|
|
52c7d980d4 | ||
|
|
5982519fe0 | ||
|
|
8dd147d5e8 | ||
|
|
7b36580552 | ||
|
|
5b4c49f47b | ||
|
|
d5a4f36632 | ||
|
|
011f0a6320 | ||
|
|
654829294e | ||
|
|
68c9b20f2d | ||
|
|
69597e4b3b | ||
|
|
65ba439117 | ||
|
|
14e3e098d5 | ||
|
|
1a14b813b8 | ||
|
|
95c9851146 | ||
|
|
127f44a40b | ||
|
|
acca1c73cf | ||
|
|
567433d272 | ||
|
|
4600c381b9 | ||
|
|
be28f4d489 | ||
|
|
413aee0752 | ||
|
|
6b16a5a8ed | ||
|
|
fd53813746 | ||
|
|
bbb03e153e | ||
|
|
63b6ec98ea | ||
|
|
7ef9fb4290 | ||
|
|
49dcb6b60b | ||
|
|
26dd1fafe3 | ||
|
|
4b99771021 | ||
|
|
e6664fe4cf | ||
|
|
b5001bc258 | ||
|
|
5dacfa1938 | ||
|
|
394b256f02 | ||
|
|
5bfa38f6c5 | ||
|
|
7c6c49bf98 | ||
|
|
6334be1ff0 | ||
|
|
a9c3bb84ba | ||
|
|
ca99e40290 | ||
|
|
7612c1af0c | ||
|
|
67e40be1ab | ||
|
|
0132174a7b | ||
|
|
8773cc4bbf | ||
|
|
dabdadfff5 | ||
|
|
bfe2cd7893 | ||
|
|
0584c20f36 | ||
|
|
3ced0b27b7 | ||
|
|
f83b27f7dd | ||
|
|
cdb41b5376 | ||
|
|
f223c89a9f | ||
|
|
efe07c09f3 | ||
|
|
79cde8b199 | ||
|
|
2078ce01cf | ||
|
|
2770a9cdf3 | ||
|
|
05ef3b1ad8 | ||
|
|
7dd4dbe285 | ||
|
|
b32a5f2c08 | ||
|
|
df76002a44 |
14
.github/scripts/strategy-matrix/generate.py
vendored
14
.github/scripts/strategy-matrix/generate.py
vendored
@@ -236,21 +236,23 @@ 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 [
|
||||
"clang-20",
|
||||
"gcc-13",
|
||||
]:
|
||||
# Add ASAN + UBSAN configuration.
|
||||
configurations.append(
|
||||
{
|
||||
"config_name": config_name + "-asan-ubsan",
|
||||
"config_name": config_name + "-asan",
|
||||
"cmake_args": cmake_args,
|
||||
"cmake_target": cmake_target,
|
||||
"build_only": build_only,
|
||||
"build_type": build_type,
|
||||
"os": os,
|
||||
"architecture": architecture,
|
||||
"sanitizers": "address,undefinedbehavior",
|
||||
"sanitizers": "address",
|
||||
}
|
||||
)
|
||||
# TSAN is deactivated due to seg faults with latest compilers.
|
||||
|
||||
27
.github/workflows/reusable-build-test-config.yml
vendored
27
.github/workflows/reusable-build-test-config.yml
vendored
@@ -76,7 +76,8 @@ jobs:
|
||||
name: ${{ inputs.config_name }}
|
||||
runs-on: ${{ fromJSON(inputs.runs_on) }}
|
||||
container: ${{ inputs.image != '' && inputs.image || null }}
|
||||
timeout-minutes: 60
|
||||
# Sanitizer builds on GCC are taking longer than 60mins. Hence increasing the timeout to 90mins.
|
||||
timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 60 }}
|
||||
env:
|
||||
# Use a namespace to keep the objects separate for each configuration.
|
||||
CCACHE_NAMESPACE: ${{ inputs.config_name }}
|
||||
@@ -205,14 +206,22 @@ jobs:
|
||||
- name: Set sanitizer options
|
||||
if: ${{ !inputs.build_only && env.SANITIZERS_ENABLED == 'true' }}
|
||||
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}
|
||||
ASAN_OPTS="include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-asan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp"
|
||||
if [[ "${{ inputs.config_name }}" == *gcc* ]]; then
|
||||
ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0"
|
||||
fi
|
||||
echo "ASAN_OPTIONS=${ASAN_OPTS}" >> ${GITHUB_ENV}
|
||||
echo "TSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-tsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV}
|
||||
echo "UBSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV}
|
||||
echo "LSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-lsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV}
|
||||
|
||||
- name: Run the separate tests
|
||||
# We continue on error here because we want to try the Embedded tests before
|
||||
# failing. This will give us details on all the failures at once.
|
||||
continue-on-error: true
|
||||
if: ${{ !inputs.build_only }}
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
id: separate_tests
|
||||
# Windows locks some of the build files while running tests, and parallel jobs can collide
|
||||
env:
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
@@ -228,8 +237,14 @@ jobs:
|
||||
working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }}
|
||||
env:
|
||||
BUILD_NPROC: ${{ steps.nproc.outputs.nproc }}
|
||||
PARALLELISM: ${{ env.SANITIZERS_ENABLED == 'true' && steps.nproc.outputs.nproc || steps.nproc.outputs.nproc }}
|
||||
run: |
|
||||
./xrpld --unittest --unittest-jobs "${BUILD_NPROC}"
|
||||
./xrpld --unittest --unittest-jobs "${PARALLELISM}"
|
||||
|
||||
# Pipeline should fail if the separate tests failed.
|
||||
- name: Check results of the SeparateTests
|
||||
if: ${{ !inputs.build_only && steps.separate_tests.outcome == 'failure' }}
|
||||
run: exit 1
|
||||
|
||||
- name: Debug failure (Linux)
|
||||
if: ${{ failure() && runner.os == 'Linux' && !inputs.build_only }}
|
||||
|
||||
@@ -17,12 +17,10 @@ find_dependency(Boost
|
||||
chrono
|
||||
container
|
||||
context
|
||||
coroutine
|
||||
date_time
|
||||
filesystem
|
||||
program_options
|
||||
regex
|
||||
system
|
||||
thread)
|
||||
#[=========================================================[
|
||||
OpenSSL
|
||||
|
||||
@@ -22,7 +22,7 @@ target_compile_definitions(
|
||||
BOOST_FILESYSTEM_NO_DEPRECATED
|
||||
>
|
||||
$<$<NOT:$<BOOL:${boost_show_deprecated}>>:
|
||||
BOOST_COROUTINES_NO_DEPRECATION_WARNING
|
||||
BOOST_COROUTINES2_NO_DEPRECATION_WARNING
|
||||
BOOST_BEAST_ALLOW_DEPRECATED
|
||||
BOOST_FILESYSTEM_DEPRECATED
|
||||
>
|
||||
|
||||
@@ -4,13 +4,12 @@ include(XrplSanitizers)
|
||||
find_package(Boost REQUIRED
|
||||
COMPONENTS chrono
|
||||
container
|
||||
coroutine
|
||||
context
|
||||
date_time
|
||||
filesystem
|
||||
json
|
||||
program_options
|
||||
regex
|
||||
system
|
||||
thread)
|
||||
|
||||
add_library(xrpl_boost INTERFACE)
|
||||
@@ -21,7 +20,7 @@ target_link_libraries(
|
||||
INTERFACE Boost::headers
|
||||
Boost::chrono
|
||||
Boost::container
|
||||
Boost::coroutine
|
||||
Boost::context
|
||||
Boost::date_time
|
||||
Boost::filesystem
|
||||
Boost::json
|
||||
@@ -32,6 +31,26 @@ target_link_libraries(
|
||||
if (Boost_COMPILER)
|
||||
target_link_libraries(xrpl_boost INTERFACE Boost::disable_autolinking)
|
||||
endif ()
|
||||
|
||||
# GCC 14+ has a false positive -Wuninitialized warning in Boost.Coroutine2's
|
||||
# state.hpp when compiled with -O3. This is due to GCC's intentional behavior
|
||||
# change (Bug #98871, #119388) where warnings from inlined system header code
|
||||
# are no longer suppressed by -isystem. The warning occurs in operator|= in
|
||||
# boost/coroutine2/detail/state.hpp when inlined from push_control_block::destroy().
|
||||
# See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119388
|
||||
if (is_gcc AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14)
|
||||
target_compile_options(xrpl_boost INTERFACE -Wno-uninitialized)
|
||||
endif ()
|
||||
|
||||
# Boost.Context's ucontext backend has ASAN fiber-switching annotations
|
||||
# (start/finish_switch_fiber) that are compiled in when BOOST_USE_ASAN is defined.
|
||||
# This tells ASAN about coroutine stack switches, preventing false positive
|
||||
# stack-use-after-scope errors. BOOST_USE_UCONTEXT ensures the ucontext backend
|
||||
# is selected (fcontext does not support ASAN annotations).
|
||||
# These defines must match what Boost was compiled with (see conan/profiles/sanitizers).
|
||||
if (enable_asan)
|
||||
target_compile_definitions(xrpl_boost INTERFACE BOOST_USE_ASAN BOOST_USE_UCONTEXT)
|
||||
endif ()
|
||||
if (SANITIZERS_ENABLED AND is_clang)
|
||||
# TODO: gcc does not support -fsanitize-blacklist...can we do something else for gcc ?
|
||||
if (NOT Boost_INCLUDE_DIRS AND TARGET Boost::headers)
|
||||
|
||||
@@ -7,16 +7,21 @@ include(default)
|
||||
{% if compiler == "gcc" %}
|
||||
{% if "address" in sanitizers or "thread" in sanitizers or "undefinedbehavior" in sanitizers %}
|
||||
{% set sanitizer_list = [] %}
|
||||
{% set defines = [] %}
|
||||
{% set model_code = "" %}
|
||||
{% set extra_cxxflags = ["-fno-omit-frame-pointer", "-O1", "-Wno-stringop-overflow"] %}
|
||||
|
||||
{% if "address" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("address") %}
|
||||
{% set model_code = "-mcmodel=large" %}
|
||||
{% set _ = defines.append("BOOST_USE_ASAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% elif "thread" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("thread") %}
|
||||
{% set model_code = "-mcmodel=medium" %}
|
||||
{% set _ = extra_cxxflags.append("-Wno-tsan") %}
|
||||
{% set _ = defines.append("BOOST_USE_TSAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% endif %}
|
||||
|
||||
{% if "undefinedbehavior" in sanitizers %}
|
||||
@@ -29,16 +34,22 @@ include(default)
|
||||
tools.build:cxxflags+=['{{sanitizer_flags}} {{" ".join(extra_cxxflags)}}']
|
||||
tools.build:sharedlinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:exelinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:defines+={{defines}}
|
||||
{% endif %}
|
||||
{% elif compiler == "apple-clang" or compiler == "clang" %}
|
||||
{% if "address" in sanitizers or "thread" in sanitizers or "undefinedbehavior" in sanitizers %}
|
||||
{% set sanitizer_list = [] %}
|
||||
{% set defines = [] %}
|
||||
{% set extra_cxxflags = ["-fno-omit-frame-pointer", "-O1"] %}
|
||||
|
||||
{% if "address" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("address") %}
|
||||
{% set _ = defines.append("BOOST_USE_ASAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% elif "thread" in sanitizers %}
|
||||
{% set _ = sanitizer_list.append("thread") %}
|
||||
{% set _ = defines.append("BOOST_USE_TSAN")%}
|
||||
{% set _ = defines.append("BOOST_USE_UCONTEXT")%}
|
||||
{% endif %}
|
||||
|
||||
{% if "undefinedbehavior" in sanitizers %}
|
||||
@@ -52,8 +63,24 @@ include(default)
|
||||
tools.build:cxxflags+=['{{sanitizer_flags}} {{" ".join(extra_cxxflags)}}']
|
||||
tools.build:sharedlinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:exelinkflags+=['{{sanitizer_flags}}']
|
||||
tools.build:defines+={{defines}}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags"]
|
||||
tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags", "tools.build:defines"]
|
||||
|
||||
[options]
|
||||
{% if sanitizers %}
|
||||
{% if "address" in sanitizers %}
|
||||
# Build Boost.Context with ucontext backend (not fcontext) so that
|
||||
# ASAN fiber-switching annotations (__sanitizer_start/finish_switch_fiber)
|
||||
# are compiled into the library. fcontext (assembly) has no ASAN support.
|
||||
# define=BOOST_USE_ASAN=1 is critical: it must be defined when building
|
||||
# Boost.Context itself so the ucontext backend compiles in the ASAN annotations.
|
||||
boost/*:extra_b2_flags=context-impl=ucontext address-sanitizer=on define=BOOST_USE_ASAN=1
|
||||
boost/*:without_context=False
|
||||
# Boost stacktrace fails to build with some sanitizers
|
||||
boost/*:without_stacktrace=True
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
15
conanfile.py
15
conanfile.py
@@ -1,4 +1,5 @@
|
||||
import re
|
||||
import os
|
||||
|
||||
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
||||
|
||||
@@ -57,6 +58,9 @@ class Xrpl(ConanFile):
|
||||
"tests": False,
|
||||
"unity": False,
|
||||
"xrpld": False,
|
||||
"boost/*:without_context": False,
|
||||
"boost/*:without_coroutine": True,
|
||||
"boost/*:without_coroutine2": False,
|
||||
"date/*:header_only": True,
|
||||
"ed25519/*:shared": False,
|
||||
"grpc/*:shared": False,
|
||||
@@ -125,6 +129,14 @@ class Xrpl(ConanFile):
|
||||
self.options["boost"].visibility = "global"
|
||||
if self.settings.compiler in ["clang", "gcc"]:
|
||||
self.options["boost"].without_cobalt = True
|
||||
self.options["boost"].without_context = False
|
||||
self.options["boost"].without_coroutine = True
|
||||
self.options["boost"].without_coroutine2 = False
|
||||
# Check if environment variable exists
|
||||
if "SANITIZERS" in os.environ:
|
||||
sanitizers = os.environ["SANITIZERS"]
|
||||
if "address" in sanitizers.lower():
|
||||
self.default_options["fPIC"] = False
|
||||
|
||||
def requirements(self):
|
||||
# Conan 2 requires transitive headers to be specified
|
||||
@@ -196,7 +208,8 @@ class Xrpl(ConanFile):
|
||||
"boost::headers",
|
||||
"boost::chrono",
|
||||
"boost::container",
|
||||
"boost::coroutine",
|
||||
"boost::context",
|
||||
"boost::coroutine2",
|
||||
"boost::date_time",
|
||||
"boost::filesystem",
|
||||
"boost::json",
|
||||
|
||||
@@ -99,8 +99,10 @@ words:
|
||||
- endmacro
|
||||
- exceptioned
|
||||
- Falco
|
||||
- fcontext
|
||||
- finalizers
|
||||
- firewalled
|
||||
- flackiness
|
||||
- fmtdur
|
||||
- fsanitize
|
||||
- funclets
|
||||
@@ -111,6 +113,7 @@ words:
|
||||
- gpgcheck
|
||||
- gpgkey
|
||||
- hotwallet
|
||||
- hwaddress
|
||||
- hwrap
|
||||
- ifndef
|
||||
- inequation
|
||||
@@ -174,6 +177,7 @@ words:
|
||||
- nftpage
|
||||
- nikb
|
||||
- nonxrp
|
||||
- norecover
|
||||
- noripple
|
||||
- nudb
|
||||
- nullptr
|
||||
@@ -233,6 +237,7 @@ words:
|
||||
- soci
|
||||
- socidb
|
||||
- sslws
|
||||
- stackful
|
||||
- statsd
|
||||
- STATSDCOLLECTOR
|
||||
- stissue
|
||||
|
||||
8
docs/build/sanitizers.md
vendored
8
docs/build/sanitizers.md
vendored
@@ -89,8 +89,8 @@ cmake --build . --parallel 4
|
||||
**IMPORTANT**: ASAN with Boost produces many false positives. Use these options:
|
||||
|
||||
```bash
|
||||
export ASAN_OPTIONS="print_stacktrace=1:detect_container_overflow=0:suppressions=path/to/asan.supp:halt_on_error=0:log_path=asan.log"
|
||||
export LSAN_OPTIONS="suppressions=path/to/lsan.supp:halt_on_error=0:log_path=lsan.log"
|
||||
export ASAN_OPTIONS="include=sanitizers/suppressions/runtime-asan-options.txt:suppressions=sanitizers/suppressions/asan.supp"
|
||||
export LSAN_OPTIONS="include=sanitizers/suppressions/runtime-lsan-options.txt:suppressions=sanitizers/suppressions/lsan.supp"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
@@ -108,7 +108,7 @@ export LSAN_OPTIONS="suppressions=path/to/lsan.supp:halt_on_error=0:log_path=lsa
|
||||
### ThreadSanitizer (TSan)
|
||||
|
||||
```bash
|
||||
export TSAN_OPTIONS="suppressions=path/to/tsan.supp halt_on_error=0 log_path=tsan.log"
|
||||
export TSAN_OPTIONS="include=sanitizers/suppressions/runtime-tsan-options.txt:suppressions=sanitizers/suppressions/tsan.supp"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
@@ -129,7 +129,7 @@ More details [here](https://github.com/google/sanitizers/wiki/AddressSanitizerLe
|
||||
### UndefinedBehaviorSanitizer (UBSan)
|
||||
|
||||
```bash
|
||||
export UBSAN_OPTIONS="suppressions=path/to/ubsan.supp:print_stacktrace=1:halt_on_error=0:log_path=ubsan.log"
|
||||
export UBSAN_OPTIONS="include=sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=sanitizers/suppressions/ubsan.supp"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
|
||||
@@ -359,6 +359,7 @@ public:
|
||||
base_uint&
|
||||
operator&=(base_uint const& b)
|
||||
{
|
||||
XRPL_ASSERT(WIDTH == b.WIDTH, "input size mismatch");
|
||||
for (int i = 0; i < WIDTH; i++)
|
||||
data_[i] &= b.data_[i];
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/sanitizers.h>
|
||||
#include <xrpl/beast/type_name.h>
|
||||
|
||||
#include <exception>
|
||||
@@ -24,7 +25,7 @@ LogThrow(std::string const& title);
|
||||
control to the next matching exception handler, if any.
|
||||
Otherwise, std::terminate will be called.
|
||||
*/
|
||||
[[noreturn]] inline void
|
||||
[[noreturn]] XRPL_NO_SANITIZE_ADDRESS inline void
|
||||
Rethrow()
|
||||
{
|
||||
LogThrow("Re-throwing exception");
|
||||
@@ -32,7 +33,7 @@ Rethrow()
|
||||
}
|
||||
|
||||
template <class E, class... Args>
|
||||
[[noreturn]] inline void
|
||||
[[noreturn]] XRPL_NO_SANITIZE_ADDRESS inline void
|
||||
Throw(Args&&... args)
|
||||
{
|
||||
static_assert(
|
||||
|
||||
6
include/xrpl/basics/sanitizers.h
Normal file
6
include/xrpl/basics/sanitizers.h
Normal file
@@ -0,0 +1,6 @@
|
||||
// Helper to disable ASan/HwASan for specific functions
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define XRPL_NO_SANITIZE_ADDRESS __attribute__((no_sanitize("address", "hwaddress")))
|
||||
#else
|
||||
#define XRPL_NO_SANITIZE_ADDRESS
|
||||
#endif
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <class F>
|
||||
@@ -10,17 +8,15 @@ JobQueue::Coro::Coro(Coro_create_t, JobQueue& jq, JobType type, std::string cons
|
||||
, type_(type)
|
||||
, name_(name)
|
||||
, running_(false)
|
||||
, coro_(
|
||||
[this, fn = std::forward<F>(f)](
|
||||
boost::coroutines::asymmetric_coroutine<void>::push_type& do_yield) {
|
||||
yield_ = &do_yield;
|
||||
yield();
|
||||
fn(shared_from_this());
|
||||
, coro_([this, fn = std::forward<F>(f)](
|
||||
boost::coroutines2::asymmetric_coroutine<void>::push_type& do_yield) {
|
||||
yield_ = &do_yield;
|
||||
yield();
|
||||
fn(shared_from_this());
|
||||
#ifndef NDEBUG
|
||||
finished_ = true;
|
||||
finished_ = true;
|
||||
#endif
|
||||
},
|
||||
boost::coroutines::attributes(megabytes(1)))
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
@@ -80,6 +76,7 @@ JobQueue::Coro::resume()
|
||||
coro_();
|
||||
detail::getLocalValues().release();
|
||||
detail::getLocalValues().reset(saved);
|
||||
|
||||
std::lock_guard lk(mutex_run_);
|
||||
running_ = false;
|
||||
cv_.notify_all();
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <xrpl/core/detail/Workers.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <boost/coroutine/all.hpp>
|
||||
#include <boost/coroutine2/all.hpp>
|
||||
|
||||
#include <set>
|
||||
|
||||
@@ -48,8 +48,8 @@ public:
|
||||
std::mutex mutex_;
|
||||
std::mutex mutex_run_;
|
||||
std::condition_variable cv_;
|
||||
boost::coroutines::asymmetric_coroutine<void>::pull_type coro_;
|
||||
boost::coroutines::asymmetric_coroutine<void>::push_type* yield_;
|
||||
boost::coroutines2::coroutine<void>::pull_type coro_;
|
||||
boost::coroutines2::coroutine<void>::push_type* yield_;
|
||||
#ifndef NDEBUG
|
||||
bool finished_ = false;
|
||||
#endif
|
||||
|
||||
@@ -64,6 +64,11 @@ public:
|
||||
std::shared_ptr<SLE const>
|
||||
read(ReadView const& base, Keylet const& k) const;
|
||||
|
||||
/** Check only local items without delegating to base.
|
||||
Returns std::nullopt if key not found locally. */
|
||||
std::optional<std::shared_ptr<SLE const>>
|
||||
readLocal(Keylet const& k) const;
|
||||
|
||||
std::shared_ptr<SLE>
|
||||
peek(ReadView const& base, Keylet const& k);
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ public:
|
||||
bool sslVerify,
|
||||
beast::Journal j);
|
||||
|
||||
static void
|
||||
cleanupSSLContext();
|
||||
|
||||
static void
|
||||
get(bool bSSL,
|
||||
boost::asio::io_context& io_context,
|
||||
|
||||
@@ -234,7 +234,7 @@ missing_field_error(std::string const& name)
|
||||
}
|
||||
|
||||
inline Json::Value
|
||||
missing_field_error(Json::StaticString name)
|
||||
missing_field_error(Json::StaticString const& name)
|
||||
{
|
||||
return missing_field_error(std::string(name));
|
||||
}
|
||||
@@ -252,7 +252,7 @@ object_field_error(std::string const& name)
|
||||
}
|
||||
|
||||
inline Json::Value
|
||||
object_field_error(Json::StaticString name)
|
||||
object_field_error(Json::StaticString const& name)
|
||||
{
|
||||
return object_field_error(std::string(name));
|
||||
}
|
||||
@@ -264,7 +264,7 @@ invalid_field_message(std::string const& name)
|
||||
}
|
||||
|
||||
inline std::string
|
||||
invalid_field_message(Json::StaticString name)
|
||||
invalid_field_message(Json::StaticString const& name)
|
||||
{
|
||||
return invalid_field_message(std::string(name));
|
||||
}
|
||||
@@ -276,7 +276,7 @@ invalid_field_error(std::string const& name)
|
||||
}
|
||||
|
||||
inline Json::Value
|
||||
invalid_field_error(Json::StaticString name)
|
||||
invalid_field_error(Json::StaticString const& name)
|
||||
{
|
||||
return invalid_field_error(std::string(name));
|
||||
}
|
||||
@@ -288,7 +288,7 @@ expected_field_message(std::string const& name, std::string const& type)
|
||||
}
|
||||
|
||||
inline std::string
|
||||
expected_field_message(Json::StaticString name, std::string const& type)
|
||||
expected_field_message(Json::StaticString const& name, std::string const& type)
|
||||
{
|
||||
return expected_field_message(std::string(name), type);
|
||||
}
|
||||
@@ -300,7 +300,7 @@ expected_field_error(std::string const& name, std::string const& type)
|
||||
}
|
||||
|
||||
inline Json::Value
|
||||
expected_field_error(Json::StaticString name, std::string const& type)
|
||||
expected_field_error(Json::StaticString const& name, std::string const& type)
|
||||
{
|
||||
return expected_field_error(std::string(name), type);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,17 @@ public:
|
||||
return id_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SHAMapNodeID of a child node at the specified branch.
|
||||
*
|
||||
* @param m The branch number (0-15) indicating which child to descend to.
|
||||
* In the SHAMap's 16-way radix tree, each inner node has up to
|
||||
* 16 children, indexed by the corresponding nibble (4 bits) of
|
||||
* the key at the current depth.
|
||||
* @return SHAMapNodeID of the child node at branch m.
|
||||
* @throws std::logic_error if this node is at the maximum leaf depth (64)
|
||||
* or if the node's id doesn't match its depth mask.
|
||||
*/
|
||||
SHAMapNodeID
|
||||
getChildNodeID(unsigned int m) const;
|
||||
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
# The idea is to empty this file gradually by fixing the underlying issues and removing suppressions.
|
||||
# The idea is to empty this file gradually by fixing the underlying issues and removing suppresions.
|
||||
#
|
||||
# ASAN_OPTIONS="print_stacktrace=1:detect_container_overflow=0:suppressions=sanitizers/suppressions/asan.supp:halt_on_error=0"
|
||||
# ASAN_OPTIONS="suppressions=sanitizers/suppressions/asan.supp:include=sanitizers/suppressions/runtime-asan-options.txt"
|
||||
#
|
||||
# The detect_container_overflow=0 option disables false positives from:
|
||||
# - Boost intrusive containers (slist_iterator.hpp, hashtable.hpp, aged_unordered_container.h)
|
||||
# - Boost context/coroutine stack switching (Workers.cpp, thread.h)
|
||||
# Boost coroutines cause multiple ASAN false positives due to swapcontext/fiber stack switching.
|
||||
# ASAN cannot correctly track stack memory across coroutine context switches, leading to:
|
||||
# - stack-use-after-return errors
|
||||
# - stack-use-after-scope errors
|
||||
# - stack-buffer-overflow errors in seemingly unrelated code (e.g., std::chrono::steady_clock::now())
|
||||
# - stack-buffer-underflow errors in seemingly unrelated code (e.g., xxhasher::retrieveHash(), clock_gettime)
|
||||
# - bad-free errors in boost::context::basic_fixedsize_stack::deallocate (ASan loses track of
|
||||
# malloc allocations after fiber/context switches, reporting "free on address not malloc()-ed")
|
||||
#
|
||||
# See: https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow
|
||||
# These are now handled by:
|
||||
# 1. Using Boost.Coroutine2 with the ucontext backend (BOOST_USE_ASAN + BOOST_USE_UCONTEXT)
|
||||
# 2. Runtime options in runtime-asan-options.txt:
|
||||
# - alloc_dealloc_mismatch=0: Suppresses false "bad-free" errors from fiber stack deallocation, on GCC. For Clang we don't instrument boost
|
||||
# - detect_stack_use_after_return=0: Prevents false positives from fake stack tracking
|
||||
# - use_sigaltstack=0: Avoids conflicts with coroutine stack switching
|
||||
interceptor_via_fun:swapcontext
|
||||
interceptor_via_fun:makecontext
|
||||
interceptor_via_fun:boost::context::fiber::~fiber
|
||||
interceptor_name:boost/context/fiber_ucontext.hpp
|
||||
interceptor_name:boost/context/fixedsize_stack.hpp
|
||||
interceptor_name:Coro.ipp
|
||||
|
||||
# Boost
|
||||
interceptor_name:boost/asio
|
||||
|
||||
# Leaks in Doctest tests: xrpl.test.*
|
||||
interceptor_name:src/libxrpl/net/HTTPClient.cpp
|
||||
interceptor_name:src/libxrpl/net/RegisterSSLCerts.cpp
|
||||
interceptor_name:src/tests/libxrpl/net/HTTPClient.cpp
|
||||
interceptor_name:xrpl/net/AutoSocket.h
|
||||
interceptor_name:xrpl/net/HTTPClient.h
|
||||
interceptor_name:xrpl/net/HTTPClientSSLContext.h
|
||||
interceptor_name:xrpl/net/RegisterSSLCerts.h
|
||||
|
||||
# Suppress false positive stack-buffer errors in thread stack allocation
|
||||
# Related to ASan's __asan_handle_no_return warnings (github.com/google/sanitizers/issues/189)
|
||||
# These occur during multi-threaded test initialization on macOS
|
||||
interceptor_name:memcpy
|
||||
interceptor_name:clock_gettime
|
||||
interceptor_name:__bzero
|
||||
interceptor_name:__asan_memset
|
||||
interceptor_name:__asan_memcpy
|
||||
interceptor_name:nudb
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
# The idea is to empty this file gradually by fixing the underlying issues and removing suppresions.
|
||||
|
||||
# Suppress leaks detected by asan in rippled code.
|
||||
leak:src/libxrpl/net/HTTPClient.cpp
|
||||
leak:src/libxrpl/net/RegisterSSLCerts.cpp
|
||||
leak:src/tests/libxrpl/net/HTTPClient.cpp
|
||||
leak:xrpl/net/AutoSocket.h
|
||||
leak:xrpl/net/HTTPClient.h
|
||||
leak:xrpl/net/HTTPClientSSLContext.h
|
||||
leak:xrpl/net/RegisterSSLCerts.h
|
||||
leak:ripple::HTTPClient
|
||||
leak:ripple::HTTPClientImp
|
||||
|
||||
# Suppress leaks detected by asan in boost code.
|
||||
leak:boost::asio
|
||||
leak:boost/asio
|
||||
# These are false positives from Boost.Asio SSL internals that use OpenSSL BIO structures.
|
||||
# The BIO structures are managed by OpenSSL's internal reference counting and freed at process exit.
|
||||
|
||||
#leak:boost::asio
|
||||
#leak:boost/asio
|
||||
|
||||
# OpenSSL BIO memory is managed internally and freed at process exit
|
||||
leak:CRYPTO_malloc
|
||||
leak:bio_make_pair
|
||||
leak:BIO_new_bio_pair
|
||||
|
||||
5
sanitizers/suppressions/runtime-asan-options.txt
Normal file
5
sanitizers/suppressions/runtime-asan-options.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
detect_container_overflow=0
|
||||
detect_stack_use_after_return=0
|
||||
halt_on_error=0
|
||||
print_summary=true
|
||||
use_sigaltstack=0
|
||||
1
sanitizers/suppressions/runtime-lsan-options.txt
Normal file
1
sanitizers/suppressions/runtime-lsan-options.txt
Normal file
@@ -0,0 +1 @@
|
||||
halt_on_error=0
|
||||
2
sanitizers/suppressions/runtime-tsan-options.txt
Normal file
2
sanitizers/suppressions/runtime-tsan-options.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
halt_on_error=0
|
||||
second_deadlock_stack=1
|
||||
1
sanitizers/suppressions/runtime-ubsan-options.txt
Normal file
1
sanitizers/suppressions/runtime-ubsan-options.txt
Normal file
@@ -0,0 +1 @@
|
||||
halt_on_error=0
|
||||
@@ -20,10 +20,15 @@ signal:test/beast/beast_PropertyStream_test.cpp
|
||||
signal:xrpld/core/detail/Workers.cpp
|
||||
signal:xrpld/core/JobQueue.cpp
|
||||
|
||||
src:beast/utility/beast_Journal.cpp
|
||||
src:beast/utility/beast_PropertyStream.cpp
|
||||
src:core/detail/Workers.cpp
|
||||
src:core/JobQueue.cpp
|
||||
src:libxrpl/beast/utility/beast_Journal.cpp
|
||||
src:test/beast/beast_PropertyStream_test.cpp
|
||||
src:src/test/app/Invariants_test.cpp
|
||||
# src:beast/utility/beast_Journal.cpp
|
||||
# src:beast/utility/beast_PropertyStream.cpp
|
||||
# src:core/detail/Workers.cpp
|
||||
# src:core/JobQueue.cpp
|
||||
# src:libxrpl/beast/utility/beast_Journal.cpp
|
||||
# src:test/beast/beast_PropertyStream_test.cpp
|
||||
# src:src/test/app/Invariants_test.cpp
|
||||
|
||||
# Boost coroutines cause false positive stack-buffer-underflow in xxhasher
|
||||
# This is a known ASAN limitation with stackful coroutines
|
||||
# See: https://github.com/google/sanitizers/issues/189
|
||||
src:beast/hash/xxhasher.h
|
||||
|
||||
@@ -140,6 +140,7 @@ 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/Loan_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
|
||||
|
||||
7
src/libxrpl/basics/LocalValue.cpp
Normal file
7
src/libxrpl/basics/LocalValue.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include <xrpl/basics/LocalValue.h>
|
||||
|
||||
namespace xrpl {
|
||||
namespace detail {
|
||||
|
||||
} // namespace detail
|
||||
} // namespace xrpl
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@@ -107,7 +108,7 @@ public:
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa,
|
||||
std::string location);
|
||||
std::string_view location);
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
template <UnsignedMantissa T>
|
||||
@@ -116,7 +117,7 @@ public:
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
void
|
||||
doRound(rep& drops, std::string location);
|
||||
doRound(rep& drops, std::string_view location);
|
||||
|
||||
private:
|
||||
void
|
||||
@@ -242,7 +243,7 @@ Number::Guard::doRoundUp(
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa,
|
||||
std::string location)
|
||||
std::string_view location)
|
||||
{
|
||||
auto r = round();
|
||||
if (r == 1 || (r == 0 && (mantissa & 1) == 1))
|
||||
@@ -258,7 +259,7 @@ Number::Guard::doRoundUp(
|
||||
}
|
||||
bringIntoRange(negative, mantissa, exponent, minMantissa);
|
||||
if (exponent > maxExponent)
|
||||
throw std::overflow_error(location);
|
||||
Throw<std::overflow_error>(std::string(location));
|
||||
}
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
@@ -284,7 +285,7 @@ Number::Guard::doRoundDown(
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
void
|
||||
Number::Guard::doRound(rep& drops, std::string location)
|
||||
Number::Guard::doRound(rep& drops, std::string_view location)
|
||||
{
|
||||
auto r = round();
|
||||
if (r == 1 || (r == 0 && (drops & 1) == 1))
|
||||
@@ -298,7 +299,7 @@ Number::Guard::doRound(rep& drops, std::string location)
|
||||
// or "(maxRep + 1) / 10", neither of which will round up when
|
||||
// converting to rep, though the latter might overflow _before_
|
||||
// rounding.
|
||||
throw std::overflow_error(location); // LCOV_EXCL_LINE
|
||||
throw std::overflow_error(std::string(location)); // LCOV_EXCL_LINE
|
||||
}
|
||||
++drops;
|
||||
}
|
||||
|
||||
@@ -193,17 +193,17 @@ Value::Value(ValueType type) : type_(type), allocated_(0)
|
||||
}
|
||||
}
|
||||
|
||||
Value::Value(Int value) : type_(intValue)
|
||||
Value::Value(Int value) : type_(intValue), allocated_(0)
|
||||
{
|
||||
value_.int_ = value;
|
||||
}
|
||||
|
||||
Value::Value(UInt value) : type_(uintValue)
|
||||
Value::Value(UInt value) : type_(uintValue), allocated_(0)
|
||||
{
|
||||
value_.uint_ = value;
|
||||
}
|
||||
|
||||
Value::Value(double value) : type_(realValue)
|
||||
Value::Value(double value) : type_(realValue), allocated_(0)
|
||||
{
|
||||
value_.real_ = value;
|
||||
}
|
||||
@@ -230,7 +230,7 @@ Value::Value(StaticString const& value) : type_(stringValue), allocated_(false)
|
||||
value_.string_ = const_cast<char*>(value.c_str());
|
||||
}
|
||||
|
||||
Value::Value(bool value) : type_(booleanValue)
|
||||
Value::Value(bool value) : type_(booleanValue), allocated_(0)
|
||||
{
|
||||
value_.bool_ = value;
|
||||
}
|
||||
|
||||
@@ -335,6 +335,28 @@ ApplyStateTable::read(ReadView const& base, Keylet const& k) const
|
||||
return sle;
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<SLE const>>
|
||||
ApplyStateTable::readLocal(Keylet const& k) const
|
||||
{
|
||||
auto const iter = items_.find(k.key);
|
||||
if (iter == items_.end())
|
||||
return std::nullopt;
|
||||
auto const& item = iter->second;
|
||||
auto const& sle = item.second;
|
||||
switch (item.first)
|
||||
{
|
||||
case Action::erase:
|
||||
return nullptr;
|
||||
case Action::cache:
|
||||
case Action::insert:
|
||||
case Action::modify:
|
||||
break;
|
||||
};
|
||||
if (!k.check(*sle))
|
||||
return nullptr;
|
||||
return sle;
|
||||
}
|
||||
|
||||
std::shared_ptr<SLE>
|
||||
ApplyStateTable::peek(ReadView const& base, Keylet const& k)
|
||||
{
|
||||
|
||||
@@ -49,7 +49,24 @@ ApplyViewBase::succ(key_type const& key, std::optional<key_type> const& last) co
|
||||
std::shared_ptr<SLE const>
|
||||
ApplyViewBase::read(Keylet const& k) const
|
||||
{
|
||||
return items_.read(*base_, k);
|
||||
// Iteratively walk up the chain of ApplyViewBase layers
|
||||
// instead of recursing through items_.read(*base_, k).
|
||||
auto const* current = this;
|
||||
while (current)
|
||||
{
|
||||
if (auto result = current->items_.readLocal(k))
|
||||
return *result;
|
||||
|
||||
// Check if the base is another ApplyViewBase layer
|
||||
auto const* next = dynamic_cast<ApplyViewBase const*>(current->base_);
|
||||
if (!next)
|
||||
return current->base_->read(k);
|
||||
current = next;
|
||||
}
|
||||
// Unreachable: current starts as `this` (non-null) and the loop
|
||||
// always returns before current could become null.
|
||||
UNREACHABLE("xrpl::ApplyViewBase::read : unreachable");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto
|
||||
|
||||
@@ -26,6 +26,12 @@ HTTPClient::initializeSSLContext(
|
||||
httpClientSSLContext.emplace(sslVerifyDir, sslVerifyFile, sslVerify, j);
|
||||
}
|
||||
|
||||
void
|
||||
HTTPClient::cleanupSSLContext()
|
||||
{
|
||||
httpClientSSLContext.reset();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Fetch a web page via http or https.
|
||||
|
||||
@@ -1003,7 +1003,12 @@ amountFromJson(SField const& name, Json::Value const& v)
|
||||
else if (v.isString())
|
||||
{
|
||||
std::string val = v.asString();
|
||||
// Pre-allocate to avoid reallocation during split. This function is often
|
||||
// called deep in the RPC stack (via JSON parsing) where stack space is
|
||||
// limited. ASAN detected stack-buffer-overflow here at ~95% coroutine
|
||||
// stack usage (1001376/1048576 bytes). Coroutine stack increased to 2MB.
|
||||
std::vector<std::string> elements;
|
||||
elements.reserve(3);
|
||||
boost::split(elements, val, boost::is_any_of("\t\n\r ,/"));
|
||||
|
||||
if (elements.size() > 3)
|
||||
|
||||
@@ -69,7 +69,7 @@ make_name(std::string const& object, std::string const& field)
|
||||
if (field.empty())
|
||||
return object;
|
||||
|
||||
return object + "." + field;
|
||||
return {object + "." + field};
|
||||
}
|
||||
|
||||
static inline Json::Value
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
static uint256 const&
|
||||
// Returns a depth mask by value to avoid potential lifetime issues in
|
||||
// multi-threaded contexts. Returning by const reference to a static member
|
||||
// could trigger stack-use-after-scope errors when the reference is used in
|
||||
// temporary expressions with operator& in concurrent coroutine scenarios.
|
||||
static uint256 const
|
||||
depthMask(unsigned int depth)
|
||||
{
|
||||
enum { mask_size = 65 };
|
||||
@@ -72,7 +76,8 @@ SHAMapNodeID::getChildNodeID(unsigned int m) const
|
||||
if (depth_ >= SHAMap::leafDepth)
|
||||
Throw<std::logic_error>("Request for child node ID of " + to_string(*this));
|
||||
|
||||
if (id_ != (id_ & depthMask(depth_)))
|
||||
auto const idAtDepth = id_ & depthMask(depth_);
|
||||
if (id_ != idAtDepth)
|
||||
Throw<std::logic_error>("Incorrect mask for " + to_string(*this));
|
||||
|
||||
SHAMapNodeID node{depth_ + 1, id_};
|
||||
|
||||
@@ -148,7 +148,7 @@ private:
|
||||
std::vector<std::string> emptyCfgKeys;
|
||||
struct publisher
|
||||
{
|
||||
publisher(FetchListConfig const& c) : cfg{c}
|
||||
publisher(FetchListConfig const& c) : cfg{c}, isRetry{false}
|
||||
{
|
||||
}
|
||||
std::shared_ptr<TrustedPublisherServer> server;
|
||||
|
||||
@@ -184,6 +184,9 @@ private:
|
||||
};
|
||||
|
||||
// Helper function to run HTTP client test
|
||||
// Note: Caller must ensure HTTPClient::initializeSSLContext has been called
|
||||
// before this function, and HTTPClient::cleanupSSLContext is called after
|
||||
// all tests are completed.
|
||||
bool
|
||||
runHTTPTest(
|
||||
TestHTTPServer& server,
|
||||
@@ -191,14 +194,9 @@ runHTTPTest(
|
||||
bool& completed,
|
||||
int& resultStatus,
|
||||
std::string& resultData,
|
||||
boost::system::error_code& resultError)
|
||||
boost::system::error_code& resultError,
|
||||
beast::Journal& j)
|
||||
{
|
||||
// Create a null journal for testing
|
||||
beast::Journal j{TestSink::instance()};
|
||||
|
||||
// Initialize HTTPClient SSL context
|
||||
HTTPClient::initializeSSLContext("", "", false, j);
|
||||
|
||||
HTTPClient::get(
|
||||
false, // no SSL
|
||||
server.ioc(),
|
||||
@@ -232,6 +230,9 @@ runHTTPTest(
|
||||
}
|
||||
}
|
||||
|
||||
// Drain any remaining handlers to ensure proper cleanup of HTTPClientImp
|
||||
server.ioc().poll();
|
||||
|
||||
return completed;
|
||||
}
|
||||
|
||||
@@ -260,19 +261,28 @@ TEST(HTTPClient, case_insensitive_content_length)
|
||||
std::string resultData;
|
||||
boost::system::error_code resultError;
|
||||
|
||||
bool testCompleted =
|
||||
runHTTPTest(server, "/test", completed, resultStatus, resultData, resultError);
|
||||
beast::Journal j{TestSink::instance()};
|
||||
HTTPClient::initializeSSLContext("", "", false, j);
|
||||
|
||||
bool testCompleted =
|
||||
runHTTPTest(server, "/test", completed, resultStatus, resultData, resultError, j);
|
||||
// Verify results
|
||||
EXPECT_TRUE(testCompleted);
|
||||
EXPECT_FALSE(resultError);
|
||||
EXPECT_EQ(resultStatus, 200);
|
||||
EXPECT_EQ(resultData, testBody);
|
||||
}
|
||||
|
||||
// Clean up SSL context to prevent memory leaks
|
||||
HTTPClient::cleanupSSLContext();
|
||||
}
|
||||
|
||||
TEST(HTTPClient, basic_http_request)
|
||||
{
|
||||
// Initialize SSL context once for the entire test
|
||||
beast::Journal j{TestSink::instance()};
|
||||
HTTPClient::initializeSSLContext("", "", false, j);
|
||||
|
||||
TestHTTPServer server;
|
||||
std::string testBody = "Test response body";
|
||||
server.setResponseBody(testBody);
|
||||
@@ -284,16 +294,23 @@ TEST(HTTPClient, basic_http_request)
|
||||
boost::system::error_code resultError;
|
||||
|
||||
bool testCompleted =
|
||||
runHTTPTest(server, "/basic", completed, resultStatus, resultData, resultError);
|
||||
runHTTPTest(server, "/basic", completed, resultStatus, resultData, resultError, j);
|
||||
|
||||
EXPECT_TRUE(testCompleted);
|
||||
EXPECT_FALSE(resultError);
|
||||
EXPECT_EQ(resultStatus, 200);
|
||||
EXPECT_EQ(resultData, testBody);
|
||||
|
||||
// Clean up SSL context to prevent memory leaks
|
||||
HTTPClient::cleanupSSLContext();
|
||||
}
|
||||
|
||||
TEST(HTTPClient, empty_response)
|
||||
{
|
||||
// Initialize SSL context once for the entire test
|
||||
beast::Journal j{TestSink::instance()};
|
||||
HTTPClient::initializeSSLContext("", "", false, j);
|
||||
|
||||
TestHTTPServer server;
|
||||
server.setResponseBody(""); // Empty body
|
||||
server.setHeader("Content-Length", "0");
|
||||
@@ -304,16 +321,23 @@ TEST(HTTPClient, empty_response)
|
||||
boost::system::error_code resultError;
|
||||
|
||||
bool testCompleted =
|
||||
runHTTPTest(server, "/empty", completed, resultStatus, resultData, resultError);
|
||||
runHTTPTest(server, "/empty", completed, resultStatus, resultData, resultError, j);
|
||||
|
||||
EXPECT_TRUE(testCompleted);
|
||||
EXPECT_FALSE(resultError);
|
||||
EXPECT_EQ(resultStatus, 200);
|
||||
EXPECT_TRUE(resultData.empty());
|
||||
|
||||
// Clean up SSL context to prevent memory leaks
|
||||
HTTPClient::cleanupSSLContext();
|
||||
}
|
||||
|
||||
TEST(HTTPClient, different_status_codes)
|
||||
{
|
||||
// Initialize SSL context once for the entire test
|
||||
beast::Journal j{TestSink::instance()};
|
||||
HTTPClient::initializeSSLContext("", "", false, j);
|
||||
|
||||
std::vector<unsigned int> statusCodes = {200, 404, 500};
|
||||
|
||||
for (auto status : statusCodes)
|
||||
@@ -328,10 +352,13 @@ TEST(HTTPClient, different_status_codes)
|
||||
boost::system::error_code resultError;
|
||||
|
||||
bool testCompleted =
|
||||
runHTTPTest(server, "/status", completed, resultStatus, resultData, resultError);
|
||||
runHTTPTest(server, "/status", completed, resultStatus, resultData, resultError, j);
|
||||
|
||||
EXPECT_TRUE(testCompleted);
|
||||
EXPECT_FALSE(resultError);
|
||||
EXPECT_EQ(resultStatus, static_cast<int>(status));
|
||||
}
|
||||
|
||||
// Clean up SSL context to prevent memory leaks
|
||||
HTTPClient::cleanupSSLContext();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user