mirror of
https://github.com/monero-project/monero
synced 2025-10-06 00:32:44 +02:00
Merge pull request #10004
36bdfad
rpc-fuzz: Add new fuzzers for RPC endpoints (Arthur Chan)
This commit is contained in:
@@ -484,8 +484,8 @@ endif()
|
||||
option(SANITIZE "Use ASAN memory sanitizer" OFF)
|
||||
if(SANITIZE)
|
||||
message(STATUS "Using ASAN")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address,undefined")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,undefined")
|
||||
endif()
|
||||
|
||||
# Set default blockchain storage location:
|
||||
|
@@ -90,7 +90,12 @@ namespace tools
|
||||
|
||||
el::Level performance_timer_log_level = el::Level::Info;
|
||||
|
||||
static __thread std::vector<LoggingPerformanceTimer*> *performance_timers = NULL;
|
||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
#warning "Building with fuzzing mode UNSAFE FOR PRODUCTION!"
|
||||
__thread std::vector<LoggingPerformanceTimer*> *performance_timers = NULL;
|
||||
#else
|
||||
static __thread std::vector<LoggingPerformanceTimer*> *performance_timers = NULL;
|
||||
#endif
|
||||
|
||||
void set_performance_timer_log_level(el::Level level)
|
||||
{
|
||||
|
@@ -26,6 +26,75 @@
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Add the include path for <fuzzer/FuzzedDataProvider.h>
|
||||
include_directories(${CMAKE_SOURCE_DIR}/tests/fuzz/include)
|
||||
|
||||
# Recompile perf_timer for fuzzing
|
||||
add_library(fuzz_unsafe_macro OBJECT
|
||||
${CMAKE_SOURCE_DIR}/src/common/perf_timer.cpp)
|
||||
target_compile_definitions(fuzz_unsafe_macro
|
||||
PRIVATE FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
|
||||
|
||||
monero_add_minimal_executable(fuzz_rpc
|
||||
fuzz_rpc/initialisation.cpp
|
||||
fuzz_rpc/rpc_endpoints.cpp
|
||||
fuzz_rpc/fuzz_rpc.cpp
|
||||
$<TARGET_OBJECTS:fuzz_unsafe_macro>)
|
||||
target_compile_definitions(fuzz_rpc PRIVATE SAFE)
|
||||
target_link_libraries(fuzz_rpc
|
||||
PRIVATE
|
||||
rpc
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${EXTRA_LIBRARIES}
|
||||
$ENV{LIB_FUZZING_ENGINE})
|
||||
set_property(TARGET fuzz_rpc
|
||||
PROPERTY
|
||||
FOLDER "tests")
|
||||
|
||||
monero_add_minimal_executable(fuzz_rpc_full
|
||||
fuzz_rpc/initialisation.cpp
|
||||
fuzz_rpc/rpc_endpoints.cpp
|
||||
fuzz_rpc/fuzz_rpc.cpp
|
||||
$<TARGET_OBJECTS:fuzz_unsafe_macro>)
|
||||
target_link_libraries(fuzz_rpc_full
|
||||
PRIVATE
|
||||
rpc
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${EXTRA_LIBRARIES}
|
||||
$ENV{LIB_FUZZING_ENGINE})
|
||||
set_property(TARGET fuzz_rpc_full
|
||||
PROPERTY
|
||||
FOLDER "tests")
|
||||
|
||||
monero_add_minimal_executable(fuzz_rpc_full_no_exceptions
|
||||
fuzz_rpc/initialisation.cpp
|
||||
fuzz_rpc/rpc_endpoints.cpp
|
||||
fuzz_rpc/fuzz_rpc.cpp
|
||||
$<TARGET_OBJECTS:fuzz_unsafe_macro>)
|
||||
target_compile_definitions(fuzz_rpc_full_no_exceptions PRIVATE CATCH_ALL_EXCEPTIONS)
|
||||
target_link_libraries(fuzz_rpc_full_no_exceptions
|
||||
PRIVATE
|
||||
rpc
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${EXTRA_LIBRARIES}
|
||||
$ENV{LIB_FUZZING_ENGINE})
|
||||
set_property(TARGET fuzz_rpc_full_no_exceptions
|
||||
PROPERTY
|
||||
FOLDER "tests")
|
||||
|
||||
monero_add_minimal_executable(fuzz_zmq
|
||||
fuzz_rpc/zmq_endpoints.cpp
|
||||
fuzz_rpc/fuzz_zmq.cpp)
|
||||
target_link_libraries(fuzz_zmq
|
||||
PRIVATE
|
||||
rpc_pub
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${EXTRA_LIBRARIES}
|
||||
$ENV{LIB_FUZZING_ENGINE})
|
||||
set_property(TARGET fuzz_zmq
|
||||
PROPERTY
|
||||
FOLDER "tests")
|
||||
|
||||
monero_add_minimal_executable(block_fuzz_tests block.cpp fuzzer.cpp)
|
||||
target_link_libraries(block_fuzz_tests
|
||||
PRIVATE
|
||||
|
50
tests/fuzz/fuzz_rpc/README.md
Normal file
50
tests/fuzz/fuzz_rpc/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Fuzzing Harness Explanation
|
||||
|
||||
The fuzzing harness skips the transport layer and directly initialises a `core_rpc_server` object. It then calls and fuzzes the RPC endpoint function handlers directly, removing the need to start a fake server while still allowing the handler logic for each RPC API to be fuzzed.
|
||||
|
||||
|
||||
## `fuzz_rpc.cpp` / `fuzz_zmq.cpp`
|
||||
|
||||
These are the main fuzzing entry points, containing `LLVMFuzzerTestOneInput`, which manages each iteration of the fuzzing process.
|
||||
|
||||
## `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.
|
||||
|
||||
## `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.
|
||||
* **Priority**: Most critical RPC endpoint functions that will at least run once per iteration.
|
||||
|
||||
# Building the Fuzzing Harnesses
|
||||
|
||||
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.
|
||||
|
||||
# Fuzzing Harness Flow
|
||||
|
||||
## Select a Random RPC Endpoint Function / ZMQ Endpoint Function
|
||||
|
||||
Random data is used to generate selectors that choose an RPC endpoint function / ZMQ endpoint function to fuzz, either from the safe or risky function maps (depending on whether the `SAFE` macro is defined). A function from the priority category is always included at least once.
|
||||
|
||||
|
||||
## `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.
|
||||
This step is skipped for `fuzz_zmq`.
|
||||
|
||||
## `generate_random_blocks`
|
||||
|
||||
The provided random data is used to initialise a set of random blocks, which are added to a dummy blockchain (initialised using `FAKECHAIN`).
|
||||
If blocks are successfully added, a list of random transactions is then generated and also pushed to the blockchain.
|
||||
This step is skipped for `fuzz_zmq`.
|
||||
|
||||
## Real Fuzzing
|
||||
|
||||
The selected RPC endpoint helper function in `rpc_endpoints` or `zmq_endpoints` generates a random request and response object for the specific call, and then invokes the corresponding handler in the `core_rpc_server` object.
|
91
tests/fuzz/fuzz_rpc/fuzz_rpc.cpp
Normal file
91
tests/fuzz/fuzz_rpc/fuzz_rpc.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "initialisation.h"
|
||||
#include "rpc_endpoints.h"
|
||||
#include <fuzzer/FuzzedDataProvider.h>
|
||||
|
||||
#include <csignal>
|
||||
#include <csetjmp>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
// In general an iteration needs a fair amount of data so ensure we have enough
|
||||
// to work with, otherwise return 0 to skip this iteration.
|
||||
if (size < 512) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Determine if this iteration should run in safe mode
|
||||
#ifdef SAFE
|
||||
constexpr bool is_safe_mode = true;
|
||||
#else
|
||||
constexpr bool is_safe_mode = false;
|
||||
#endif
|
||||
|
||||
// Retrieve a list of all fuzz targets
|
||||
auto fuzz_targets = get_fuzz_targets(is_safe_mode);
|
||||
|
||||
// Disable fatal exits for logging.
|
||||
el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog);
|
||||
|
||||
// Prepare base FuzzedDataProvider
|
||||
FuzzedDataProvider provider(data, size);
|
||||
|
||||
// Randomly choose multiple fuzz_targets to fuzz
|
||||
int rpc_messages_to_send = provider.ConsumeIntegralInRange<int>(1, 16);
|
||||
std::vector<int> selectors;
|
||||
if (is_safe_mode) {
|
||||
selectors.reserve(rpc_messages_to_send);
|
||||
} else {
|
||||
selectors.reserve(rpc_messages_to_send + priority_fuzz_targets.size());
|
||||
for (int i = 0; i < priority_fuzz_targets.size(); ++i) {
|
||||
selectors.push_back(i);
|
||||
}
|
||||
|
||||
// Randomly shuffle the selectors for priority fuzz targets
|
||||
for (int i = 0; i < priority_fuzz_targets.size(); i++) {
|
||||
int target = provider.ConsumeIntegralInRange<int>(0, priority_fuzz_targets.size() - 1);
|
||||
std::swap(selectors[i], selectors[target]);
|
||||
}
|
||||
}
|
||||
|
||||
// Randomly select rpc functions to call
|
||||
for (int i = 0; i < rpc_messages_to_send && provider.remaining_bytes() >= 2; ++i) {
|
||||
int selector = provider.ConsumeIntegralInRange<int>(0, fuzz_targets.size() - 1);
|
||||
selectors.push_back(selector);
|
||||
}
|
||||
|
||||
// Initialise core and core_rpc_server
|
||||
auto core_env = initialise_rpc_core();
|
||||
auto& dummy_core = core_env->core;
|
||||
auto rpc_handler = initialise_rpc_server(*dummy_core, provider, !is_safe_mode);
|
||||
|
||||
// Generate random blocks/miners/transactions and push to the core blockchains
|
||||
if (!generate_random_blocks(*dummy_core, provider)) {
|
||||
// No randomised blocks have been successfully added, skipping this iteration
|
||||
dummy_core->get_blockchain_storage().get_db().batch_stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Disable bootstrap daemon
|
||||
disable_bootstrap_daemon(*rpc_handler->rpc);
|
||||
|
||||
for (int selector : selectors) {
|
||||
try {
|
||||
// Fuzz the target function
|
||||
fuzz_targets[selector](*rpc_handler->rpc, provider);
|
||||
} catch (const std::runtime_error&) {
|
||||
// Known runtime_error thrown from monero
|
||||
} catch (const cryptonote::DB_ERROR& e) {
|
||||
// Known error thrown from monero on internal blockchain DB check
|
||||
// when fuzzing with random values
|
||||
#ifdef CATCH_ALL_EXCEPTIONS
|
||||
} catch (...) {
|
||||
// Silent all exceptions
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
dummy_core->get_blockchain_storage().get_db().batch_stop();
|
||||
return 0;
|
||||
}
|
43
tests/fuzz/fuzz_rpc/fuzz_zmq.cpp
Normal file
43
tests/fuzz/fuzz_rpc/fuzz_zmq.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include <fuzzer/FuzzedDataProvider.h>
|
||||
#include "zmq_endpoints.h"
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
using namespace cryptonote;
|
||||
using namespace cryptonote::listener;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
if (size < 64) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FuzzedDataProvider provider(data, size);
|
||||
|
||||
void* ctx = zmq_ctx_new();
|
||||
if (!ctx) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Randomly choose multiple zmq_targets to fuzz
|
||||
int to_sent = provider.ConsumeIntegralInRange<int>(1, 8);
|
||||
std::vector<int> selectors;
|
||||
selectors.reserve(to_sent);
|
||||
for (int i = 0; i < to_sent && provider.remaining_bytes() >= 2; ++i) {
|
||||
uint16_t raw = provider.ConsumeIntegral<uint16_t>();
|
||||
selectors.push_back(raw % zmq_targets.size());
|
||||
}
|
||||
|
||||
try {
|
||||
zmq_pub pub(ctx);
|
||||
for (int selector : selectors) {
|
||||
zmq_targets[selector](pub, provider);
|
||||
}
|
||||
} catch (const std::runtime_error& e) {
|
||||
// Ignore known runtime_error from checking
|
||||
}
|
||||
|
||||
zmq_ctx_shutdown(ctx);
|
||||
zmq_ctx_term(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
357
tests/fuzz/fuzz_rpc/initialisation.cpp
Normal file
357
tests/fuzz/fuzz_rpc/initialisation.cpp
Normal file
@@ -0,0 +1,357 @@
|
||||
#include "initialisation.h"
|
||||
|
||||
#include "crypto/crypto.h"
|
||||
#include "cryptonote_config.h"
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "cryptonote_basic/tx_extra.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "cryptonote_core/cryptonote_tx_utils.h"
|
||||
#include "cryptonote_core/tx_pool.h"
|
||||
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
|
||||
#include "p2p/net_node.h"
|
||||
#include "p2p/net_node.inl"
|
||||
#include "ringct/rctTypes.h"
|
||||
#include "serialization/binary_utils.h"
|
||||
#include "serialization/variant.h"
|
||||
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
// For storing valid transaction hashes
|
||||
std::vector<crypto::hash> cached_tx_hashes;
|
||||
|
||||
// Configure a dummy protocol to avoid external requests
|
||||
bool DummyProtocol::is_synchronized() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DummyProtocol::relay_transactions(cryptonote::NOTIFY_NEW_TRANSACTIONS::request&, const boost::uuids::uuid&, epee::net_utils::zone, cryptonote::relay_method) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DummyProtocol::relay_block(cryptonote::NOTIFY_NEW_FLUFFY_BLOCK::request&, cryptonote::cryptonote_connection_context&) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function to create and initialise a dummy rpc core object
|
||||
std::unique_ptr<CoreEnv> initialise_rpc_core() {
|
||||
auto env = std::make_unique<CoreEnv>();
|
||||
|
||||
// Create fresh pointer for dummy protocol and core
|
||||
env->protocol = std::make_unique<DummyProtocol>();
|
||||
env->core = std::make_unique<cryptonote::core>(env->protocol.get());
|
||||
|
||||
// Configure the rpc core object with default settings
|
||||
boost::program_options::options_description desc;
|
||||
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"};
|
||||
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);
|
||||
|
||||
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) {
|
||||
// Create rpc server bundle object
|
||||
auto bundle = std::make_unique<RpcServerBundle>();
|
||||
|
||||
// Allocate new protocol and node server then create RPC server object
|
||||
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);
|
||||
|
||||
// Set up dummy variable map for rpc initialisation with payment
|
||||
if (need_payment) {
|
||||
boost::program_options::variables_map vm;
|
||||
boost::program_options::options_description desc{"fuzz"};
|
||||
command_line::add_arg(desc, cryptonote::arg_data_dir);
|
||||
command_line::add_arg(desc, cryptonote::arg_testnet_on);
|
||||
command_line::add_arg(desc, cryptonote::arg_stagenet_on);
|
||||
cryptonote::core_rpc_server::init_options(desc);
|
||||
|
||||
// Generate random address and use it if it is a valid address with valid format
|
||||
std::string address_arg;
|
||||
if (provider.remaining_bytes() > 95) {
|
||||
std::string random_str = provider.ConsumeBytesAsString(95);
|
||||
if (!random_str.empty() && std::all_of(random_str.begin(), random_str.end(), [](char c) {
|
||||
return isalnum(c);
|
||||
})) {
|
||||
address_arg = "--rpc-payment-address=" + random_str;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to default hardcoded address if generated address is invalid
|
||||
if (address_arg.empty()) {
|
||||
address_arg = "--rpc-payment-address=44AFFq5kSiGBoZKfRLKFY7bUuS5JxqLkZ3Zf1diYv5ZdfTP7hS5gZtSGdgjNXmYGFzRiV3yTgF8Yf4zrhGcq14D3z8PUnHT";
|
||||
}
|
||||
|
||||
// Provide needed payment related configuration for init of core_rpc_server
|
||||
std::vector<const char*> argv = {
|
||||
"fuzz",
|
||||
address_arg.c_str()
|
||||
};
|
||||
boost::program_options::store(boost::program_options::parse_command_line(argv.size(), argv.data(), desc), vm);
|
||||
boost::program_options::notify(vm);
|
||||
bool success = bundle->rpc->init(vm, true, "18089", true, "");
|
||||
if (!success) {
|
||||
// Revert back to a fresh core_rpc_server if payment module init is failed
|
||||
bundle->rpc = std::make_unique<cryptonote::core_rpc_server>(dummy_core, *bundle->dummy_p2p);
|
||||
}
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
bool generate_random_blocks(cryptonote::core& core, FuzzedDataProvider& provider) {
|
||||
static std::vector<cryptonote::block> cached_blocks;
|
||||
static std::vector<cryptonote::blobdata> cached_txs;
|
||||
|
||||
bool use_cached_blocks = provider.ConsumeBool();
|
||||
bool use_cached_txs = provider.ConsumeBool();
|
||||
|
||||
if (!use_cached_blocks || cached_blocks.empty()) {
|
||||
// Create new cached blocks
|
||||
cached_blocks.clear();
|
||||
|
||||
// Prepare the genesis block (initial base block) in the block chains
|
||||
cryptonote::block genesis_block;
|
||||
cryptonote::generate_genesis_block(genesis_block, config::GENESIS_TX, config::GENESIS_NONCE);
|
||||
cached_blocks.push_back(genesis_block);
|
||||
|
||||
// Setup initial values
|
||||
const size_t median_weight = 300000;
|
||||
const size_t block_weight = 100000;
|
||||
const uint8_t version = core.get_blockchain_storage().get_current_hard_fork_version();
|
||||
|
||||
// Accumulate coins for genesis block
|
||||
uint64_t coins = 0;
|
||||
for (const auto& o : genesis_block.miner_tx.vout) {
|
||||
coins += o.amount;
|
||||
}
|
||||
|
||||
// Create random number of miners
|
||||
size_t miner_count = provider.ConsumeIntegralInRange<size_t>(1, 4);
|
||||
std::vector<cryptonote::account_base> miners(miner_count);
|
||||
for (auto& miner : miners) {
|
||||
miner.generate();
|
||||
}
|
||||
|
||||
// Create random number of blocks
|
||||
size_t total_blocks = provider.ConsumeIntegralInRange<size_t>(2, 15);
|
||||
for (size_t i = 0; i < total_blocks; ++i) {
|
||||
// Stop generate blocks if no more bytes left
|
||||
if (provider.remaining_bytes() <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Randomly link a miner identity to this block
|
||||
cryptonote::block blk;
|
||||
size_t selected_miner_index = provider.ConsumeIntegralInRange<size_t>(0, miners.size() - 1);
|
||||
cryptonote::account_base& miner = miners[selected_miner_index];
|
||||
|
||||
// Initialise block property with random value
|
||||
uint64_t height = cached_blocks.size();
|
||||
crypto::hash prev_id = cached_blocks.back().hash;
|
||||
blk.major_version = version;
|
||||
blk.minor_version = version;
|
||||
blk.timestamp = core.get_blockchain_storage().get_adjusted_time(height);
|
||||
blk.prev_id = prev_id;
|
||||
blk.nonce = provider.ConsumeIntegral<uint32_t>();
|
||||
|
||||
// Calculate and accumulate reward
|
||||
uint64_t base_reward = 0, fee = 0;
|
||||
if (!cryptonote::get_block_reward(median_weight, block_weight, coins, base_reward, version)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Link empty miner transaction to block
|
||||
cryptonote::transaction miner_tx;
|
||||
cryptonote::construct_miner_tx(
|
||||
height, median_weight, coins, block_weight,
|
||||
fee, miner.get_keys().m_account_address,
|
||||
miner_tx, cryptonote::blobdata(),
|
||||
1, blk.major_version
|
||||
);
|
||||
for (const auto& o : miner_tx.vout) {
|
||||
coins += o.amount;
|
||||
}
|
||||
miner_tx.version = 2;
|
||||
blk.miner_tx = miner_tx;
|
||||
|
||||
// Add the block to the cache
|
||||
cached_blocks.push_back(blk);
|
||||
}
|
||||
}
|
||||
|
||||
if (cached_blocks.empty()) {
|
||||
// No random block generated, exit early to next iteration
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!use_cached_txs || cached_txs.empty()) {
|
||||
// Create new cache transactions
|
||||
cached_txs.clear();
|
||||
cached_tx_hashes.clear();
|
||||
|
||||
// Random number of transactions to generate
|
||||
const size_t tx_count = provider.ConsumeIntegralInRange<size_t>(1, 4);
|
||||
for (size_t i = 0; i < tx_count; ++i) {
|
||||
// Stop generate transactions if no more bytes left
|
||||
if (provider.remaining_bytes() <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Initialise variables
|
||||
cryptonote::transaction tx;
|
||||
std::vector<cryptonote::tx_source_entry> sources;
|
||||
std::vector<cryptonote::tx_destination_entry> destinations;
|
||||
std::vector<uint8_t> extra;
|
||||
|
||||
// Generate sender account
|
||||
cryptonote::account_base sender;
|
||||
sender.generate();
|
||||
|
||||
// Generate receiver account
|
||||
cryptonote::account_base receiver;
|
||||
receiver.generate();
|
||||
|
||||
// Generate random transfer amount
|
||||
uint64_t transfer_amount = provider.ConsumeIntegralInRange<uint64_t>(10000, 1000000000);
|
||||
uint64_t input_amount = transfer_amount + 10000;
|
||||
|
||||
// Prepare source entry
|
||||
cryptonote::tx_source_entry src{};
|
||||
src.amount = input_amount;
|
||||
src.rct = false;
|
||||
src.real_output = 0;
|
||||
src.real_output_in_tx_index = 0;
|
||||
|
||||
// Derive valid public key
|
||||
hw::device &hwdev = sender.get_keys().get_device();
|
||||
cryptonote::keypair tx_key = cryptonote::keypair::generate(hwdev);
|
||||
crypto::key_derivation derivation;
|
||||
if (!crypto::generate_key_derivation(sender.get_keys().m_account_address.m_view_public_key, tx_key.sec, derivation)) {
|
||||
continue;
|
||||
}
|
||||
crypto::public_key out_pub_key;
|
||||
if (!crypto::derive_public_key(derivation, 0, sender.get_keys().m_account_address.m_spend_public_key, out_pub_key)) {
|
||||
continue;
|
||||
}
|
||||
src.real_out_tx_key = tx_key.pub;
|
||||
|
||||
// Configure output entry for source_entry
|
||||
for (size_t j = 0; j < 10; ++j) {
|
||||
cryptonote::tx_source_entry::output_entry oe;
|
||||
oe.first = j;
|
||||
crypto::public_key pk;
|
||||
|
||||
if (j == src.real_output) {
|
||||
pk = out_pub_key;
|
||||
} else {
|
||||
cryptonote::keypair fake = cryptonote::keypair::generate(hwdev);
|
||||
pk = fake.pub;
|
||||
}
|
||||
|
||||
crypto::view_tag vtag{};
|
||||
vtag.data = static_cast<uint8_t>(provider.ConsumeIntegral<uint8_t>());
|
||||
|
||||
cryptonote::txout_to_tagged_key tagged{};
|
||||
tagged.key = pk;
|
||||
tagged.view_tag = vtag;
|
||||
|
||||
oe.second.dest = *reinterpret_cast<const rct::key*>(&tagged.key);
|
||||
oe.second.mask = rct::identity();
|
||||
src.outputs.push_back(oe);
|
||||
}
|
||||
sources.push_back(src);
|
||||
|
||||
// Prepare destination entry
|
||||
cryptonote::tx_destination_entry dst;
|
||||
dst.amount = transfer_amount;
|
||||
dst.addr = receiver.get_keys().m_account_address;
|
||||
dst.is_subaddress = false;
|
||||
destinations.push_back(dst);
|
||||
|
||||
// Construct the transaction
|
||||
crypto::secret_key tx_secret_key;
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
||||
subaddresses[sender.get_keys().m_account_address.m_spend_public_key] = {0, 0};
|
||||
std::vector<cryptonote::tx_destination_entry> destinations_copy = destinations;
|
||||
|
||||
// Construct transaction objects
|
||||
bool success = cryptonote::construct_tx_and_get_tx_key(
|
||||
sender.get_keys(),
|
||||
subaddresses,
|
||||
sources,
|
||||
destinations_copy,
|
||||
boost::none,
|
||||
extra,
|
||||
tx,
|
||||
tx_secret_key,
|
||||
additional_tx_keys,
|
||||
true,
|
||||
{rct::RangeProofPaddedBulletproof, 0},
|
||||
true
|
||||
);
|
||||
if (!success) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Force version and unlock_time
|
||||
tx.version = 2;
|
||||
tx.unlock_time = 0;
|
||||
|
||||
// Serialise the transaction
|
||||
cryptonote::blobdata tx_blob;
|
||||
if (!cryptonote::t_serializable_object_to_blob(tx, tx_blob)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the transaction to cache
|
||||
cached_txs.push_back(tx_blob);
|
||||
}
|
||||
}
|
||||
|
||||
core.get_blockchain_storage().get_db().batch_start();
|
||||
|
||||
bool added_block = false;
|
||||
for (const auto& blk : cached_blocks) {
|
||||
cryptonote::block_verification_context bvc{};
|
||||
if (core.get_blockchain_storage().add_new_block(blk, bvc)) {
|
||||
added_block = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool added_txs = false;
|
||||
for (const auto& tx_blob : cached_txs) {
|
||||
cryptonote::tx_verification_context tvc;
|
||||
bool accepted = core.handle_incoming_tx(tx_blob, tvc, cryptonote::relay_method::block, true);
|
||||
if (accepted || tvc.m_added_to_pool) {
|
||||
added_txs = true;
|
||||
|
||||
// Store legit hashes
|
||||
cryptonote::transaction tx;
|
||||
if (cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx)) {
|
||||
cached_tx_hashes.push_back(cryptonote::get_transaction_hash(tx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return added_block;
|
||||
}
|
31
tests/fuzz/fuzz_rpc/initialisation.h
Normal file
31
tests/fuzz/fuzz_rpc/initialisation.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include "crypto/hash.h"
|
||||
#include "rpc/core_rpc_server.h"
|
||||
#include <fuzzer/FuzzedDataProvider.h>
|
||||
|
||||
// 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 {
|
||||
public:
|
||||
bool is_synchronized() const;
|
||||
bool relay_transactions(cryptonote::NOTIFY_NEW_TRANSACTIONS::request&, const boost::uuids::uuid&, epee::net_utils::zone, cryptonote::relay_method);
|
||||
bool relay_block(cryptonote::NOTIFY_NEW_FLUFFY_BLOCK::request&, cryptonote::cryptonote_connection_context&);
|
||||
};
|
||||
|
||||
struct RpcServerBundle {
|
||||
std::unique_ptr<cryptonote::t_cryptonote_protocol_handler<cryptonote::core>> proto_handler;
|
||||
std::unique_ptr<nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core>>> dummy_p2p;
|
||||
std::unique_ptr<cryptonote::core_rpc_server> rpc;
|
||||
};
|
||||
|
||||
struct CoreEnv {
|
||||
std::unique_ptr<DummyProtocol> protocol;
|
||||
std::unique_ptr<cryptonote::core> core;
|
||||
};
|
||||
|
||||
std::unique_ptr<CoreEnv> initialise_rpc_core();
|
||||
std::unique_ptr<RpcServerBundle> initialise_rpc_server(cryptonote::core&, FuzzedDataProvider&, bool);
|
||||
bool generate_random_blocks(cryptonote::core&, FuzzedDataProvider&);
|
||||
|
||||
extern std::vector<crypto::hash> cached_tx_hashes;
|
939
tests/fuzz/fuzz_rpc/rpc_endpoints.cpp
Normal file
939
tests/fuzz/fuzz_rpc/rpc_endpoints.cpp
Normal file
@@ -0,0 +1,939 @@
|
||||
#include "rpc_endpoints.h"
|
||||
#include "initialisation.h"
|
||||
#include "rpc/rpc_payment_signature.h"
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
|
||||
// Initialising common objects for calling RPC endpoint functions
|
||||
cryptonote::core_rpc_server::connection_context ctx;
|
||||
epee::json_rpc::error error_resp;
|
||||
|
||||
// Helper function to disable bootstrap daemon
|
||||
void disable_bootstrap_daemon(cryptonote::core_rpc_server& rpc) {
|
||||
cryptonote::COMMAND_RPC_SET_BOOTSTRAP_DAEMON::request req;
|
||||
cryptonote::COMMAND_RPC_SET_BOOTSTRAP_DAEMON::response res;
|
||||
|
||||
req.address = "";
|
||||
req.username = "";
|
||||
req.password = "";
|
||||
req.proxy = "";
|
||||
|
||||
rpc.on_set_bootstrap_daemon(req, res, &ctx);
|
||||
}
|
||||
|
||||
// Retrieve fuzz targets base on SAFE settings
|
||||
std::map<int, std::function<void(cryptonote::core_rpc_server& rpc, FuzzedDataProvider&)>> get_fuzz_targets(bool safe) {
|
||||
std::map<int, std::function<void(cryptonote::core_rpc_server& rpc, FuzzedDataProvider&)>> results;
|
||||
|
||||
if (safe) {
|
||||
// Only return safe and stable fuzz targets after re-indexing
|
||||
for (const auto& kv : safe_fuzz_targets) {
|
||||
results[kv.first - 14] = kv.second;
|
||||
}
|
||||
} else {
|
||||
// Return the full list of fuzz targets
|
||||
results.insert(priority_fuzz_targets.begin(), priority_fuzz_targets.end());
|
||||
results.insert(safe_fuzz_targets.begin(), safe_fuzz_targets.end());
|
||||
results.insert(risky_fuzz_targets.begin(), risky_fuzz_targets.end());
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// Fuzzing functions for different RPC endpoint functions
|
||||
void fuzz_get_height(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_HEIGHT::request req;
|
||||
cryptonote::COMMAND_RPC_GET_HEIGHT::response res;
|
||||
|
||||
rpc.on_get_height(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_blocks(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req;
|
||||
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
req.requested_info = provider.ConsumeIntegralInRange<uint8_t>(0, 2);
|
||||
|
||||
size_t count = provider.ConsumeIntegralInRange<size_t>(0, 16);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
std::string hash_data = provider.ConsumeBytesAsString(sizeof(crypto::hash));
|
||||
if (hash_data.size() != sizeof(crypto::hash)) break;
|
||||
crypto::hash h;
|
||||
std::memcpy(&h, hash_data.data(), sizeof(h));
|
||||
req.block_ids.push_back(h);
|
||||
}
|
||||
|
||||
req.start_height = provider.ConsumeIntegral<uint64_t>();
|
||||
req.pool_info_since = provider.ConsumeIntegral<uint64_t>();
|
||||
req.max_block_count = provider.ConsumeIntegral<uint64_t>();
|
||||
|
||||
req.prune = provider.ConsumeBool();
|
||||
req.no_miner_tx = provider.ConsumeBool();
|
||||
|
||||
rpc.on_get_blocks(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_blocks_by_height(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request req;
|
||||
cryptonote::COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
size_t count = provider.ConsumeIntegralInRange<size_t>(0, 16);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
uint64_t height = provider.ConsumeIntegral<uint64_t>();
|
||||
req.heights.push_back(height);
|
||||
}
|
||||
|
||||
rpc.on_get_blocks_by_height(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_hashes(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_HASHES_FAST::request req;
|
||||
cryptonote::COMMAND_RPC_GET_HASHES_FAST::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
size_t count = provider.ConsumeIntegralInRange<size_t>(0, 16);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
std::string hash_data = provider.ConsumeBytesAsString(sizeof(crypto::hash));
|
||||
if (hash_data.size() != sizeof(crypto::hash)) break;
|
||||
crypto::hash h;
|
||||
std::memcpy(&h, hash_data.data(), sizeof(h));
|
||||
req.block_ids.push_back(h);
|
||||
}
|
||||
|
||||
req.start_height = provider.ConsumeIntegral<uint64_t>();
|
||||
|
||||
rpc.on_get_hashes(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_indexes(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req;
|
||||
cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
std::string txid_data = provider.ConsumeBytesAsString(sizeof(crypto::hash));
|
||||
if (txid_data.size() == sizeof(crypto::hash)) {
|
||||
std::memcpy(&req.txid, txid_data.data(), sizeof(req.txid));
|
||||
}
|
||||
|
||||
rpc.on_get_indexes(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_outs_bin(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_OUTPUTS_BIN::request req;
|
||||
cryptonote::COMMAND_RPC_GET_OUTPUTS_BIN::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
size_t count = provider.ConsumeIntegralInRange<size_t>(0, 16);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
cryptonote::get_outputs_out out;
|
||||
out.amount = provider.ConsumeIntegral<uint64_t>();
|
||||
out.index = provider.ConsumeIntegral<uint64_t>();
|
||||
req.outputs.push_back(out);
|
||||
}
|
||||
|
||||
req.get_txid = provider.ConsumeBool();
|
||||
|
||||
rpc.on_get_outs_bin(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_transactions(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req;
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
// Cached valid transaction hashes
|
||||
size_t real_count = provider.ConsumeIntegralInRange<size_t>(0, cached_tx_hashes.size());
|
||||
for (size_t i = 0; i < real_count; ++i) {
|
||||
const crypto::hash& h = cached_tx_hashes[i];
|
||||
req.txs_hashes.emplace_back(epee::string_tools::pod_to_hex(h));
|
||||
}
|
||||
|
||||
// Random transaction hashes (that still need 64 bytes hex
|
||||
size_t junk_count = provider.ConsumeIntegralInRange<size_t>(0, 3);
|
||||
for (size_t i = 0; i < junk_count; ++i) {
|
||||
std::string junk;
|
||||
for (size_t j = 0; j < 64; ++j) {
|
||||
junk += "0123456789abcdef"[provider.ConsumeIntegralInRange<int>(0, 15)];
|
||||
}
|
||||
req.txs_hashes.emplace_back(std::move(junk));
|
||||
}
|
||||
|
||||
req.decode_as_json = provider.ConsumeBool();
|
||||
req.prune = provider.ConsumeBool();
|
||||
req.split = provider.ConsumeBool();
|
||||
|
||||
rpc.on_get_transactions(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_alt_blocks_hashes(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_ALT_BLOCKS_HASHES::request req;
|
||||
cryptonote::COMMAND_RPC_GET_ALT_BLOCKS_HASHES::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
rpc.on_get_alt_blocks_hashes(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_is_key_image_spent(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req;
|
||||
cryptonote::COMMAND_RPC_IS_KEY_IMAGE_SPENT::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
size_t count = provider.ConsumeIntegralInRange<size_t>(0, 16);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
req.key_images.push_back(provider.ConsumeRandomLengthString(64));
|
||||
}
|
||||
|
||||
rpc.on_is_key_image_spent(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_send_raw_tx(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_SEND_RAW_TX::request req;
|
||||
cryptonote::COMMAND_RPC_SEND_RAW_TX::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
const std::string raw_tx_blob = provider.ConsumeRandomLengthString(512);
|
||||
req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(raw_tx_blob);
|
||||
req.do_not_relay = provider.ConsumeBool();
|
||||
req.do_sanity_checks = provider.ConsumeBool();
|
||||
|
||||
rpc.on_send_raw_tx(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_start_mining(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_START_MINING::request req;
|
||||
cryptonote::COMMAND_RPC_START_MINING::response res;
|
||||
|
||||
req.miner_address = provider.ConsumeRandomLengthString(128);
|
||||
req.threads_count = provider.ConsumeIntegralInRange<uint64_t>(0, 256);
|
||||
req.do_background_mining = provider.ConsumeBool();
|
||||
req.ignore_battery = provider.ConsumeBool();
|
||||
|
||||
rpc.on_start_mining(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_stop_mining(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_STOP_MINING::request req;
|
||||
cryptonote::COMMAND_RPC_STOP_MINING::response res;
|
||||
|
||||
rpc.on_stop_mining(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_mining_status(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_MINING_STATUS::request req;
|
||||
cryptonote::COMMAND_RPC_MINING_STATUS::response res;
|
||||
|
||||
rpc.on_mining_status(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_save_bc(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_SAVE_BC::request req;
|
||||
cryptonote::COMMAND_RPC_SAVE_BC::response res;
|
||||
|
||||
rpc.on_save_bc(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_peer_list(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_PEER_LIST::request req;
|
||||
cryptonote::COMMAND_RPC_GET_PEER_LIST::response res;
|
||||
|
||||
req.public_only = provider.ConsumeBool();
|
||||
req.include_blocked = provider.ConsumeBool();
|
||||
|
||||
rpc.on_get_peer_list(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_public_nodes(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_PUBLIC_NODES::request req;
|
||||
cryptonote::COMMAND_RPC_GET_PUBLIC_NODES::response res;
|
||||
|
||||
req.gray = provider.ConsumeBool();
|
||||
req.white = provider.ConsumeBool();
|
||||
req.include_blocked = provider.ConsumeBool();
|
||||
|
||||
rpc.on_get_public_nodes(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_set_log_hash_rate(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_SET_LOG_HASH_RATE::request req;
|
||||
cryptonote::COMMAND_RPC_SET_LOG_HASH_RATE::response res;
|
||||
|
||||
req.visible = provider.ConsumeBool();
|
||||
|
||||
rpc.on_set_log_hash_rate(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_set_log_level(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_SET_LOG_LEVEL::request req;
|
||||
cryptonote::COMMAND_RPC_SET_LOG_LEVEL::response res;
|
||||
|
||||
req.level = provider.ConsumeIntegral<int8_t>();
|
||||
|
||||
rpc.on_set_log_level(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_set_log_categories(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_SET_LOG_CATEGORIES::request req;
|
||||
cryptonote::COMMAND_RPC_SET_LOG_CATEGORIES::response res;
|
||||
|
||||
req.categories = provider.ConsumeRandomLengthString(32);
|
||||
|
||||
rpc.on_set_log_categories(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_transaction_pool(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::request req;
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
rpc.on_get_transaction_pool(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_transaction_pool_hashes_bin(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request req;
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
rpc.on_get_transaction_pool_hashes_bin(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_transaction_pool_hashes(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request req;
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
rpc.on_get_transaction_pool_hashes(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_transaction_pool_stats(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request req;
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
rpc.on_get_transaction_pool_stats(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_set_bootstrap_daemon(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_SET_BOOTSTRAP_DAEMON::request req;
|
||||
cryptonote::COMMAND_RPC_SET_BOOTSTRAP_DAEMON::response res;
|
||||
|
||||
req.address = provider.ConsumeRandomLengthString(64);
|
||||
req.username = provider.ConsumeRandomLengthString(32);
|
||||
req.password = provider.ConsumeRandomLengthString(32);
|
||||
req.proxy = provider.ConsumeRandomLengthString(32);
|
||||
|
||||
rpc.on_set_bootstrap_daemon(req, res, &ctx);
|
||||
|
||||
// Immediate reset bootstrap daemon to avoid affecting other fuzzing with external calls
|
||||
disable_bootstrap_daemon(rpc);
|
||||
}
|
||||
|
||||
void fuzz_stop_daemon(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_STOP_DAEMON::request req;
|
||||
cryptonote::COMMAND_RPC_STOP_DAEMON::response res;
|
||||
|
||||
rpc.on_stop_daemon(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_info(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_INFO::request req;
|
||||
cryptonote::COMMAND_RPC_GET_INFO::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
rpc.on_get_info(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_net_stats(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_NET_STATS::request req;
|
||||
cryptonote::COMMAND_RPC_GET_NET_STATS::response res;
|
||||
|
||||
rpc.on_get_net_stats(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_limit(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_LIMIT::request req;
|
||||
cryptonote::COMMAND_RPC_GET_LIMIT::response res;
|
||||
|
||||
rpc.on_get_limit(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_set_limit(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_SET_LIMIT::request req;
|
||||
cryptonote::COMMAND_RPC_SET_LIMIT::response res;
|
||||
|
||||
req.limit_down = provider.ConsumeIntegral<int64_t>();
|
||||
req.limit_up = provider.ConsumeIntegral<int64_t>();
|
||||
|
||||
rpc.on_set_limit(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_out_peers(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_OUT_PEERS::request req;
|
||||
cryptonote::COMMAND_RPC_OUT_PEERS::response res;
|
||||
|
||||
req.set = provider.ConsumeBool();
|
||||
req.out_peers = provider.ConsumeIntegral<uint32_t>();
|
||||
|
||||
rpc.on_out_peers(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_in_peers(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_IN_PEERS::request req;
|
||||
cryptonote::COMMAND_RPC_IN_PEERS::response res;
|
||||
|
||||
req.set = provider.ConsumeBool();
|
||||
req.in_peers = provider.ConsumeIntegral<uint32_t>();
|
||||
|
||||
rpc.on_in_peers(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_outs(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_OUTPUTS::request req;
|
||||
cryptonote::COMMAND_RPC_GET_OUTPUTS::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
size_t count = provider.ConsumeIntegralInRange<size_t>(0, 5);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
cryptonote::get_outputs_out out;
|
||||
out.amount = provider.ConsumeIntegral<uint64_t>();
|
||||
out.index = provider.ConsumeIntegral<uint64_t>();
|
||||
req.outputs.push_back(out);
|
||||
}
|
||||
|
||||
req.get_txid = provider.ConsumeBool();
|
||||
|
||||
rpc.on_get_outs(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_update(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_UPDATE::request req;
|
||||
cryptonote::COMMAND_RPC_UPDATE::response res;
|
||||
|
||||
req.command = provider.ConsumeRandomLengthString(16);
|
||||
req.path = provider.ConsumeRandomLengthString(32);
|
||||
|
||||
rpc.on_update(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_output_distribution_bin(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request req;
|
||||
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response res;
|
||||
req.client = "fuzz";
|
||||
req.amounts = {0};
|
||||
|
||||
req.from_height = provider.ConsumeIntegral<uint64_t>();
|
||||
req.to_height = req.from_height + provider.ConsumeIntegralInRange<uint64_t>(0, 1000);
|
||||
req.cumulative = provider.ConsumeBool();
|
||||
req.binary = provider.ConsumeBool();
|
||||
req.compress = provider.ConsumeBool();
|
||||
|
||||
rpc.on_get_output_distribution_bin(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_pop_blocks(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_POP_BLOCKS::request req;
|
||||
cryptonote::COMMAND_RPC_POP_BLOCKS::response res;
|
||||
|
||||
req.nblocks = provider.ConsumeIntegral<uint64_t>();
|
||||
rpc.on_pop_blocks(req, res, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_getblockcount(cryptonote::core_rpc_server& rpc, FuzzedDataProvider&) {
|
||||
cryptonote::COMMAND_RPC_GETBLOCKCOUNT::request req;
|
||||
cryptonote::COMMAND_RPC_GETBLOCKCOUNT::response res;
|
||||
|
||||
rpc.on_getblockcount(req, res, &ctx);
|
||||
}
|
||||
|
||||
|
||||
// Fuzzing functions for RPC JSONAPI endpoint functions
|
||||
void fuzz_getblockhash(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GETBLOCKHASH::request req;
|
||||
cryptonote::COMMAND_RPC_GETBLOCKHASH::response res;
|
||||
|
||||
size_t count = provider.ConsumeIntegralInRange<size_t>(0, 10);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
req.push_back(provider.ConsumeIntegral<uint64_t>());
|
||||
}
|
||||
|
||||
rpc.on_getblockhash(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_getblocktemplate(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GETBLOCKTEMPLATE::request req;
|
||||
cryptonote::COMMAND_RPC_GETBLOCKTEMPLATE::response res;
|
||||
|
||||
if (provider.ConsumeBool()) {
|
||||
req.wallet_address = "9uVsvEryzpN8WH2t1WWhFFCG5tS8cBNdmJYNRuckLENFimfauV5pZKeS1P2CbxGkSDTUPHXWwiYE5ZGSXDAGbaZgDxobqDN";
|
||||
} else {
|
||||
req.wallet_address = provider.ConsumeRandomLengthString(128);
|
||||
}
|
||||
|
||||
req.reserve_size = provider.ConsumeIntegral<uint64_t>();
|
||||
req.prev_block = provider.ConsumeRandomLengthString(64);
|
||||
req.extra_nonce = provider.ConsumeRandomLengthString(32);
|
||||
|
||||
rpc.on_getblocktemplate(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_getminerdata(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GETMINERDATA::request req;
|
||||
cryptonote::COMMAND_RPC_GETMINERDATA::response res;
|
||||
|
||||
rpc.on_getminerdata(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_calcpow(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_CALCPOW::request req;
|
||||
cryptonote::COMMAND_RPC_CALCPOW::response res;
|
||||
|
||||
req.major_version = provider.ConsumeIntegral<uint8_t>();
|
||||
req.height = provider.ConsumeIntegral<uint64_t>();
|
||||
req.block_blob = provider.ConsumeRandomLengthString(128);
|
||||
req.seed_hash = provider.ConsumeRandomLengthString(64);
|
||||
|
||||
rpc.on_calcpow(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_add_aux_pow(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_ADD_AUX_POW::request req;
|
||||
cryptonote::COMMAND_RPC_ADD_AUX_POW::response res;
|
||||
|
||||
req.blocktemplate_blob = provider.ConsumeRandomLengthString(128);
|
||||
|
||||
size_t count = provider.ConsumeIntegralInRange<size_t>(0, 4);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
cryptonote::COMMAND_RPC_ADD_AUX_POW::aux_pow_t aux;
|
||||
aux.id = provider.ConsumeRandomLengthString(32);
|
||||
aux.hash = provider.ConsumeRandomLengthString(64);
|
||||
req.aux_pow.push_back(aux);
|
||||
}
|
||||
|
||||
rpc.on_add_aux_pow(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_submitblock(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_SUBMITBLOCK::request req;
|
||||
cryptonote::COMMAND_RPC_SUBMITBLOCK::response res;
|
||||
|
||||
std::string hash_data = provider.ConsumeBytesAsString(sizeof(crypto::hash));
|
||||
if (hash_data.size() != sizeof(crypto::hash)) return;
|
||||
crypto::hash h;
|
||||
std::memcpy(&h, hash_data.data(), sizeof(h));
|
||||
std::string hex_str = epee::string_tools::pod_to_hex(h);
|
||||
req.push_back(hex_str);
|
||||
|
||||
rpc.on_submitblock(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_generateblocks(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GENERATEBLOCKS::request req;
|
||||
cryptonote::COMMAND_RPC_GENERATEBLOCKS::response res;
|
||||
|
||||
req.amount_of_blocks = provider.ConsumeIntegral<uint64_t>();
|
||||
req.wallet_address = provider.ConsumeRandomLengthString(128);
|
||||
req.prev_block = provider.ConsumeRandomLengthString(128);
|
||||
req.starting_nonce = provider.ConsumeIntegral<uint32_t>();
|
||||
|
||||
rpc.on_generateblocks(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_last_block_header(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::request req;
|
||||
cryptonote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
req.fill_pow_hash = provider.ConsumeBool();
|
||||
|
||||
rpc.on_get_last_block_header(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_block_header_by_hash(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request req;
|
||||
cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
req.hash = provider.ConsumeRandomLengthString(64);
|
||||
|
||||
size_t count = provider.ConsumeIntegralInRange<size_t>(0, 3);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
req.hashes.push_back(provider.ConsumeRandomLengthString(64));
|
||||
}
|
||||
|
||||
req.fill_pow_hash = provider.ConsumeBool();
|
||||
|
||||
rpc.on_get_block_header_by_hash(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_block_header_by_height(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request req;
|
||||
cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
req.height = provider.ConsumeIntegral<uint64_t>();
|
||||
req.fill_pow_hash = provider.ConsumeBool();
|
||||
|
||||
rpc.on_get_block_header_by_height(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_block_headers_range(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request req;
|
||||
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
req.start_height = provider.ConsumeIntegral<uint64_t>();
|
||||
req.end_height = provider.ConsumeIntegral<uint64_t>();
|
||||
req.fill_pow_hash = provider.ConsumeBool();
|
||||
|
||||
rpc.on_get_block_headers_range(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_block(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_BLOCK::request req;
|
||||
cryptonote::COMMAND_RPC_GET_BLOCK::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
req.hash = provider.ConsumeRandomLengthString(64);
|
||||
req.height = provider.ConsumeIntegral<uint64_t>();
|
||||
req.fill_pow_hash = provider.ConsumeBool();
|
||||
|
||||
rpc.on_get_block(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_connections(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_CONNECTIONS::request req;
|
||||
cryptonote::COMMAND_RPC_GET_CONNECTIONS::response res;
|
||||
|
||||
rpc.on_get_connections(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_info_json(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_INFO::request req;
|
||||
cryptonote::COMMAND_RPC_GET_INFO::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
rpc.on_get_info_json(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_hard_fork_info(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req;
|
||||
cryptonote::COMMAND_RPC_HARD_FORK_INFO::response res;
|
||||
|
||||
req.version = provider.ConsumeIntegral<uint8_t>();
|
||||
|
||||
rpc.on_hard_fork_info(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_set_bans(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_SETBANS::request req;
|
||||
cryptonote::COMMAND_RPC_SETBANS::response res;
|
||||
|
||||
int count = provider.ConsumeIntegralInRange<int>(0, 4);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
cryptonote::COMMAND_RPC_SETBANS::ban entry;
|
||||
entry.host = provider.ConsumeRandomLengthString(32);
|
||||
entry.ip = provider.ConsumeIntegral<uint32_t>();
|
||||
entry.ban = provider.ConsumeBool();
|
||||
entry.seconds = provider.ConsumeIntegral<uint32_t>();
|
||||
req.bans.push_back(entry);
|
||||
}
|
||||
|
||||
rpc.on_set_bans(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_bans(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GETBANS::request req;
|
||||
cryptonote::COMMAND_RPC_GETBANS::response res;
|
||||
|
||||
rpc.on_get_bans(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_banned(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_BANNED::request req;
|
||||
cryptonote::COMMAND_RPC_BANNED::response res;
|
||||
|
||||
req.address = provider.ConsumeRandomLengthString(32);
|
||||
|
||||
rpc.on_banned(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_flush_txpool(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_FLUSH_TRANSACTION_POOL::request req;
|
||||
cryptonote::COMMAND_RPC_FLUSH_TRANSACTION_POOL::response res;
|
||||
|
||||
int count = provider.ConsumeIntegralInRange<int>(0, 8);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
req.txids.push_back(provider.ConsumeRandomLengthString(64));
|
||||
}
|
||||
|
||||
rpc.on_flush_txpool(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_output_histogram(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req;
|
||||
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
int amount_count = provider.ConsumeIntegralInRange<int>(0, 5);
|
||||
for (int i = 0; i < amount_count; ++i) {
|
||||
req.amounts.push_back(provider.ConsumeIntegral<uint64_t>());
|
||||
}
|
||||
req.min_count = provider.ConsumeIntegral<uint64_t>();
|
||||
req.max_count = provider.ConsumeIntegral<uint64_t>();
|
||||
req.unlocked = provider.ConsumeBool();
|
||||
req.recent_cutoff = provider.ConsumeIntegral<uint64_t>();
|
||||
|
||||
rpc.on_get_output_histogram(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_version(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_VERSION::request req;
|
||||
cryptonote::COMMAND_RPC_GET_VERSION::response res;
|
||||
|
||||
rpc.on_get_version(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_coinbase_tx_sum(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_COINBASE_TX_SUM::request req;
|
||||
cryptonote::COMMAND_RPC_GET_COINBASE_TX_SUM::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
req.height = provider.ConsumeIntegral<uint64_t>();
|
||||
req.count = provider.ConsumeIntegral<uint64_t>();
|
||||
|
||||
rpc.on_get_coinbase_tx_sum(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_base_fee_estimate(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req;
|
||||
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
req.grace_blocks = provider.ConsumeIntegral<uint64_t>();
|
||||
|
||||
rpc.on_get_base_fee_estimate(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_alternate_chains(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::request req;
|
||||
cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::response res;
|
||||
|
||||
rpc.on_get_alternate_chains(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_relay_tx(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_RELAY_TX::request req;
|
||||
cryptonote::COMMAND_RPC_RELAY_TX::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
size_t count = provider.ConsumeIntegralInRange<size_t>(0, 4);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
std::string hash_data = provider.ConsumeBytesAsString(sizeof(crypto::hash));
|
||||
if (hash_data.size() != sizeof(crypto::hash)) break;
|
||||
crypto::hash h;
|
||||
std::memcpy(&h, hash_data.data(), sizeof(h));
|
||||
std::string hex_str = epee::string_tools::pod_to_hex(h);
|
||||
req.txids.push_back(hex_str);
|
||||
}
|
||||
epee::json_rpc::error error_resp;
|
||||
rpc.on_relay_tx(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_sync_info(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_SYNC_INFO::request req;
|
||||
cryptonote::COMMAND_RPC_SYNC_INFO::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
rpc.on_sync_info(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_txpool_backlog(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request req;
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
rpc.on_get_txpool_backlog(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_output_distribution(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request req;
|
||||
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response res;
|
||||
req.client = "fuzz";
|
||||
req.amounts = {0};
|
||||
|
||||
req.from_height = provider.ConsumeIntegral<uint64_t>();
|
||||
req.to_height = req.from_height + provider.ConsumeIntegralInRange<uint64_t>(0, 1000);
|
||||
req.cumulative = provider.ConsumeBool();
|
||||
req.binary = provider.ConsumeBool();
|
||||
req.compress = provider.ConsumeBool();
|
||||
|
||||
rpc.on_get_output_distribution(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_prune_blockchain(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_PRUNE_BLOCKCHAIN::request req;
|
||||
cryptonote::COMMAND_RPC_PRUNE_BLOCKCHAIN::response res;
|
||||
|
||||
req.check = provider.ConsumeBool();
|
||||
|
||||
rpc.on_prune_blockchain(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_flush_cache(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_FLUSH_CACHE::request req;
|
||||
cryptonote::COMMAND_RPC_FLUSH_CACHE::response res;
|
||||
|
||||
req.bad_blocks = provider.ConsumeBool();
|
||||
|
||||
rpc.on_flush_cache(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_get_txids_loose(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_GET_TXIDS_LOOSE::request req;
|
||||
cryptonote::COMMAND_RPC_GET_TXIDS_LOOSE::response res;
|
||||
|
||||
req.txid_template = provider.ConsumeRandomLengthString(64);
|
||||
req.num_matching_bits = provider.ConsumeIntegral<uint32_t>();
|
||||
|
||||
rpc.on_get_txids_loose(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_rpc_access_info(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_ACCESS_INFO::request req;
|
||||
cryptonote::COMMAND_RPC_ACCESS_INFO::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
rpc.on_rpc_access_info(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_rpc_access_submit_nonce(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_ACCESS_SUBMIT_NONCE::request req;
|
||||
cryptonote::COMMAND_RPC_ACCESS_SUBMIT_NONCE::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
req.nonce = provider.ConsumeIntegral<uint32_t>();
|
||||
req.cookie = provider.ConsumeIntegral<uint32_t>();
|
||||
|
||||
rpc.on_rpc_access_submit_nonce(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_rpc_access_pay(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_ACCESS_PAY::request req;
|
||||
cryptonote::COMMAND_RPC_ACCESS_PAY::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
req.paying_for = provider.ConsumeRandomLengthString(32);
|
||||
req.payment = provider.ConsumeIntegral<uint64_t>();
|
||||
|
||||
rpc.on_rpc_access_pay(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_rpc_access_tracking(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_ACCESS_TRACKING::request req;
|
||||
cryptonote::COMMAND_RPC_ACCESS_TRACKING::response res;
|
||||
|
||||
req.clear = provider.ConsumeBool();
|
||||
|
||||
rpc.on_rpc_access_tracking(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_rpc_access_data(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_ACCESS_DATA::request req;
|
||||
cryptonote::COMMAND_RPC_ACCESS_DATA::response res;
|
||||
|
||||
rpc.on_rpc_access_data(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
void fuzz_rpc_access_account(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
|
||||
cryptonote::COMMAND_RPC_ACCESS_ACCOUNT::request req;
|
||||
cryptonote::COMMAND_RPC_ACCESS_ACCOUNT::response res;
|
||||
req.client = "fuzz";
|
||||
|
||||
req.delta_balance = provider.ConsumeIntegral<int64_t>();
|
||||
|
||||
rpc.on_rpc_access_account(req, res, error_resp, &ctx);
|
||||
}
|
||||
|
||||
// Maps storing all fuzzing functions
|
||||
std::map<int, std::function<void(cryptonote::core_rpc_server&, FuzzedDataProvider&)>> priority_fuzz_targets = {
|
||||
{0, fuzz_get_blocks},
|
||||
{1, fuzz_get_blocks_by_height},
|
||||
{2, fuzz_get_hashes},
|
||||
{3, fuzz_get_outs_bin},
|
||||
{4, fuzz_get_transactions},
|
||||
{5, fuzz_is_key_image_spent},
|
||||
{6, fuzz_send_raw_tx},
|
||||
{7, fuzz_get_output_distribution_bin},
|
||||
{8, fuzz_pop_blocks},
|
||||
{9, fuzz_getblocktemplate},
|
||||
{10, fuzz_submitblock},
|
||||
{11, fuzz_generateblocks},
|
||||
{12, fuzz_relay_tx},
|
||||
{13, fuzz_get_output_distribution},
|
||||
};
|
||||
|
||||
std::map<int, std::function<void(cryptonote::core_rpc_server&, FuzzedDataProvider&)>> safe_fuzz_targets = {
|
||||
{14, fuzz_get_height},
|
||||
{15, fuzz_get_indexes},
|
||||
{16, fuzz_get_alt_blocks_hashes},
|
||||
{17, fuzz_get_peer_list},
|
||||
{18, fuzz_get_public_nodes},
|
||||
{19, fuzz_set_log_hash_rate},
|
||||
{20, fuzz_set_log_level},
|
||||
{21, fuzz_set_log_categories},
|
||||
{22, fuzz_get_transaction_pool},
|
||||
{23, fuzz_get_transaction_pool_hashes_bin},
|
||||
{24, fuzz_get_transaction_pool_hashes},
|
||||
{25, fuzz_get_transaction_pool_stats},
|
||||
{26, fuzz_get_info},
|
||||
{27, fuzz_get_net_stats},
|
||||
{28, fuzz_get_limit},
|
||||
{29, fuzz_set_limit},
|
||||
{30, fuzz_out_peers},
|
||||
{31, fuzz_in_peers},
|
||||
{32, fuzz_get_outs},
|
||||
{33, fuzz_getblockcount},
|
||||
{34, fuzz_getblockhash},
|
||||
{35, fuzz_getminerdata},
|
||||
{36, fuzz_calcpow},
|
||||
{37, fuzz_get_last_block_header},
|
||||
{38, fuzz_get_block_header_by_hash},
|
||||
{39, fuzz_get_block_header_by_height},
|
||||
{40, fuzz_get_block_headers_range},
|
||||
{41, fuzz_get_block},
|
||||
{42, fuzz_get_connections},
|
||||
{43, fuzz_get_info_json},
|
||||
{44, fuzz_hard_fork_info},
|
||||
{45, fuzz_set_bans},
|
||||
{46, fuzz_get_bans},
|
||||
{47, fuzz_banned},
|
||||
{48, fuzz_get_output_histogram},
|
||||
{49, fuzz_get_version},
|
||||
{50, fuzz_get_coinbase_tx_sum},
|
||||
{51, fuzz_get_base_fee_estimate},
|
||||
{52, fuzz_get_alternate_chains},
|
||||
{53, fuzz_sync_info},
|
||||
{54, fuzz_get_txpool_backlog},
|
||||
};
|
||||
|
||||
std::map<int, std::function<void(cryptonote::core_rpc_server&, FuzzedDataProvider&)>> risky_fuzz_targets = {
|
||||
{55, fuzz_start_mining},
|
||||
{56, fuzz_stop_mining},
|
||||
{57, fuzz_mining_status},
|
||||
{58, fuzz_save_bc},
|
||||
{59, fuzz_set_bootstrap_daemon},
|
||||
{60, fuzz_stop_daemon},
|
||||
{61, fuzz_update},
|
||||
{62, fuzz_add_aux_pow},
|
||||
{63, fuzz_flush_txpool},
|
||||
{64, fuzz_flush_cache},
|
||||
{65, fuzz_get_txids_loose},
|
||||
{66, fuzz_rpc_access_info},
|
||||
{67, fuzz_rpc_access_submit_nonce},
|
||||
{68, fuzz_rpc_access_pay},
|
||||
{69, fuzz_rpc_access_tracking},
|
||||
{70, fuzz_rpc_access_data},
|
||||
{71, fuzz_rpc_access_account},
|
||||
// {72, fuzz_prune_blockchain},
|
||||
};
|
92
tests/fuzz/fuzz_rpc/rpc_endpoints.h
Normal file
92
tests/fuzz/fuzz_rpc/rpc_endpoints.h
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
#include "common/perf_timer.h"
|
||||
#include "rpc/core_rpc_server.h"
|
||||
#include "fuzzer/FuzzedDataProvider.h"
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace tools {
|
||||
extern __thread std::vector<LoggingPerformanceTimer*> *performance_timers;
|
||||
}
|
||||
|
||||
void disable_bootstrap_daemon(cryptonote::core_rpc_server& rpc);
|
||||
std::map<int, std::function<void(cryptonote::core_rpc_server& rpc, FuzzedDataProvider&)>> get_fuzz_targets(bool safe);
|
||||
|
||||
void fuzz_get_height(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_blocks(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_blocks_by_height(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_hashes(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_indexes(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_outs_bin(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_transactions(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_alt_blocks_hashes(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_is_key_image_spent(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_send_raw_tx(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_start_mining(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_stop_mining(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_mining_status(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_save_bc(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_peer_list(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_public_nodes(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_set_log_hash_rate(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_set_log_level(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_set_log_categories(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_transaction_pool(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_transaction_pool_hashes_bin(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_transaction_pool_hashes(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_transaction_pool_stats(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_set_bootstrap_daemon(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_stop_daemon(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_info(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_net_stats(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_limit(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_set_limit(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_out_peers(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_in_peers(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_outs(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_update(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_output_distribution_bin(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_pop_blocks(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_getblockcount(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_getblockhash(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_getblocktemplate(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_getminerdata(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_calcpow(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_add_aux_pow(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_submitblock(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_generateblocks(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_last_block_header(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_block_header_by_hash(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_block_header_by_height(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_block_headers_range(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_block(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_connections(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_info_json(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_hard_fork_info(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_set_bans(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_bans(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_banned(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_flush_txpool(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_output_histogram(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_version(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_coinbase_tx_sum(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_base_fee_estimate(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_alternate_chains(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_relay_tx(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_sync_info(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_txpool_backlog(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_output_distribution(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_prune_blockchain(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_flush_cache(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_txids_loose(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_rpc_access_info(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_rpc_access_submit_nonce(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_rpc_access_pay(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_rpc_access_tracking(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_rpc_access_data(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_rpc_access_account(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
void fuzz_get_height(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
|
||||
|
||||
extern std::map<int, std::function<void(cryptonote::core_rpc_server&, FuzzedDataProvider&)>> priority_fuzz_targets;
|
||||
extern std::map<int, std::function<void(cryptonote::core_rpc_server&, FuzzedDataProvider&)>> safe_fuzz_targets;
|
||||
extern std::map<int, std::function<void(cryptonote::core_rpc_server&, FuzzedDataProvider&)>> risky_fuzz_targets;
|
87
tests/fuzz/fuzz_rpc/zmq_endpoints.cpp
Normal file
87
tests/fuzz/fuzz_rpc/zmq_endpoints.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#include "cryptonote_basic/events.h"
|
||||
#include "cryptonote_core/tx_pool.h"
|
||||
#include "rpc/zmq_pub.h"
|
||||
#include "rpc_endpoints.h"
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
|
||||
using namespace cryptonote;
|
||||
using namespace cryptonote::listener;
|
||||
|
||||
void fuzz_sub_request(zmq_pub& pub, FuzzedDataProvider& provider) {
|
||||
std::string sub = provider.ConsumeRandomLengthString(64);
|
||||
pub.sub_request(boost::string_ref(sub));
|
||||
}
|
||||
|
||||
void fuzz_send_chain_main(zmq_pub& pub, FuzzedDataProvider& provider) {
|
||||
uint64_t height = provider.ConsumeIntegral<uint64_t>();
|
||||
size_t blk_count = provider.ConsumeIntegralInRange<size_t>(0, 4);
|
||||
std::vector<block> blocks;
|
||||
|
||||
for (size_t i = 0; i < blk_count; ++i) {
|
||||
block b{};
|
||||
b.major_version = provider.ConsumeIntegral<uint8_t>();
|
||||
b.minor_version = provider.ConsumeIntegral<uint8_t>();
|
||||
b.timestamp = provider.ConsumeIntegral<uint64_t>();
|
||||
b.prev_id = crypto::null_hash;
|
||||
b.nonce = provider.ConsumeIntegral<uint32_t>();
|
||||
blocks.push_back(b);
|
||||
}
|
||||
|
||||
pub.send_chain_main(height, epee::span<const block>(blocks.data(), blocks.size()));
|
||||
}
|
||||
|
||||
void fuzz_send_miner_data(zmq_pub& pub, FuzzedDataProvider& provider) {
|
||||
uint8_t major = provider.ConsumeIntegral<uint8_t>();
|
||||
uint64_t h = provider.ConsumeIntegral<uint64_t>();
|
||||
|
||||
crypto::hash prev_id, seed_hash;
|
||||
std::memset(&prev_id, 0x01, sizeof(prev_id));
|
||||
std::memset(&seed_hash, 0x02, sizeof(seed_hash));
|
||||
difficulty_type diff = provider.ConsumeIntegral<uint64_t>();
|
||||
|
||||
uint64_t median_weight = provider.ConsumeIntegral<uint64_t>();
|
||||
uint64_t coins = provider.ConsumeIntegral<uint64_t>();
|
||||
std::vector<tx_block_template_backlog_entry> backlog;
|
||||
size_t count = provider.ConsumeIntegralInRange<size_t>(0, 3);
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
tx_block_template_backlog_entry entry;
|
||||
entry.weight = provider.ConsumeIntegral<uint64_t>();
|
||||
entry.fee = provider.ConsumeIntegral<uint64_t>();
|
||||
backlog.push_back(entry);
|
||||
}
|
||||
|
||||
pub.send_miner_data(major, h, prev_id, seed_hash, diff, median_weight, coins, backlog);
|
||||
}
|
||||
|
||||
void fuzz_send_txpool_add(zmq_pub& pub, FuzzedDataProvider& provider) {
|
||||
size_t count = provider.ConsumeIntegralInRange<size_t>(0, 3);
|
||||
std::vector<txpool_event> events;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
txpool_event evt{};
|
||||
|
||||
evt.res = provider.ConsumeBool();
|
||||
|
||||
auto blob = provider.ConsumeRandomLengthString(128);
|
||||
if (!parse_and_validate_tx_from_blob(blob, evt.tx)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
evt.hash = get_transaction_hash(evt.tx);
|
||||
evt.blob_size = blob.size();
|
||||
evt.weight = provider.ConsumeIntegral<uint64_t>();
|
||||
|
||||
events.push_back(std::move(evt));
|
||||
}
|
||||
|
||||
pub.send_txpool_add(std::move(events));
|
||||
}
|
||||
|
||||
// Map all zmq targets
|
||||
std::map<int, std::function<void(zmq_pub&, FuzzedDataProvider&)>> zmq_targets = {
|
||||
{0, fuzz_sub_request},
|
||||
{1, fuzz_send_chain_main},
|
||||
{2, fuzz_send_miner_data},
|
||||
{3, fuzz_send_txpool_add},
|
||||
};
|
19
tests/fuzz/fuzz_rpc/zmq_endpoints.h
Normal file
19
tests/fuzz/fuzz_rpc/zmq_endpoints.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "rpc/zmq_pub.h"
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "span.h"
|
||||
#include <fuzzer/FuzzedDataProvider.h>
|
||||
#include <zmq.h>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
using namespace cryptonote;
|
||||
using namespace cryptonote::listener;
|
||||
|
||||
void fuzz_sub_request(zmq_pub&, FuzzedDataProvider&);
|
||||
void fuzz_send_chain_main(zmq_pub&, FuzzedDataProvider&);
|
||||
void fuzz_send_miner_data(zmq_pub&, FuzzedDataProvider&);
|
||||
void fuzz_send_txpool_add(zmq_pub&, FuzzedDataProvider&);
|
||||
|
||||
extern std::map<int, std::function<void(zmq_pub&, FuzzedDataProvider&)>> zmq_targets;
|
397
tests/fuzz/include/fuzzer/FuzzedDataProvider.h
Normal file
397
tests/fuzz/include/fuzzer/FuzzedDataProvider.h
Normal file
@@ -0,0 +1,397 @@
|
||||
//===- FuzzedDataProvider.h - Utility header for fuzz targets ---*- C++ -* ===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// A single header library providing an utility class to break up an array of
|
||||
// bytes. Whenever run on the same input, provides the same output, as long as
|
||||
// its methods are called in the same order, with the same arguments.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_
|
||||
#define LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// In addition to the comments below, the API is also briefly documented at
|
||||
// https://github.com/google/fuzzing/blob/master/docs/split-inputs.md#fuzzed-data-provider
|
||||
class FuzzedDataProvider {
|
||||
public:
|
||||
// |data| is an array of length |size| that the FuzzedDataProvider wraps to
|
||||
// provide more granular access. |data| must outlive the FuzzedDataProvider.
|
||||
FuzzedDataProvider(const uint8_t *data, size_t size)
|
||||
: data_ptr_(data), remaining_bytes_(size) {}
|
||||
~FuzzedDataProvider() = default;
|
||||
|
||||
// See the implementation below (after the class definition) for more verbose
|
||||
// comments for each of the methods.
|
||||
|
||||
// Methods returning std::vector of bytes. These are the most popular choice
|
||||
// when splitting fuzzing input into pieces, as every piece is put into a
|
||||
// separate buffer (i.e. ASan would catch any under-/overflow) and the memory
|
||||
// will be released automatically.
|
||||
template <typename T> std::vector<T> ConsumeBytes(size_t num_bytes);
|
||||
template <typename T>
|
||||
std::vector<T> ConsumeBytesWithTerminator(size_t num_bytes, T terminator = 0);
|
||||
template <typename T> std::vector<T> ConsumeRemainingBytes();
|
||||
|
||||
// Methods returning strings. Use only when you need a std::string or a null
|
||||
// terminated C-string. Otherwise, prefer the methods returning std::vector.
|
||||
std::string ConsumeBytesAsString(size_t num_bytes);
|
||||
std::string ConsumeRandomLengthString(size_t max_length);
|
||||
std::string ConsumeRandomLengthString();
|
||||
std::string ConsumeRemainingBytesAsString();
|
||||
|
||||
// Methods returning integer values.
|
||||
template <typename T> T ConsumeIntegral();
|
||||
template <typename T> T ConsumeIntegralInRange(T min, T max);
|
||||
|
||||
// Methods returning floating point values.
|
||||
template <typename T> T ConsumeFloatingPoint();
|
||||
template <typename T> T ConsumeFloatingPointInRange(T min, T max);
|
||||
|
||||
// 0 <= return value <= 1.
|
||||
template <typename T> T ConsumeProbability();
|
||||
|
||||
bool ConsumeBool();
|
||||
|
||||
// Returns a value chosen from the given enum.
|
||||
template <typename T> T ConsumeEnum();
|
||||
|
||||
// Returns a value from the given array.
|
||||
template <typename T, size_t size> T PickValueInArray(const T (&array)[size]);
|
||||
template <typename T, size_t size>
|
||||
T PickValueInArray(const std::array<T, size> &array);
|
||||
template <typename T> T PickValueInArray(std::initializer_list<const T> list);
|
||||
|
||||
// Writes data to the given destination and returns number of bytes written.
|
||||
size_t ConsumeData(void *destination, size_t num_bytes);
|
||||
|
||||
// Reports the remaining bytes available for fuzzed input.
|
||||
size_t remaining_bytes() { return remaining_bytes_; }
|
||||
|
||||
private:
|
||||
FuzzedDataProvider(const FuzzedDataProvider &) = delete;
|
||||
FuzzedDataProvider &operator=(const FuzzedDataProvider &) = delete;
|
||||
|
||||
void CopyAndAdvance(void *destination, size_t num_bytes);
|
||||
|
||||
void Advance(size_t num_bytes);
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> ConsumeBytes(size_t size, size_t num_bytes);
|
||||
|
||||
template <typename TS, typename TU> TS ConvertUnsignedToSigned(TU value);
|
||||
|
||||
const uint8_t *data_ptr_;
|
||||
size_t remaining_bytes_;
|
||||
};
|
||||
|
||||
// Returns a std::vector containing |num_bytes| of input data. If fewer than
|
||||
// |num_bytes| of data remain, returns a shorter std::vector containing all
|
||||
// of the data that's left. Can be used with any byte sized type, such as
|
||||
// char, unsigned char, uint8_t, etc.
|
||||
template <typename T>
|
||||
std::vector<T> FuzzedDataProvider::ConsumeBytes(size_t num_bytes) {
|
||||
num_bytes = std::min(num_bytes, remaining_bytes_);
|
||||
return ConsumeBytes<T>(num_bytes, num_bytes);
|
||||
}
|
||||
|
||||
// Similar to |ConsumeBytes|, but also appends the terminator value at the end
|
||||
// of the resulting vector. Useful, when a mutable null-terminated C-string is
|
||||
// needed, for example. But that is a rare case. Better avoid it, if possible,
|
||||
// and prefer using |ConsumeBytes| or |ConsumeBytesAsString| methods.
|
||||
template <typename T>
|
||||
std::vector<T> FuzzedDataProvider::ConsumeBytesWithTerminator(size_t num_bytes,
|
||||
T terminator) {
|
||||
num_bytes = std::min(num_bytes, remaining_bytes_);
|
||||
std::vector<T> result = ConsumeBytes<T>(num_bytes + 1, num_bytes);
|
||||
result.back() = terminator;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns a std::vector containing all remaining bytes of the input data.
|
||||
template <typename T>
|
||||
std::vector<T> FuzzedDataProvider::ConsumeRemainingBytes() {
|
||||
return ConsumeBytes<T>(remaining_bytes_);
|
||||
}
|
||||
|
||||
// Returns a std::string containing |num_bytes| of input data. Using this and
|
||||
// |.c_str()| on the resulting string is the best way to get an immutable
|
||||
// null-terminated C string. If fewer than |num_bytes| of data remain, returns
|
||||
// a shorter std::string containing all of the data that's left.
|
||||
inline std::string FuzzedDataProvider::ConsumeBytesAsString(size_t num_bytes) {
|
||||
static_assert(sizeof(std::string::value_type) == sizeof(uint8_t),
|
||||
"ConsumeBytesAsString cannot convert the data to a string.");
|
||||
|
||||
num_bytes = std::min(num_bytes, remaining_bytes_);
|
||||
std::string result(
|
||||
reinterpret_cast<const std::string::value_type *>(data_ptr_), num_bytes);
|
||||
Advance(num_bytes);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns a std::string of length from 0 to |max_length|. When it runs out of
|
||||
// input data, returns what remains of the input. Designed to be more stable
|
||||
// with respect to a fuzzer inserting characters than just picking a random
|
||||
// length and then consuming that many bytes with |ConsumeBytes|.
|
||||
inline std::string
|
||||
FuzzedDataProvider::ConsumeRandomLengthString(size_t max_length) {
|
||||
// Reads bytes from the start of |data_ptr_|. Maps "\\" to "\", and maps "\"
|
||||
// followed by anything else to the end of the string. As a result of this
|
||||
// logic, a fuzzer can insert characters into the string, and the string
|
||||
// will be lengthened to include those new characters, resulting in a more
|
||||
// stable fuzzer than picking the length of a string independently from
|
||||
// picking its contents.
|
||||
std::string result;
|
||||
|
||||
// Reserve the anticipated capacity to prevent several reallocations.
|
||||
result.reserve(std::min(max_length, remaining_bytes_));
|
||||
for (size_t i = 0; i < max_length && remaining_bytes_ != 0; ++i) {
|
||||
char next = ConvertUnsignedToSigned<char>(data_ptr_[0]);
|
||||
Advance(1);
|
||||
if (next == '\\' && remaining_bytes_ != 0) {
|
||||
next = ConvertUnsignedToSigned<char>(data_ptr_[0]);
|
||||
Advance(1);
|
||||
if (next != '\\')
|
||||
break;
|
||||
}
|
||||
result += next;
|
||||
}
|
||||
|
||||
result.shrink_to_fit();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns a std::string of length from 0 to |remaining_bytes_|.
|
||||
inline std::string FuzzedDataProvider::ConsumeRandomLengthString() {
|
||||
return ConsumeRandomLengthString(remaining_bytes_);
|
||||
}
|
||||
|
||||
// Returns a std::string containing all remaining bytes of the input data.
|
||||
// Prefer using |ConsumeRemainingBytes| unless you actually need a std::string
|
||||
// object.
|
||||
inline std::string FuzzedDataProvider::ConsumeRemainingBytesAsString() {
|
||||
return ConsumeBytesAsString(remaining_bytes_);
|
||||
}
|
||||
|
||||
// Returns a number in the range [Type's min, Type's max]. The value might
|
||||
// not be uniformly distributed in the given range. If there's no input data
|
||||
// left, always returns |min|.
|
||||
template <typename T> T FuzzedDataProvider::ConsumeIntegral() {
|
||||
return ConsumeIntegralInRange(std::numeric_limits<T>::min(),
|
||||
std::numeric_limits<T>::max());
|
||||
}
|
||||
|
||||
// Returns a number in the range [min, max] by consuming bytes from the
|
||||
// input data. The value might not be uniformly distributed in the given
|
||||
// range. If there's no input data left, always returns |min|. |min| must
|
||||
// be less than or equal to |max|.
|
||||
template <typename T>
|
||||
T FuzzedDataProvider::ConsumeIntegralInRange(T min, T max) {
|
||||
static_assert(std::is_integral_v<T>, "An integral type is required.");
|
||||
static_assert(sizeof(T) <= sizeof(uint64_t), "Unsupported integral type.");
|
||||
|
||||
if (min > max)
|
||||
abort();
|
||||
|
||||
// Use the biggest type possible to hold the range and the result.
|
||||
uint64_t range = static_cast<uint64_t>(max) - static_cast<uint64_t>(min);
|
||||
uint64_t result = 0;
|
||||
size_t offset = 0;
|
||||
|
||||
while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0 &&
|
||||
remaining_bytes_ != 0) {
|
||||
// Pull bytes off the end of the seed data. Experimentally, this seems to
|
||||
// allow the fuzzer to more easily explore the input space. This makes
|
||||
// sense, since it works by modifying inputs that caused new code to run,
|
||||
// and this data is often used to encode length of data read by
|
||||
// |ConsumeBytes|. Separating out read lengths makes it easier modify the
|
||||
// contents of the data that is actually read.
|
||||
--remaining_bytes_;
|
||||
result = (result << CHAR_BIT) | data_ptr_[remaining_bytes_];
|
||||
offset += CHAR_BIT;
|
||||
}
|
||||
|
||||
// Avoid division by 0, in case |range + 1| results in overflow.
|
||||
if (range != std::numeric_limits<decltype(range)>::max())
|
||||
result = result % (range + 1);
|
||||
|
||||
return static_cast<T>(static_cast<uint64_t>(min) + result);
|
||||
}
|
||||
|
||||
// Returns a floating point value in the range [Type's lowest, Type's max] by
|
||||
// consuming bytes from the input data. If there's no input data left, always
|
||||
// returns approximately 0.
|
||||
template <typename T> T FuzzedDataProvider::ConsumeFloatingPoint() {
|
||||
return ConsumeFloatingPointInRange<T>(std::numeric_limits<T>::lowest(),
|
||||
std::numeric_limits<T>::max());
|
||||
}
|
||||
|
||||
// Returns a floating point value in the given range by consuming bytes from
|
||||
// the input data. If there's no input data left, returns |min|. Note that
|
||||
// |min| must be less than or equal to |max|.
|
||||
template <typename T>
|
||||
T FuzzedDataProvider::ConsumeFloatingPointInRange(T min, T max) {
|
||||
if (min > max)
|
||||
abort();
|
||||
|
||||
T range = .0;
|
||||
T result = min;
|
||||
constexpr T zero(.0);
|
||||
if (max > zero && min < zero && max > min + std::numeric_limits<T>::max()) {
|
||||
// The diff |max - min| would overflow the given floating point type. Use
|
||||
// the half of the diff as the range and consume a bool to decide whether
|
||||
// the result is in the first of the second part of the diff.
|
||||
range = (max / 2.0) - (min / 2.0);
|
||||
if (ConsumeBool()) {
|
||||
result += range;
|
||||
}
|
||||
} else {
|
||||
range = max - min;
|
||||
}
|
||||
|
||||
return result + range * ConsumeProbability<T>();
|
||||
}
|
||||
|
||||
// Returns a floating point number in the range [0.0, 1.0]. If there's no
|
||||
// input data left, always returns 0.
|
||||
template <typename T> T FuzzedDataProvider::ConsumeProbability() {
|
||||
static_assert(std::is_floating_point_v<T>,
|
||||
"A floating point type is required.");
|
||||
|
||||
// Use different integral types for different floating point types in order
|
||||
// to provide better density of the resulting values.
|
||||
using IntegralType =
|
||||
typename std::conditional_t<(sizeof(T) <= sizeof(uint32_t)), uint32_t,
|
||||
uint64_t>;
|
||||
|
||||
T result = static_cast<T>(ConsumeIntegral<IntegralType>());
|
||||
result /= static_cast<T>(std::numeric_limits<IntegralType>::max());
|
||||
return result;
|
||||
}
|
||||
|
||||
// Reads one byte and returns a bool, or false when no data remains.
|
||||
inline bool FuzzedDataProvider::ConsumeBool() {
|
||||
return 1 & ConsumeIntegral<uint8_t>();
|
||||
}
|
||||
|
||||
// Returns an enum value. The enum must start at 0 and be contiguous. It must
|
||||
// also contain |kMaxValue| aliased to its largest (inclusive) value. Such as:
|
||||
// enum class Foo { SomeValue, OtherValue, kMaxValue = OtherValue };
|
||||
template <typename T> T FuzzedDataProvider::ConsumeEnum() {
|
||||
static_assert(std::is_enum_v<T>, "|T| must be an enum type.");
|
||||
return static_cast<T>(
|
||||
ConsumeIntegralInRange<uint32_t>(0, static_cast<uint32_t>(T::kMaxValue)));
|
||||
}
|
||||
|
||||
// Returns a copy of the value selected from the given fixed-size |array|.
|
||||
template <typename T, size_t size>
|
||||
T FuzzedDataProvider::PickValueInArray(const T (&array)[size]) {
|
||||
static_assert(size > 0, "The array must be non empty.");
|
||||
return array[ConsumeIntegralInRange<size_t>(0, size - 1)];
|
||||
}
|
||||
|
||||
template <typename T, size_t size>
|
||||
T FuzzedDataProvider::PickValueInArray(const std::array<T, size> &array) {
|
||||
static_assert(size > 0, "The array must be non empty.");
|
||||
return array[ConsumeIntegralInRange<size_t>(0, size - 1)];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T FuzzedDataProvider::PickValueInArray(std::initializer_list<const T> list) {
|
||||
if (!list.size())
|
||||
abort();
|
||||
|
||||
return *(list.begin() + ConsumeIntegralInRange<size_t>(0, list.size() - 1));
|
||||
}
|
||||
|
||||
// Writes |num_bytes| of input data to the given destination pointer. If there
|
||||
// is not enough data left, writes all remaining bytes. Return value is the
|
||||
// number of bytes written.
|
||||
// In general, it's better to avoid using this function, but it may be useful
|
||||
// in cases when it's necessary to fill a certain buffer or object with
|
||||
// fuzzing data.
|
||||
inline size_t FuzzedDataProvider::ConsumeData(void *destination,
|
||||
size_t num_bytes) {
|
||||
num_bytes = std::min(num_bytes, remaining_bytes_);
|
||||
CopyAndAdvance(destination, num_bytes);
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
// Private methods.
|
||||
inline void FuzzedDataProvider::CopyAndAdvance(void *destination,
|
||||
size_t num_bytes) {
|
||||
std::memcpy(destination, data_ptr_, num_bytes);
|
||||
Advance(num_bytes);
|
||||
}
|
||||
|
||||
inline void FuzzedDataProvider::Advance(size_t num_bytes) {
|
||||
if (num_bytes > remaining_bytes_)
|
||||
abort();
|
||||
|
||||
data_ptr_ += num_bytes;
|
||||
remaining_bytes_ -= num_bytes;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> FuzzedDataProvider::ConsumeBytes(size_t size, size_t num_bytes) {
|
||||
static_assert(sizeof(T) == sizeof(uint8_t), "Incompatible data type.");
|
||||
|
||||
// The point of using the size-based constructor below is to increase the
|
||||
// odds of having a vector object with capacity being equal to the length.
|
||||
// That part is always implementation specific, but at least both libc++ and
|
||||
// libstdc++ allocate the requested number of bytes in that constructor,
|
||||
// which seems to be a natural choice for other implementations as well.
|
||||
// To increase the odds even more, we also call |shrink_to_fit| below.
|
||||
std::vector<T> result(size);
|
||||
if (size == 0) {
|
||||
if (num_bytes != 0)
|
||||
abort();
|
||||
return result;
|
||||
}
|
||||
|
||||
CopyAndAdvance(result.data(), num_bytes);
|
||||
|
||||
// Even though |shrink_to_fit| is also implementation specific, we expect it
|
||||
// to provide an additional assurance in case vector's constructor allocated
|
||||
// a buffer which is larger than the actual amount of data we put inside it.
|
||||
result.shrink_to_fit();
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename TS, typename TU>
|
||||
TS FuzzedDataProvider::ConvertUnsignedToSigned(TU value) {
|
||||
static_assert(sizeof(TS) == sizeof(TU), "Incompatible data types.");
|
||||
static_assert(!std::numeric_limits<TU>::is_signed,
|
||||
"Source type must be unsigned.");
|
||||
|
||||
// TODO(Dor1s): change to `if constexpr` once C++17 becomes mainstream.
|
||||
if (std::numeric_limits<TS>::is_modulo)
|
||||
return static_cast<TS>(value);
|
||||
|
||||
// Avoid using implementation-defined unsigned to signed conversions.
|
||||
// To learn more, see https://stackoverflow.com/questions/13150449.
|
||||
if (value <= std::numeric_limits<TS>::max()) {
|
||||
return static_cast<TS>(value);
|
||||
} else {
|
||||
constexpr auto TS_min = std::numeric_limits<TS>::min();
|
||||
return TS_min + static_cast<TS>(value - TS_min);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_
|
Reference in New Issue
Block a user