aboutsummaryrefslogtreecommitdiff
path: root/src/test/fuzz
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/fuzz')
-rw-r--r--src/test/fuzz/addrman.cpp33
-rw-r--r--src/test/fuzz/connman.cpp10
-rw-r--r--src/test/fuzz/descriptor_parse.cpp149
-rw-r--r--src/test/fuzz/load_external_block_file.cpp4
-rw-r--r--src/test/fuzz/parse_univalue.cpp2
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 {