tests: harden RPC fuzz harness setup

Initialize RPC server state used by direct handler calls,
let fuzz input choose restricted or unrestricted mode,
and skip iterations when the dummy core fails to initialize.

Also update the RPC fuzz harness documentation after removal
of the old RPC payment setup.
This commit is contained in:
selsta
2026-08-26 20:23:47 +02:00
parent bc933f920f
commit 0a47ee2c94
6 changed files with 29 additions and 15 deletions
+2
View File
@@ -120,9 +120,11 @@ namespace cryptonote
core_rpc_server::core_rpc_server(
core& cr
, nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& p2p
, bool restricted
)
: m_core(cr)
, m_p2p(p2p)
, m_restricted(restricted)
, disable_rpc_ban(false)
{}
//------------------------------------------------------------------------------------------------------------------------------
+1
View File
@@ -73,6 +73,7 @@ namespace cryptonote
core_rpc_server(
core& cr
, nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& p2p
, bool restricted = false
);
~core_rpc_server();
+4 -5
View File
@@ -9,14 +9,14 @@ These are the main fuzzing entry points, containing `LLVMFuzzerTestOneInput`, wh
## `initialisation.cpp` / `initialisation.h`
This set of source files provides initialisation helper functions to configure the `core_rpc_server` class with dummy protocols, P2P, payment modules, and other components. It also includes functions to generate fake random blocks, miners, and transactions for use in fuzzing.
This set of source files provides initialisation helper functions to configure the `core_rpc_server` class with dummy protocols, P2P, and other components. It also includes functions to generate fake random blocks, miners, and transactions for use in fuzzing.
## `rpc_endpoints.cpp` / `rpc_endpoints.h` / `zmq_endpoints.cpp` / `zmq_endpoints.h`
These source files handle the creation of the necessary request and response objects and the invocation of specific endpoint functions. These simulate real RPC requests / ZMQ requests for the purpose of fuzzing. There are three categories of RPC endpoint functions and only one for ZMQ endpoint functions:
* **Safe**: Considered stable and unlikely to fail.
* **Risky**: More prone to failure and early exits, especially if no valid blockchain is generated or no payment modules are configured.
* **Risky**: More prone to failure and early exits, especially if no valid blockchain is generated.
* **Priority**: Most critical RPC endpoint functions that will at least run once per iteration.
# Building the Fuzzing Harnesses
@@ -24,7 +24,7 @@ These source files handle the creation of the necessary request and response obj
The same `fuzz_rpc.cpp` is compiled into two versions of the fuzzer, one with the `SAFE` macro defined and one without. Meanwhile, `fuzz_zmq` is compiled into a single version.
* With `SAFE` defined: Only safe endpoint functions are fuzzed.
* Without `SAFE`: Both safe and risky functions are included in the fuzzing process, and payment modules are configured.
* Without `SAFE`: Both safe and risky functions are included in the fuzzing process.
# Fuzzing Harness Flow
@@ -35,8 +35,7 @@ Random data is used to generate selectors that choose an RPC endpoint function /
## `initialise_rpc_core` / `initialise_rpc_server`
At the start of each fuzzing iteration, a `core_rpc_server` object is created and initialised with dummy protocols, P2P, payment modules, and more.
If the `SAFE` macro is not defined, the payment module will also be initialised.
At the start of each fuzzing iteration, a `core_rpc_server` object is created and initialised with dummy protocols and P2P. The fuzz input selects whether the server runs in restricted or unrestricted RPC mode.
This step is skipped for `fuzz_zmq`.
## `generate_random_blocks`
+4 -1
View File
@@ -57,8 +57,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Initialise core and core_rpc_server
auto core_env = initialise_rpc_core();
if (!core_env) {
return 0;
}
auto& dummy_core = core_env->core;
auto rpc_handler = initialise_rpc_server(*dummy_core, provider, !is_safe_mode);
auto rpc_handler = initialise_rpc_server(*dummy_core, provider.ConsumeBool());
// Generate random blocks/miners/transactions and push to the core blockchains
if (!generate_random_blocks(*dummy_core, provider)) {
+16 -7
View File
@@ -16,6 +16,7 @@
#include "serialization/binary_utils.h"
#include "serialization/variant.h"
#include <boost/filesystem.hpp>
#include <boost/program_options/variables_map.hpp>
#include <vector>
#include <string>
@@ -51,21 +52,29 @@ std::unique_ptr<CoreEnv> initialise_rpc_core() {
cryptonote::core::init_options(desc);
boost::program_options::variables_map vm;
// Add command line argument to configure the rpc core object to use regression testing mode (FAKECHAIN)
// Enabling FAKECHAIN mode allows skipping validation logic of the authors signature and transactions ID
// on valid blocks and transactions while keeping other logic
std::vector<const char*> argv = {"fuzz", "--regtest"};
// Add command line arguments to configure the rpc core object to use regression testing mode (FAKECHAIN)
// and a throwaway data directory. Enabling FAKECHAIN mode allows skipping validation logic of the authors
// signature and transactions ID on valid blocks and transactions while keeping other logic.
const boost::filesystem::path data_dir = boost::filesystem::temp_directory_path() / "monero-fuzz-rpc";
std::vector<std::string> args = {"fuzz", "--regtest", "--offline", "--data-dir", data_dir.string()};
std::vector<const char*> argv;
argv.reserve(args.size());
for (const auto& arg : args) {
argv.push_back(arg.c_str());
}
boost::program_options::store(boost::program_options::parse_command_line(argv.size(), argv.data(), desc), vm);
boost::program_options::notify(vm);
// Initialise the dummy core with all the above settings
env->core->init(vm);
if (!env->core->init(vm)) {
return nullptr;
}
return env;
}
// Build the core_rpc_server handler object with the configured dummy core object
std::unique_ptr<RpcServerBundle> initialise_rpc_server(cryptonote::core& dummy_core, FuzzedDataProvider& provider, bool need_payment) {
std::unique_ptr<RpcServerBundle> initialise_rpc_server(cryptonote::core& dummy_core, bool restricted) {
// Create rpc server bundle object
auto bundle = std::make_unique<RpcServerBundle>();
@@ -73,7 +82,7 @@ std::unique_ptr<RpcServerBundle> initialise_rpc_server(cryptonote::core& dummy_c
bundle->proto_handler = std::make_unique<cryptonote::t_cryptonote_protocol_handler<cryptonote::core>>(dummy_core, nullptr, true);
bundle->proto_handler->set_no_sync(false);
bundle->dummy_p2p = std::make_unique<nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core>>>(*bundle->proto_handler);
bundle->rpc = std::make_unique<cryptonote::core_rpc_server>(dummy_core, *bundle->dummy_p2p);
bundle->rpc = std::make_unique<cryptonote::core_rpc_server>(dummy_core, *bundle->dummy_p2p, restricted);
return bundle;
}
+2 -2
View File
@@ -6,7 +6,7 @@
// Define templates for dummy protocol object
template class nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core>>;
struct DummyProtocol : public cryptonote::i_cryptonote_protocol {
struct DummyProtocol final : public cryptonote::i_cryptonote_protocol {
public:
bool is_synchronized() const;
bool relay_transactions(cryptonote::NOTIFY_NEW_TRANSACTIONS::request&, const boost::uuids::uuid&, epee::net_utils::zone, cryptonote::relay_method);
@@ -25,7 +25,7 @@ struct CoreEnv {
};
std::unique_ptr<CoreEnv> initialise_rpc_core();
std::unique_ptr<RpcServerBundle> initialise_rpc_server(cryptonote::core&, FuzzedDataProvider&, bool);
std::unique_ptr<RpcServerBundle> initialise_rpc_server(cryptonote::core&, bool);
bool generate_random_blocks(cryptonote::core&, FuzzedDataProvider&);
extern std::vector<crypto::hash> cached_tx_hashes;