diff options
Diffstat (limited to 'src/test/fuzz')
-rw-r--r-- | src/test/fuzz/addrman.cpp | 33 | ||||
-rw-r--r-- | src/test/fuzz/connman.cpp | 10 | ||||
-rw-r--r-- | src/test/fuzz/descriptor_parse.cpp | 149 | ||||
-rw-r--r-- | src/test/fuzz/load_external_block_file.cpp | 4 | ||||
-rw-r--r-- | src/test/fuzz/parse_univalue.cpp | 2 |
5 files changed, 163 insertions, 35 deletions
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp index 8ac1330dcb..02df4590de 100644 --- a/src/test/fuzz/addrman.cpp +++ b/src/test/fuzz/addrman.cpp @@ -263,40 +263,21 @@ FUZZ_TARGET(addrman, .init = initialize_addrman) [&] { std::vector<CAddress> addresses; LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { - const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider); - if (!opt_address) { - break; - } - addresses.push_back(*opt_address); - } - const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider); - if (opt_net_addr) { - addr_man.Add(addresses, *opt_net_addr, std::chrono::seconds{ConsumeTime(fuzzed_data_provider, 0, 100000000)}); + addresses.push_back(ConsumeAddress(fuzzed_data_provider)); } + addr_man.Add(addresses, ConsumeNetAddr(fuzzed_data_provider), std::chrono::seconds{ConsumeTime(fuzzed_data_provider, 0, 100000000)}); }, [&] { - const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); - if (opt_service) { - addr_man.Good(*opt_service, NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); - } + addr_man.Good(ConsumeService(fuzzed_data_provider), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); }, [&] { - const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); - if (opt_service) { - addr_man.Attempt(*opt_service, fuzzed_data_provider.ConsumeBool(), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); - } + addr_man.Attempt(ConsumeService(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool(), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); }, [&] { - const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); - if (opt_service) { - addr_man.Connected(*opt_service, NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); - } + addr_man.Connected(ConsumeService(fuzzed_data_provider), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); }, [&] { - const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); - if (opt_service) { - addr_man.SetServices(*opt_service, ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS)); - } + addr_man.SetServices(ConsumeService(fuzzed_data_provider), ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS)); }); } const AddrMan& const_addr_man{addr_man}; @@ -334,4 +315,4 @@ FUZZ_TARGET(addrman_serdeser, .init = initialize_addrman) data_stream << addr_man1; data_stream >> addr_man2; assert(addr_man1 == addr_man2); -} +}
\ No newline at end of file diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp index 23fadd8984..cdf240dc59 100644 --- a/src/test/fuzz/connman.cpp +++ b/src/test/fuzz/connman.cpp @@ -32,7 +32,7 @@ FUZZ_TARGET(connman, .init = initialize_connman) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; SetMockTime(ConsumeTime(fuzzed_data_provider)); - CConnman connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), + ConnmanTestMsg connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>(), *g_setup->m_node.addrman, *g_setup->m_node.netgroupman, @@ -41,6 +41,12 @@ FUZZ_TARGET(connman, .init = initialize_connman) CNode random_node = ConsumeNode(fuzzed_data_provider); CSubNet random_subnet; std::string random_string; + + LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) { + CNode& p2p_node{*ConsumeNodeAsUniquePtr(fuzzed_data_provider).release()}; + connman.AddTestNode(p2p_node); + } + LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { CallOneOf( fuzzed_data_provider, @@ -128,4 +134,6 @@ FUZZ_TARGET(connman, .init = initialize_connman) (void)connman.GetTotalBytesSent(); (void)connman.GetTryNewOutboundPeer(); (void)connman.GetUseAddrmanOutgoing(); + + connman.ClearTestNodes(); } diff --git a/src/test/fuzz/descriptor_parse.cpp b/src/test/fuzz/descriptor_parse.cpp index 579942c3b5..8ed659323c 100644 --- a/src/test/fuzz/descriptor_parse.cpp +++ b/src/test/fuzz/descriptor_parse.cpp @@ -3,17 +3,160 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <chainparams.h> +#include <key_io.h> #include <pubkey.h> #include <script/descriptor.h> #include <test/fuzz/fuzz.h> #include <util/chaintype.h> +//! Types are raw (un)compressed pubkeys, raw xonly pubkeys, raw privkeys (WIF), xpubs, xprvs. +static constexpr uint8_t KEY_TYPES_COUNT{6}; +//! How many keys we'll generate in total. +static constexpr size_t TOTAL_KEYS_GENERATED{std::numeric_limits<uint8_t>::max() + 1}; + +/** + * Converts a mocked descriptor string to a valid one. Every key in a mocked descriptor key is + * represented by 2 hex characters preceded by the '%' character. We parse the two hex characters + * as an index in a list of pre-generated keys. This list contains keys of the various types + * accepted in descriptor keys expressions. + */ +class MockedDescriptorConverter { + //! 256 keys of various types. + std::array<std::string, TOTAL_KEYS_GENERATED> keys_str; + +public: + // We derive the type of key to generate from the 1-byte id parsed from hex. + bool IdIsCompPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 0; } + bool IdIsUnCompPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 1; } + bool IdIsXOnlyPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 2; } + bool IdIsConstPrivKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 3; } + bool IdIsXpub(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 4; } + bool IdIsXprv(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 5; } + + //! When initializing the target, populate the list of keys. + void Init() { + // The data to use as a private key or a seed for an xprv. + std::array<std::byte, 32> key_data{std::byte{1}}; + // Generate keys of all kinds and store them in the keys array. + for (size_t i{0}; i < TOTAL_KEYS_GENERATED; i++) { + key_data[31] = std::byte(i); + + // If this is a "raw" key, generate a normal privkey. Otherwise generate + // an extended one. + if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i) || IdIsXOnlyPubKey(i) || IdIsConstPrivKey(i)) { + CKey privkey; + privkey.Set(UCharCast(key_data.begin()), UCharCast(key_data.end()), !IdIsUnCompPubKey(i)); + if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i)) { + CPubKey pubkey{privkey.GetPubKey()}; + keys_str[i] = HexStr(pubkey); + } else if (IdIsXOnlyPubKey(i)) { + const XOnlyPubKey pubkey{privkey.GetPubKey()}; + keys_str[i] = HexStr(pubkey); + } else { + keys_str[i] = EncodeSecret(privkey); + } + } else { + CExtKey ext_privkey; + ext_privkey.SetSeed(key_data); + if (IdIsXprv(i)) { + keys_str[i] = EncodeExtKey(ext_privkey); + } else { + const CExtPubKey ext_pubkey{ext_privkey.Neuter()}; + keys_str[i] = EncodeExtPubKey(ext_pubkey); + } + } + } + } + + //! Parse an id in the keys vectors from a 2-characters hex string. + std::optional<uint8_t> IdxFromHex(std::string_view hex_characters) const { + if (hex_characters.size() != 2) return {}; + auto idx = ParseHex(hex_characters); + if (idx.size() != 1) return {}; + return idx[0]; + } + + //! Get an actual descriptor string from a descriptor string whose keys were mocked. + std::optional<std::string> GetDescriptor(std::string_view mocked_desc) const { + // The smallest fragment would be "pk(%00)" + if (mocked_desc.size() < 7) return {}; + + // The actual descriptor string to be returned. + std::string desc; + desc.reserve(mocked_desc.size()); + + // Replace all occurrences of '%' followed by two hex characters with the corresponding key. + for (size_t i = 0; i < mocked_desc.size();) { + if (mocked_desc[i] == '%') { + if (i + 3 >= mocked_desc.size()) return {}; + if (const auto idx = IdxFromHex(mocked_desc.substr(i + 1, 2))) { + desc += keys_str[*idx]; + i += 3; + } else { + return {}; + } + } else { + desc += mocked_desc[i++]; + } + } + + return desc; + } +}; + +//! The converter of mocked descriptors, needs to be initialized when the target is. +MockedDescriptorConverter MOCKED_DESC_CONVERTER; + +/** Test a successfully parsed descriptor. */ +static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_provider, std::string& dummy) +{ + // Trivial helpers. + (void)desc.IsRange(); + (void)desc.IsSolvable(); + (void)desc.IsSingleType(); + (void)desc.GetOutputType(); + + // Serialization to string representation. + (void)desc.ToString(); + (void)desc.ToPrivateString(sig_provider, dummy); + (void)desc.ToNormalizedString(sig_provider, dummy); + + // Serialization to Script. + DescriptorCache cache; + std::vector<CScript> out_scripts; + (void)desc.Expand(0, sig_provider, out_scripts, sig_provider, &cache); + (void)desc.ExpandPrivate(0, sig_provider, sig_provider); + (void)desc.ExpandFromCache(0, cache, out_scripts, sig_provider); + + // If we could serialize to script we must be able to infer using the same provider. + if (!out_scripts.empty()) { + assert(InferDescriptor(out_scripts.back(), sig_provider)); + } +} + void initialize_descriptor_parse() { ECC_Start(); SelectParams(ChainType::MAIN); } +void initialize_mocked_descriptor_parse() +{ + initialize_descriptor_parse(); + MOCKED_DESC_CONVERTER.Init(); +} + +FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse) +{ + const std::string mocked_descriptor{buffer.begin(), buffer.end()}; + if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) { + FlatSigningProvider signing_provider; + std::string error; + const auto desc = Parse(*descriptor, signing_provider, error); + if (desc) TestDescriptor(*desc, signing_provider, error); + } +} + FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse) { const std::string descriptor(buffer.begin(), buffer.end()); @@ -21,10 +164,6 @@ FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse) std::string error; for (const bool require_checksum : {true, false}) { const auto desc = Parse(descriptor, signing_provider, error, require_checksum); - if (desc) { - (void)desc->ToString(); - (void)desc->IsRange(); - (void)desc->IsSolvable(); - } + if (desc) TestDescriptor(*desc, signing_provider, error); } } diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp index 502f7b897c..bdaa4ad1b8 100644 --- a/src/test/fuzz/load_external_block_file.cpp +++ b/src/test/fuzz/load_external_block_file.cpp @@ -35,9 +35,9 @@ FUZZ_TARGET(load_external_block_file, .init = initialize_load_external_block_fil // Corresponds to the -reindex case (track orphan blocks across files). FlatFilePos flat_file_pos; std::multimap<uint256, FlatFilePos> blocks_with_unknown_parent; - g_setup->m_node.chainman->ActiveChainstate().LoadExternalBlockFile(fuzzed_block_file, &flat_file_pos, &blocks_with_unknown_parent); + g_setup->m_node.chainman->LoadExternalBlockFile(fuzzed_block_file, &flat_file_pos, &blocks_with_unknown_parent); } else { // Corresponds to the -loadblock= case (orphan blocks aren't tracked across files). - g_setup->m_node.chainman->ActiveChainstate().LoadExternalBlockFile(fuzzed_block_file); + g_setup->m_node.chainman->LoadExternalBlockFile(fuzzed_block_file); } } diff --git a/src/test/fuzz/parse_univalue.cpp b/src/test/fuzz/parse_univalue.cpp index c9096d0386..a3d6ab6375 100644 --- a/src/test/fuzz/parse_univalue.cpp +++ b/src/test/fuzz/parse_univalue.cpp @@ -67,7 +67,7 @@ FUZZ_TARGET(parse_univalue, .init = initialize_parse_univalue) } catch (const std::runtime_error&) { } try { - (void)ParseSighashString(univalue); + if (univalue.isNull() || univalue.isStr()) (void)ParseSighashString(univalue); } catch (const UniValue&) { } try { |