diff options
Diffstat (limited to 'src/test/fuzz')
-rw-r--r-- | src/test/fuzz/fuzz.cpp | 72 | ||||
-rw-r--r-- | src/test/fuzz/http_request.cpp | 15 | ||||
-rw-r--r-- | src/test/fuzz/miniscript_decode.cpp | 72 | ||||
-rw-r--r-- | src/test/fuzz/net.cpp | 10 | ||||
-rw-r--r-- | src/test/fuzz/node_eviction.cpp | 2 | ||||
-rw-r--r-- | src/test/fuzz/script_format.cpp | 4 | ||||
-rw-r--r-- | src/test/fuzz/transaction.cpp | 4 | ||||
-rw-r--r-- | src/test/fuzz/tx_pool.cpp | 14 | ||||
-rw-r--r-- | src/test/fuzz/util.cpp | 11 |
9 files changed, 157 insertions, 47 deletions
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp index a490bbfa1d..59adec075e 100644 --- a/src/test/fuzz/fuzz.cpp +++ b/src/test/fuzz/fuzz.cpp @@ -10,7 +10,9 @@ #include <test/util/setup_common.h> #include <util/check.h> #include <util/sock.h> +#include <util/time.h> +#include <csignal> #include <cstdint> #include <exception> #include <fstream> @@ -59,6 +61,7 @@ void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, Assert(it_ins.second); } +static std::string_view g_fuzz_target; static TypeTestOneInput* g_test_one_input{nullptr}; void initialize() @@ -92,9 +95,12 @@ void initialize() should_abort = true; } Assert(!should_abort); - std::string_view fuzz_target{Assert(std::getenv("FUZZ"))}; - const auto it = FuzzTargets().find(fuzz_target); - Assert(it != FuzzTargets().end()); + g_fuzz_target = Assert(std::getenv("FUZZ")); + const auto it = FuzzTargets().find(g_fuzz_target); + if (it == FuzzTargets().end()) { + std::cerr << "No fuzzer for " << g_fuzz_target << "." << std::endl; + std::exit(EXIT_FAILURE); + } Assert(!g_test_one_input); g_test_one_input = &std::get<0>(it->second); std::get<1>(it->second)(); @@ -112,6 +118,35 @@ static bool read_stdin(std::vector<uint8_t>& data) } #endif +#if defined(PROVIDE_FUZZ_MAIN_FUNCTION) && !defined(__AFL_LOOP) +static bool read_file(fs::path p, std::vector<uint8_t>& data) +{ + uint8_t buffer[1024]; + FILE* f = fsbridge::fopen(p, "rb"); + if (f == nullptr) return false; + do { + const size_t length = fread(buffer, sizeof(uint8_t), sizeof(buffer), f); + if (ferror(f)) return false; + data.insert(data.end(), buffer, buffer + length); + } while (!feof(f)); + fclose(f); + return true; +} +#endif + +#if defined(PROVIDE_FUZZ_MAIN_FUNCTION) && !defined(__AFL_LOOP) +static fs::path g_input_path; +void signal_handler(int signal) +{ + if (signal == SIGABRT) { + std::cerr << "Error processing input " << g_input_path << std::endl; + } else { + std::cerr << "Unexpected signal " << signal << " received\n"; + } + std::_Exit(EXIT_FAILURE); +} +#endif + // This function is used by libFuzzer extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { @@ -151,10 +186,37 @@ int main(int argc, char** argv) } #else std::vector<uint8_t> buffer; - if (!read_stdin(buffer)) { + if (argc <= 1) { + if (!read_stdin(buffer)) { + return 0; + } + test_one_input(buffer); return 0; } - test_one_input(buffer); + std::signal(SIGABRT, signal_handler); + int64_t start_time = GetTimeSeconds(); + int tested = 0; + for (int i = 1; i < argc; ++i) { + fs::path input_path(*(argv + i)); + if (fs::is_directory(input_path)) { + for (fs::directory_iterator it(input_path); it != fs::directory_iterator(); ++it) { + if (!fs::is_regular_file(it->path())) continue; + g_input_path = it->path(); + Assert(read_file(it->path(), buffer)); + test_one_input(buffer); + ++tested; + buffer.clear(); + } + } else { + g_input_path = input_path; + Assert(read_file(input_path, buffer)); + test_one_input(buffer); + ++tested; + buffer.clear(); + } + } + int64_t end_time = GetTimeSeconds(); + std::cout << g_fuzz_target << ": succeeded against " << tested << " files in " << (end_time - start_time) << "s." << std::endl; #endif return 0; } diff --git a/src/test/fuzz/http_request.cpp b/src/test/fuzz/http_request.cpp index e3b62032bc..916e90e986 100644 --- a/src/test/fuzz/http_request.cpp +++ b/src/test/fuzz/http_request.cpp @@ -19,23 +19,8 @@ #include <string> #include <vector> -// workaround for libevent versions before 2.1.1, -// when internal functions didn't have underscores at the end -#if LIBEVENT_VERSION_NUMBER < 0x02010100 -extern "C" int evhttp_parse_firstline(struct evhttp_request*, struct evbuffer*); -extern "C" int evhttp_parse_headers(struct evhttp_request*, struct evbuffer*); -inline int evhttp_parse_firstline_(struct evhttp_request* r, struct evbuffer* b) -{ - return evhttp_parse_firstline(r, b); -} -inline int evhttp_parse_headers_(struct evhttp_request* r, struct evbuffer* b) -{ - return evhttp_parse_headers(r, b); -} -#else extern "C" int evhttp_parse_firstline_(struct evhttp_request*, struct evbuffer*); extern "C" int evhttp_parse_headers_(struct evhttp_request*, struct evbuffer*); -#endif std::string RequestMethodString(HTTPRequest::RequestMethod m); diff --git a/src/test/fuzz/miniscript_decode.cpp b/src/test/fuzz/miniscript_decode.cpp new file mode 100644 index 0000000000..4cc0a1be8f --- /dev/null +++ b/src/test/fuzz/miniscript_decode.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <core_io.h> +#include <hash.h> +#include <key.h> +#include <script/miniscript.h> +#include <script/script.h> +#include <span.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> +#include <util/strencodings.h> + +#include <optional> + +using miniscript::operator""_mst; + + +struct Converter { + typedef CPubKey Key; + + bool ToString(const Key& key, std::string& ret) const { + ret = HexStr(key); + return true; + } + const std::vector<unsigned char> ToPKBytes(const Key& key) const { + return {key.begin(), key.end()}; + } + const std::vector<unsigned char> ToPKHBytes(const Key& key) const { + const auto h = Hash160(key); + return {h.begin(), h.end()}; + } + + template<typename I> + bool FromString(I first, I last, Key& key) const { + const auto bytes = ParseHex(std::string(first, last)); + key.Set(bytes.begin(), bytes.end()); + return key.IsValid(); + } + template<typename I> + bool FromPKBytes(I first, I last, CPubKey& key) const { + key.Set(first, last); + return key.IsValid(); + } + template<typename I> + bool FromPKHBytes(I first, I last, CPubKey& key) const { + assert(last - first == 20); + return false; + } +}; + +const Converter CONVERTER; + +FUZZ_TARGET(miniscript_decode) +{ + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + const std::optional<CScript> script = ConsumeDeserializable<CScript>(fuzzed_data_provider); + if (!script) return; + + const auto ms = miniscript::FromScript(*script, CONVERTER); + if (!ms) return; + + // We can roundtrip it to its string representation. + std::string ms_str; + assert(ms->ToString(CONVERTER, ms_str)); + assert(*miniscript::FromString(ms_str, CONVERTER) == *ms); + // The Script representation must roundtrip since we parsed it this way the first time. + const CScript ms_script = ms->ToScript(CONVERTER); + assert(ms_script == *script); +} diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp index fb11ea36ce..4981287152 100644 --- a/src/test/fuzz/net.cpp +++ b/src/test/fuzz/net.cpp @@ -52,16 +52,6 @@ FUZZ_TARGET_INIT(net, initialize_net) } }, [&] { - const std::optional<CInv> inv_opt = ConsumeDeserializable<CInv>(fuzzed_data_provider); - if (!inv_opt) { - return; - } - node.AddKnownTx(inv_opt->hash); - }, - [&] { - node.PushTxInventory(ConsumeUInt256(fuzzed_data_provider)); - }, - [&] { const std::optional<CService> service_opt = ConsumeDeserializable<CService>(fuzzed_data_provider); if (!service_opt) { return; diff --git a/src/test/fuzz/node_eviction.cpp b/src/test/fuzz/node_eviction.cpp index 2e90085744..6a363f00f7 100644 --- a/src/test/fuzz/node_eviction.cpp +++ b/src/test/fuzz/node_eviction.cpp @@ -26,7 +26,7 @@ FUZZ_TARGET(node_eviction) /*m_last_block_time=*/std::chrono::seconds{fuzzed_data_provider.ConsumeIntegral<int64_t>()}, /*m_last_tx_time=*/std::chrono::seconds{fuzzed_data_provider.ConsumeIntegral<int64_t>()}, /*fRelevantServices=*/fuzzed_data_provider.ConsumeBool(), - /*fRelayTxes=*/fuzzed_data_provider.ConsumeBool(), + /*m_relay_txs=*/fuzzed_data_provider.ConsumeBool(), /*fBloomFilter=*/fuzzed_data_provider.ConsumeBool(), /*nKeyedNetGroup=*/fuzzed_data_provider.ConsumeIntegral<uint64_t>(), /*prefer_evict=*/fuzzed_data_provider.ConsumeBool(), diff --git a/src/test/fuzz/script_format.cpp b/src/test/fuzz/script_format.cpp index 241bdfe666..9186746bcf 100644 --- a/src/test/fuzz/script_format.cpp +++ b/src/test/fuzz/script_format.cpp @@ -29,7 +29,5 @@ FUZZ_TARGET_INIT(script_format, initialize_script_format) (void)ScriptToAsmStr(script, /*fAttemptSighashDecode=*/fuzzed_data_provider.ConsumeBool()); UniValue o1(UniValue::VOBJ); - ScriptPubKeyToUniv(script, o1, /*include_hex=*/fuzzed_data_provider.ConsumeBool()); - UniValue o3(UniValue::VOBJ); - ScriptToUniv(script, o3); + ScriptToUniv(script, /*out=*/o1, /*include_hex=*/fuzzed_data_provider.ConsumeBool(), /*include_address=*/fuzzed_data_provider.ConsumeBool()); } diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp index 6dd8a36692..273aa0dc5c 100644 --- a/src/test/fuzz/transaction.cpp +++ b/src/test/fuzz/transaction.cpp @@ -102,6 +102,6 @@ FUZZ_TARGET_INIT(transaction, initialize_transaction) (void)IsWitnessStandard(tx, coins_view_cache); UniValue u(UniValue::VOBJ); - TxToUniv(tx, /*hashBlock=*/uint256::ZERO, u); - TxToUniv(tx, /*hashBlock=*/uint256::ONE, u); + TxToUniv(tx, /*block_hash=*/uint256::ZERO, /*entry=*/u); + TxToUniv(tx, /*block_hash=*/uint256::ONE, /*entry=*/u); } diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp index df5b271d06..f686f4fd86 100644 --- a/src/test/fuzz/tx_pool.cpp +++ b/src/test/fuzz/tx_pool.cpp @@ -234,14 +234,18 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool) const bool bypass_limits = fuzzed_data_provider.ConsumeBool(); ::fRequireStandard = fuzzed_data_provider.ConsumeBool(); - // Make sure ProcessNewPackage on one transaction works and always fully validates the transaction. + // Make sure ProcessNewPackage on one transaction works. // The result is not guaranteed to be the same as what is returned by ATMP. const auto result_package = WITH_LOCK(::cs_main, return ProcessNewPackage(chainstate, tx_pool, {tx}, true)); - auto it = result_package.m_tx_results.find(tx->GetWitnessHash()); - Assert(it != result_package.m_tx_results.end()); - Assert(it->second.m_result_type == MempoolAcceptResult::ResultType::VALID || - it->second.m_result_type == MempoolAcceptResult::ResultType::INVALID); + // If something went wrong due to a package-specific policy, it might not return a + // validation result for the transaction. + if (result_package.m_state.GetResult() != PackageValidationResult::PCKG_POLICY) { + auto it = result_package.m_tx_results.find(tx->GetWitnessHash()); + Assert(it != result_package.m_tx_results.end()); + Assert(it->second.m_result_type == MempoolAcceptResult::ResultType::VALID || + it->second.m_result_type == MempoolAcceptResult::ResultType::INVALID); + } const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), bypass_limits, /*test_accept=*/false)); const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID; diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp index f0c1b0d147..6766fbf2d9 100644 --- a/src/test/fuzz/util.cpp +++ b/src/test/fuzz/util.cpp @@ -225,7 +225,7 @@ void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman, const ServiceFlags remote_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS); const NetPermissionFlags permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS); const int32_t version = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(MIN_PEER_PROTO_VERSION, std::numeric_limits<int32_t>::max()); - const bool filter_txs = fuzzed_data_provider.ConsumeBool(); + const bool relay_txs{fuzzed_data_provider.ConsumeBool()}; const CNetMsgMaker mm{0}; @@ -241,7 +241,7 @@ void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman, uint64_t{1}, // dummy nonce std::string{}, // dummy subver int32_t{}, // dummy starting_height - filter_txs), + relay_txs), }; (void)connman.ReceiveMsgFrom(node, msg_version); @@ -255,10 +255,9 @@ void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman, assert(node.nVersion == version); assert(node.GetCommonVersion() == std::min(version, PROTOCOL_VERSION)); assert(node.nServices == remote_services); - if (node.m_tx_relay != nullptr) { - LOCK(node.m_tx_relay->cs_filter); - assert(node.m_tx_relay->fRelayTxes == filter_txs); - } + CNodeStateStats statestats; + assert(peerman.GetNodeStateStats(node.GetId(), statestats)); + assert(statestats.m_relay_txs == (relay_txs && !node.IsBlockOnlyConn())); node.m_permissionFlags = permission_flags; if (successfully_connected) { CSerializedNetMsg msg_verack{mm.Make(NetMsgType::VERACK)}; |