git / moonbite-core / src/test/fuzz/util.h · raw
// Copyright (c) 2009-2020 The Bitcoin Core developers// Distributed under the MIT software license, see the accompanying// file COPYING or http://www.opensource.org/licenses/mit-license.php.#ifndef BITCOIN_TEST_FUZZ_UTIL_H#define BITCOIN_TEST_FUZZ_UTIL_H#include <amount.h>#include <arith_uint256.h>#include <attributes.h>#include <chainparamsbase.h>#include <coins.h>#include <consensus/consensus.h>#include <merkleblock.h>#include <net.h>#include <netaddress.h>#include <netbase.h>#include <primitives/transaction.h>#include <script/script.h>#include <script/standard.h>#include <serialize.h>#include <streams.h>#include <test/fuzz/FuzzedDataProvider.h>#include <test/fuzz/fuzz.h>#include <test/util/setup_common.h>#include <txmempool.h>#include <uint256.h>#include <util/time.h>#include <version.h>#include <algorithm>#include <cstdint>#include <cstdio>#include <optional>#include <string>#include <vector>NODISCARD inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept{ const std::string s = fuzzed_data_provider.ConsumeRandomLengthString(max_length); return {s.begin(), s.end()};}NODISCARD inline std::vector<bool> ConsumeRandomLengthBitVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept{ return BytesToBits(ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length));}NODISCARD inline CDataStream ConsumeDataStream(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept{ return {ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length), SER_NETWORK, INIT_PROTO_VERSION};}NODISCARD inline std::vector<std::string> ConsumeRandomLengthStringVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_vector_size = 16, const size_t max_string_length = 16) noexcept{ const size_t n_elements = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size); std::vector<std::string> r; for (size_t i = 0; i < n_elements; ++i) { r.push_back(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length)); } return r;}template <typename T>NODISCARD inline std::vector<T> ConsumeRandomLengthIntegralVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_vector_size = 16) noexcept{ const size_t n_elements = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size); std::vector<T> r; for (size_t i = 0; i < n_elements; ++i) { r.push_back(fuzzed_data_provider.ConsumeIntegral<T>()); } return r;}template <typename T>NODISCARD inline std::optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept{ const std::vector<uint8_t> buffer = ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length); CDataStream ds{buffer, SER_NETWORK, INIT_PROTO_VERSION}; T obj; try { ds >> obj; } catch (const std::ios_base::failure&) { return std::nullopt; } return obj;}NODISCARD inline opcodetype ConsumeOpcodeType(FuzzedDataProvider& fuzzed_data_provider) noexcept{ return static_cast<opcodetype>(fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, MAX_OPCODE));}NODISCARD inline CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider) noexcept{ return fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, MAX_MONEY);}NODISCARD inline int64_t ConsumeTime(FuzzedDataProvider& fuzzed_data_provider) noexcept{ static const int64_t time_min = ParseISO8601DateTime("1970-01-01T00:00:00Z"); static const int64_t time_max = ParseISO8601DateTime("9999-12-31T23:59:59Z"); return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(time_min, time_max);}NODISCARD inline CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider) noexcept{ const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider); return {b.begin(), b.end()};}NODISCARD inline CScriptNum ConsumeScriptNum(FuzzedDataProvider& fuzzed_data_provider) noexcept{ return CScriptNum{fuzzed_data_provider.ConsumeIntegral<int64_t>()};}NODISCARD inline uint160 ConsumeUInt160(FuzzedDataProvider& fuzzed_data_provider) noexcept{ const std::vector<uint8_t> v160 = fuzzed_data_provider.ConsumeBytes<uint8_t>(160 / 8); if (v160.size() != 160 / 8) { return {}; } return uint160{v160};}NODISCARD inline uint256 ConsumeUInt256(FuzzedDataProvider& fuzzed_data_provider) noexcept{ const std::vector<uint8_t> v256 = fuzzed_data_provider.ConsumeBytes<uint8_t>(256 / 8); if (v256.size() != 256 / 8) { return {}; } return uint256{v256};}NODISCARD inline arith_uint256 ConsumeArithUInt256(FuzzedDataProvider& fuzzed_data_provider) noexcept{ return UintToArith256(ConsumeUInt256(fuzzed_data_provider));}NODISCARD inline CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept{ // Avoid: // policy/feerate.cpp:28:34: runtime error: signed integer overflow: 34873208148477500 * 1000 cannot be represented in type 'long' // // Reproduce using CFeeRate(348732081484775, 10).GetFeePerK() const CAmount fee = std::min<CAmount>(ConsumeMoney(fuzzed_data_provider), std::numeric_limits<CAmount>::max() / static_cast<CAmount>(100000)); assert(MoneyRange(fee)); const int64_t time = fuzzed_data_provider.ConsumeIntegral<int64_t>(); const unsigned int entry_height = fuzzed_data_provider.ConsumeIntegral<unsigned int>(); const bool spends_coinbase = fuzzed_data_provider.ConsumeBool(); const unsigned int sig_op_cost = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, MAX_BLOCK_SIGOPS_COST); return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}};}NODISCARD inline CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept{ CTxDestination tx_destination; switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) { case 0: { tx_destination = CNoDestination{}; break; } case 1: { tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)}; break; } case 2: { tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)}; break; } case 3: { tx_destination = WitnessV0ScriptHash{ConsumeUInt256(fuzzed_data_provider)}; break; } case 4: { tx_destination = WitnessV0KeyHash{ConsumeUInt160(fuzzed_data_provider)}; break; } case 5: { WitnessUnknown witness_unknown{}; witness_unknown.version = fuzzed_data_provider.ConsumeIntegral<int>(); const std::vector<uint8_t> witness_unknown_program_1 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40); witness_unknown.length = witness_unknown_program_1.size(); std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown.program); tx_destination = witness_unknown; break; } } return tx_destination;}template <typename T>NODISCARD bool MultiplicationOverflow(const T i, const T j) noexcept{ static_assert(std::is_integral<T>::value, "Integral required."); if (std::numeric_limits<T>::is_signed) { if (i > 0) { if (j > 0) { return i > (std::numeric_limits<T>::max() / j); } else { return j < (std::numeric_limits<T>::min() / i); } } else { if (j > 0) { return i < (std::numeric_limits<T>::min() / j); } else { return i != 0 && (j < (std::numeric_limits<T>::max() / i)); } } } else { return j != 0 && i > std::numeric_limits<T>::max() / j; }}template <class T>NODISCARD bool AdditionOverflow(const T i, const T j) noexcept{ static_assert(std::is_integral<T>::value, "Integral required."); if (std::numeric_limits<T>::is_signed) { return (i > 0 && j > std::numeric_limits<T>::max() - i) || (i < 0 && j < std::numeric_limits<T>::min() - i); } return std::numeric_limits<T>::max() - i < j;}NODISCARD inline bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept{ for (const CTxIn& tx_in : tx.vin) { const Coin& coin = inputs.AccessCoin(tx_in.prevout); if (coin.IsSpent()) { return true; } } return false;}/** * Returns a byte vector of specified size regardless of the number of remaining bytes available * from the fuzzer. Pads with zero value bytes if needed to achieve the specified size. */NODISCARD inline std::vector<uint8_t> ConsumeFixedLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t length) noexcept{ std::vector<uint8_t> result(length); const std::vector<uint8_t> random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(length); if (!random_bytes.empty()) { std::memcpy(result.data(), random_bytes.data(), random_bytes.size()); } return result;}CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept{ const Network network = fuzzed_data_provider.PickValueInArray({Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL, Network::NET_ONION}); CNetAddr net_addr; if (network == Network::NET_IPV4) { const in_addr v4_addr = { .s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>()}; net_addr = CNetAddr{v4_addr}; } else if (network == Network::NET_IPV6) { if (fuzzed_data_provider.remaining_bytes() >= 16) { in6_addr v6_addr = {}; memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16); net_addr = CNetAddr{v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()}; } } else if (network == Network::NET_INTERNAL) { net_addr.SetInternal(fuzzed_data_provider.ConsumeBytesAsString(32)); } else if (network == Network::NET_ONION) { net_addr.SetSpecial(fuzzed_data_provider.ConsumeBytesAsString(32)); } return net_addr;}CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept{ return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint8_t>()};}CService ConsumeService(FuzzedDataProvider& fuzzed_data_provider) noexcept{ return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint16_t>()};}CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept{ return {ConsumeService(fuzzed_data_provider), static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()), fuzzed_data_provider.ConsumeIntegral<uint32_t>()};}CNode ConsumeNode(FuzzedDataProvider& fuzzed_data_provider) noexcept{ const NodeId node_id = fuzzed_data_provider.ConsumeIntegral<NodeId>(); const ServiceFlags local_services = static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); const int my_starting_height = fuzzed_data_provider.ConsumeIntegral<int>(); const SOCKET socket = INVALID_SOCKET; const CAddress address = ConsumeAddress(fuzzed_data_provider); const uint64_t keyed_net_group = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); const uint64_t local_host_nonce = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); const CAddress addr_bind = ConsumeAddress(fuzzed_data_provider); const std::string addr_name = fuzzed_data_provider.ConsumeRandomLengthString(64); const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH}); const bool inbound_onion = fuzzed_data_provider.ConsumeBool(); return {node_id, local_services, my_starting_height, socket, address, keyed_net_group, local_host_nonce, addr_bind, addr_name, conn_type, inbound_onion};}void InitializeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST){ static const BasicTestingSetup basic_testing_setup{chain_name, {"-nodebuglogfile"}};}class FuzzedFileProvider{ FuzzedDataProvider& m_fuzzed_data_provider; int64_t m_offset = 0;public: FuzzedFileProvider(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider{fuzzed_data_provider} { } FILE* open() { if (m_fuzzed_data_provider.ConsumeBool()) { return nullptr; } std::string mode; switch (m_fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) { case 0: { mode = "r"; break; } case 1: { mode = "r+"; break; } case 2: { mode = "w"; break; } case 3: { mode = "w+"; break; } case 4: { mode = "a"; break; } case 5: { mode = "a+"; break; } }#ifdef _GNU_SOURCE const cookie_io_functions_t io_hooks = { FuzzedFileProvider::read, FuzzedFileProvider::write, FuzzedFileProvider::seek, FuzzedFileProvider::close, }; return fopencookie(this, mode.c_str(), io_hooks);#else (void)mode; return nullptr;#endif } static ssize_t read(void* cookie, char* buf, size_t size) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) { return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; } const std::vector<uint8_t> random_bytes = fuzzed_file->m_fuzzed_data_provider.ConsumeBytes<uint8_t>(size); if (random_bytes.empty()) { return 0; } std::memcpy(buf, random_bytes.data(), random_bytes.size()); if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)random_bytes.size())) { return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; } fuzzed_file->m_offset += random_bytes.size(); return random_bytes.size(); } static ssize_t write(void* cookie, const char* buf, size_t size) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(0, size); if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)n)) { return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; } fuzzed_file->m_offset += n; return n; } static int seek(void* cookie, int64_t* offset, int whence) { assert(whence == SEEK_SET || whence == SEEK_CUR); // SEEK_END not implemented yet. FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; int64_t new_offset = 0; if (whence == SEEK_SET) { new_offset = *offset; } else if (whence == SEEK_CUR) { if (AdditionOverflow(fuzzed_file->m_offset, *offset)) { return -1; } new_offset = fuzzed_file->m_offset + *offset; } if (new_offset < 0) { return -1; } fuzzed_file->m_offset = new_offset; *offset = new_offset; return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0); } static int close(void* cookie) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0); }};NODISCARD inline FuzzedFileProvider ConsumeFile(FuzzedDataProvider& fuzzed_data_provider) noexcept{ return {fuzzed_data_provider};}class FuzzedAutoFileProvider{ FuzzedDataProvider& m_fuzzed_data_provider; FuzzedFileProvider m_fuzzed_file_provider;public: FuzzedAutoFileProvider(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider{fuzzed_data_provider}, m_fuzzed_file_provider{fuzzed_data_provider} { } CAutoFile open() { return {m_fuzzed_file_provider.open(), m_fuzzed_data_provider.ConsumeIntegral<int>(), m_fuzzed_data_provider.ConsumeIntegral<int>()}; }};NODISCARD inline FuzzedAutoFileProvider ConsumeAutoFile(FuzzedDataProvider& fuzzed_data_provider) noexcept{ return {fuzzed_data_provider};}#define WRITE_TO_STREAM_CASE(id, type, consume) \ case id: { \ type o = consume; \ stream << o; \ break; \ }template <typename Stream>void WriteToStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noexcept{ while (fuzzed_data_provider.ConsumeBool()) { try { switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 13)) { WRITE_TO_STREAM_CASE(0, bool, fuzzed_data_provider.ConsumeBool()) WRITE_TO_STREAM_CASE(1, char, fuzzed_data_provider.ConsumeIntegral<char>()) WRITE_TO_STREAM_CASE(2, int8_t, fuzzed_data_provider.ConsumeIntegral<int8_t>()) WRITE_TO_STREAM_CASE(3, uint8_t, fuzzed_data_provider.ConsumeIntegral<uint8_t>()) WRITE_TO_STREAM_CASE(4, int16_t, fuzzed_data_provider.ConsumeIntegral<int16_t>()) WRITE_TO_STREAM_CASE(5, uint16_t, fuzzed_data_provider.ConsumeIntegral<uint16_t>()) WRITE_TO_STREAM_CASE(6, int32_t, fuzzed_data_provider.ConsumeIntegral<int32_t>()) WRITE_TO_STREAM_CASE(7, uint32_t, fuzzed_data_provider.ConsumeIntegral<uint32_t>()) WRITE_TO_STREAM_CASE(8, int64_t, fuzzed_data_provider.ConsumeIntegral<int64_t>()) WRITE_TO_STREAM_CASE(9, uint64_t, fuzzed_data_provider.ConsumeIntegral<uint64_t>()) WRITE_TO_STREAM_CASE(10, float, fuzzed_data_provider.ConsumeFloatingPoint<float>()) WRITE_TO_STREAM_CASE(11, double, fuzzed_data_provider.ConsumeFloatingPoint<double>()) WRITE_TO_STREAM_CASE(12, std::string, fuzzed_data_provider.ConsumeRandomLengthString(32)) WRITE_TO_STREAM_CASE(13, std::vector<char>, ConsumeRandomLengthIntegralVector<char>(fuzzed_data_provider)) } } catch (const std::ios_base::failure&) { break; } }}#define READ_FROM_STREAM_CASE(id, type) \ case id: { \ type o; \ stream >> o; \ break; \ }template <typename Stream>void ReadFromStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noexcept{ while (fuzzed_data_provider.ConsumeBool()) { try { switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 13)) { READ_FROM_STREAM_CASE(0, bool) READ_FROM_STREAM_CASE(1, char) READ_FROM_STREAM_CASE(2, int8_t) READ_FROM_STREAM_CASE(3, uint8_t) READ_FROM_STREAM_CASE(4, int16_t) READ_FROM_STREAM_CASE(5, uint16_t) READ_FROM_STREAM_CASE(6, int32_t) READ_FROM_STREAM_CASE(7, uint32_t) READ_FROM_STREAM_CASE(8, int64_t) READ_FROM_STREAM_CASE(9, uint64_t) READ_FROM_STREAM_CASE(10, float) READ_FROM_STREAM_CASE(11, double) READ_FROM_STREAM_CASE(12, std::string) READ_FROM_STREAM_CASE(13, std::vector<char>) } } catch (const std::ios_base::failure&) { break; } }}#endif // BITCOIN_TEST_FUZZ_UTIL_H