fuzz: Exclude too expensive inputs in descriptor_parse targets
Also, fixup iwyu warnings in the util module. Also, fixup a typo. The moved part can be reviewed with the git option: --color-moved=dimmed-zebra
This commit is contained in:
4 files changed
+34
-30
No files matched your search
@@ -77,21 +77,9 @@ void initialize_mocked_descriptor_parse()
|
|||||||
|
|
||||||
FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse)
|
FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse)
|
||||||
{
|
{
|
||||||
// Key derivation is expensive. Deriving deep derivation paths take a lot of compute and we'd
|
|
||||||
// rather spend time elsewhere in this target, like on the actual descriptor syntax. So rule
|
|
||||||
// out strings which could correspond to a descriptor containing a too large derivation path.
|
|
||||||
if (HasDeepDerivPath(buffer)) return;
|
|
||||||
|
|
||||||
// Some fragments can take a virtually unlimited number of sub-fragments (thresh, multi_a) but
|
|
||||||
// may perform quadratic operations on them. Limit the number of sub-fragments per fragment.
|
|
||||||
if (HasTooManySubFrag(buffer)) return;
|
|
||||||
|
|
||||||
// The script building logic performs quadratic copies in the number of nested wrappers. Limit
|
|
||||||
// the number of nested wrappers per fragment.
|
|
||||||
if (HasTooManyWrappers(buffer)) return;
|
|
||||||
|
|
||||||
const std::string mocked_descriptor{buffer.begin(), buffer.end()};
|
const std::string mocked_descriptor{buffer.begin(), buffer.end()};
|
||||||
if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) {
|
if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) {
|
||||||
|
if (IsTooExpensive(MakeUCharSpan(*descriptor))) return;
|
||||||
FlatSigningProvider signing_provider;
|
FlatSigningProvider signing_provider;
|
||||||
std::string error;
|
std::string error;
|
||||||
const auto desc = Parse(*descriptor, signing_provider, error);
|
const auto desc = Parse(*descriptor, signing_provider, error);
|
||||||
@@ -106,10 +94,7 @@ FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse)
|
|||||||
|
|
||||||
FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse)
|
FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse)
|
||||||
{
|
{
|
||||||
// See comments above for rationales.
|
if (IsTooExpensive(buffer)) return;
|
||||||
if (HasDeepDerivPath(buffer)) return;
|
|
||||||
if (HasTooManySubFrag(buffer)) return;
|
|
||||||
if (HasTooManyWrappers(buffer)) return;
|
|
||||||
|
|
||||||
const std::string descriptor(buffer.begin(), buffer.end());
|
const std::string descriptor(buffer.begin(), buffer.end());
|
||||||
FlatSigningProvider signing_provider;
|
FlatSigningProvider signing_provider;
|
||||||
|
|||||||
@@ -7,12 +7,15 @@
|
|||||||
#include <key.h>
|
#include <key.h>
|
||||||
#include <key_io.h>
|
#include <key_io.h>
|
||||||
#include <pubkey.h>
|
#include <pubkey.h>
|
||||||
|
#include <span.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
|
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
void MockedDescriptorConverter::Init() {
|
void MockedDescriptorConverter::Init()
|
||||||
|
{
|
||||||
// The data to use as a private key or a seed for an xprv.
|
// The data to use as a private key or a seed for an xprv.
|
||||||
std::array<std::byte, 32> key_data{std::byte{1}};
|
std::array<std::byte, 32> key_data{std::byte{1}};
|
||||||
// Generate keys of all kinds and store them in the keys array.
|
// Generate keys of all kinds and store them in the keys array.
|
||||||
|
|||||||
@@ -86,4 +86,31 @@ constexpr uint32_t MAX_LEAF_SIZE{200};
|
|||||||
/// MockedDescriptorConverter) has a leaf size too large.
|
/// MockedDescriptorConverter) has a leaf size too large.
|
||||||
bool HasTooLargeLeafSize(std::span<const uint8_t> buff, uint32_t max_leaf_size = MAX_LEAF_SIZE);
|
bool HasTooLargeLeafSize(std::span<const uint8_t> buff, uint32_t max_leaf_size = MAX_LEAF_SIZE);
|
||||||
|
|
||||||
|
/// Deriving "expensive" descriptors will consume useful fuzz compute. The
|
||||||
|
/// compute is better spent on a smaller subset of descriptors, which still
|
||||||
|
/// covers all real end-user settings.
|
||||||
|
///
|
||||||
|
/// Use this function after MockedDescriptorConverter::GetDescriptor()
|
||||||
|
inline bool IsTooExpensive(std::span<const uint8_t> buffer)
|
||||||
|
{
|
||||||
|
// Key derivation is expensive. Deriving deep derivation paths takes a lot of compute and we'd
|
||||||
|
// rather spend time elsewhere in this target, like on the actual descriptor syntax. So rule
|
||||||
|
// out strings which could correspond to a descriptor containing a too large derivation path.
|
||||||
|
if (HasDeepDerivPath(buffer)) return true;
|
||||||
|
|
||||||
|
// Some fragments can take a virtually unlimited number of sub-fragments (thresh, multi_a) but
|
||||||
|
// may perform quadratic operations on them. Limit the number of sub-fragments per fragment.
|
||||||
|
if (HasTooManySubFrag(buffer)) return true;
|
||||||
|
|
||||||
|
// The script building logic performs quadratic copies in the number of nested wrappers. Limit
|
||||||
|
// the number of nested wrappers per fragment.
|
||||||
|
if (HasTooManyWrappers(buffer)) return true;
|
||||||
|
|
||||||
|
// If any suspected leaf is too large, it will likely not represent a valid
|
||||||
|
// use-case. Also, possible base58 parsing in the leaf is quadratic. So
|
||||||
|
// limit the leaf size.
|
||||||
|
if (HasTooLargeLeafSize(buffer)) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#endif // BITCOIN_TEST_FUZZ_UTIL_DESCRIPTOR_H
|
#endif // BITCOIN_TEST_FUZZ_UTIL_DESCRIPTOR_H
|
||||||
@@ -50,23 +50,12 @@ void initialize_spkm()
|
|||||||
MOCKED_DESC_CONVERTER.Init();
|
MOCKED_DESC_CONVERTER.Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Deriving "expensive" descriptors will consume useful fuzz compute. The
|
|
||||||
* compute is better spent on a smaller subset of descriptors, which still
|
|
||||||
* covers all real end-user settings.
|
|
||||||
*/
|
|
||||||
static bool IsTooExpensive(std::span<const uint8_t> desc)
|
|
||||||
{
|
|
||||||
return HasDeepDerivPath(desc) || HasTooManySubFrag(desc) || HasTooManyWrappers(desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::optional<std::pair<WalletDescriptor, FlatSigningProvider>> CreateWalletDescriptor(FuzzedDataProvider& fuzzed_data_provider)
|
static std::optional<std::pair<WalletDescriptor, FlatSigningProvider>> CreateWalletDescriptor(FuzzedDataProvider& fuzzed_data_provider)
|
||||||
{
|
{
|
||||||
const std::string mocked_descriptor{fuzzed_data_provider.ConsumeRandomLengthString()};
|
const std::string mocked_descriptor{fuzzed_data_provider.ConsumeRandomLengthString()};
|
||||||
if (IsTooExpensive(MakeUCharSpan(mocked_descriptor))) return {};
|
|
||||||
const auto desc_str{MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)};
|
const auto desc_str{MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)};
|
||||||
if (!desc_str.has_value()) return std::nullopt;
|
if (!desc_str.has_value()) return std::nullopt;
|
||||||
if (HasTooLargeLeafSize(MakeUCharSpan(*desc_str))) return {};
|
if (IsTooExpensive(MakeUCharSpan(*desc_str))) return {};
|
||||||
|
|
||||||
FlatSigningProvider keys;
|
FlatSigningProvider keys;
|
||||||
std::string error;
|
std::string error;
|
||||||
|
|||||||
Reference in new issue
Block a user