Changeset View
Changeset View
Standalone View
Standalone View
src/test/fuzz/deserialize.cpp
| Show All 21 Lines | |||||
| #include <psbt.h> | #include <psbt.h> | ||||
| #include <pubkey.h> | #include <pubkey.h> | ||||
| #include <script/keyorigin.h> | #include <script/keyorigin.h> | ||||
| #include <streams.h> | #include <streams.h> | ||||
| #include <undo.h> | #include <undo.h> | ||||
| #include <version.h> | #include <version.h> | ||||
| #include <test/fuzz/fuzz.h> | #include <test/fuzz/fuzz.h> | ||||
| #include <test/fuzz/util.h> | |||||
| #include <cstdint> | #include <cstdint> | ||||
| #include <exception> | #include <exception> | ||||
| #include <optional> | #include <optional> | ||||
| #include <stdexcept> | #include <stdexcept> | ||||
| #include <unistd.h> | #include <unistd.h> | ||||
| #include <vector> | #include <vector> | ||||
| Show All 11 Lines | FUZZ_TARGET_INIT(name, initialize_deserialize) { \ | ||||
| } catch (const invalid_fuzzing_input_exception &) { \ | } catch (const invalid_fuzzing_input_exception &) { \ | ||||
| } \ | } \ | ||||
| } | } | ||||
| namespace { | namespace { | ||||
| struct invalid_fuzzing_input_exception : public std::exception {}; | struct invalid_fuzzing_input_exception : public std::exception {}; | ||||
| template <typename T, typename P> | |||||
| DataStream Serialize(const T &obj, const P ¶ms) { | |||||
| DataStream ds{}; | |||||
| ds << WithParams(params, obj); | |||||
| return ds; | |||||
| } | |||||
| template <typename T, typename P> | |||||
| T Deserialize(DataStream &&ds, const P ¶ms) { | |||||
| T obj; | |||||
| ds >> WithParams(params, obj); | |||||
| return obj; | |||||
| } | |||||
| template <typename T, typename P> | |||||
| void DeserializeFromFuzzingInput(std::vector<uint8_t> &buffer, T &&obj, | |||||
| const P ¶ms) { | |||||
| DataStream ds{buffer}; | |||||
| try { | |||||
| ds >> WithParams(params, obj); | |||||
| } catch (const std::ios_base::failure &) { | |||||
| throw invalid_fuzzing_input_exception(); | |||||
| } | |||||
| assert(buffer.empty() || !Serialize(obj, params).empty()); | |||||
| } | |||||
| template <typename T> | template <typename T> | ||||
| CDataStream Serialize(const T &obj, const int version = INIT_PROTO_VERSION, | CDataStream Serialize(const T &obj, const int version = INIT_PROTO_VERSION, | ||||
| const int ser_type = SER_NETWORK) { | const int ser_type = SER_NETWORK) { | ||||
| CDataStream ds(ser_type, version); | CDataStream ds(ser_type, version); | ||||
| ds << obj; | ds << obj; | ||||
| return ds; | return ds; | ||||
| } | } | ||||
| template <typename T> T Deserialize(CDataStream ds) { | template <typename T> T Deserialize(CDataStream ds) { | ||||
| T obj; | T obj; | ||||
| ds >> obj; | ds >> obj; | ||||
| return obj; | return obj; | ||||
| } | } | ||||
| template <typename T> | template <typename T> | ||||
| void DeserializeFromFuzzingInput( | void DeserializeFromFuzzingInput( | ||||
| const std::vector<uint8_t> &buffer, T &obj, | const std::vector<uint8_t> &buffer, T &&obj, | ||||
| const std::optional<int> protocol_version = std::nullopt, | const std::optional<int> protocol_version = std::nullopt, | ||||
| const int ser_type = SER_NETWORK) { | const int ser_type = SER_NETWORK) { | ||||
| CDataStream ds(buffer, ser_type, INIT_PROTO_VERSION); | CDataStream ds(buffer, ser_type, INIT_PROTO_VERSION); | ||||
| if (protocol_version) { | if (protocol_version) { | ||||
| ds.SetVersion(*protocol_version); | ds.SetVersion(*protocol_version); | ||||
| } else { | } else { | ||||
| try { | try { | ||||
| int version; | int version; | ||||
| ds >> version; | ds >> version; | ||||
| ds.SetVersion(version); | ds.SetVersion(version); | ||||
| } catch (const std::ios_base::failure &) { | } catch (const std::ios_base::failure &) { | ||||
| throw invalid_fuzzing_input_exception(); | throw invalid_fuzzing_input_exception(); | ||||
| } | } | ||||
| } | } | ||||
| try { | try { | ||||
| ds >> obj; | ds >> obj; | ||||
| } catch (const std::ios_base::failure &) { | } catch (const std::ios_base::failure &) { | ||||
| throw invalid_fuzzing_input_exception(); | throw invalid_fuzzing_input_exception(); | ||||
| } | } | ||||
| assert(buffer.empty() || !Serialize(obj).empty()); | assert(buffer.empty() || !Serialize(obj).empty()); | ||||
| } | } | ||||
| template <typename T, typename P> | |||||
| void AssertEqualAfterSerializeDeserialize(const T &obj, const P ¶ms) { | |||||
| assert(Deserialize<T>(Serialize(obj, params), params) == obj); | |||||
| } | |||||
| template <typename T> | template <typename T> | ||||
| void AssertEqualAfterSerializeDeserialize( | void AssertEqualAfterSerializeDeserialize( | ||||
| const T &obj, const int version = INIT_PROTO_VERSION, | const T &obj, const int version = INIT_PROTO_VERSION, | ||||
| const int ser_type = SER_NETWORK) { | const int ser_type = SER_NETWORK) { | ||||
| assert(Deserialize<T>(Serialize(obj, version, ser_type)) == obj); | assert(Deserialize<T>(Serialize(obj, version, ser_type)) == obj); | ||||
| } | } | ||||
| } // namespace | } // namespace | ||||
| FUZZ_TARGET_DESERIALIZE(block_filter_deserialize, { | FUZZ_TARGET_DESERIALIZE(block_filter_deserialize, { | ||||
| BlockFilter block_filter; | BlockFilter block_filter; | ||||
| DeserializeFromFuzzingInput(buffer, block_filter); | DeserializeFromFuzzingInput(buffer, block_filter); | ||||
| }) | }) | ||||
| FUZZ_TARGET_DESERIALIZE(addr_info_deserialize, { | FUZZ_TARGET_INIT(addr_info_deserialize, initialize_deserialize) { | ||||
| AddrInfo addr_info; | FuzzedDataProvider fdp{buffer.data(), buffer.size()}; | ||||
| DeserializeFromFuzzingInput(buffer, addr_info); | (void)ConsumeDeserializable<AddrInfo>( | ||||
| }) | fdp, ConsumeDeserializationParams<CAddress::SerParams>(fdp)); | ||||
| } | |||||
| FUZZ_TARGET_DESERIALIZE(block_file_info_deserialize, { | FUZZ_TARGET_DESERIALIZE(block_file_info_deserialize, { | ||||
| CBlockFileInfo block_file_info; | CBlockFileInfo block_file_info; | ||||
| DeserializeFromFuzzingInput(buffer, block_file_info); | DeserializeFromFuzzingInput(buffer, block_file_info); | ||||
| }) | }) | ||||
| FUZZ_TARGET_DESERIALIZE(block_header_and_short_txids_deserialize, { | FUZZ_TARGET_DESERIALIZE(block_header_and_short_txids_deserialize, { | ||||
| CBlockHeaderAndShortTxIDs block_header_and_short_txids; | CBlockHeaderAndShortTxIDs block_header_and_short_txids; | ||||
| DeserializeFromFuzzingInput(buffer, block_header_and_short_txids); | DeserializeFromFuzzingInput(buffer, block_header_and_short_txids); | ||||
| }) | }) | ||||
| Show All 20 Lines | FUZZ_TARGET_DESERIALIZE(pub_key_deserialize, { | ||||
| DeserializeFromFuzzingInput(buffer, pub_key); | DeserializeFromFuzzingInput(buffer, pub_key); | ||||
| // TODO: The following equivalence should hold for CPubKey? Fix. | // TODO: The following equivalence should hold for CPubKey? Fix. | ||||
| // AssertEqualAfterSerializeDeserialize(pub_key); | // AssertEqualAfterSerializeDeserialize(pub_key); | ||||
| }) | }) | ||||
| FUZZ_TARGET_DESERIALIZE(script_deserialize, { | FUZZ_TARGET_DESERIALIZE(script_deserialize, { | ||||
| CScript script; | CScript script; | ||||
| DeserializeFromFuzzingInput(buffer, script); | DeserializeFromFuzzingInput(buffer, script); | ||||
| }) | }) | ||||
| FUZZ_TARGET_DESERIALIZE(sub_net_deserialize, { | FUZZ_TARGET_INIT(sub_net_deserialize, initialize_deserialize) { | ||||
| CSubNet sub_net_1; | FuzzedDataProvider fdp{buffer.data(), buffer.size()}; | ||||
| DeserializeFromFuzzingInput(buffer, sub_net_1, INIT_PROTO_VERSION); | const auto ser_params{ | ||||
| AssertEqualAfterSerializeDeserialize(sub_net_1, INIT_PROTO_VERSION); | ConsumeDeserializationParams<CNetAddr::SerParams>(fdp)}; | ||||
| CSubNet sub_net_2; | const auto maybe_s1{ConsumeDeserializable<CSubNet>(fdp, ser_params)}; | ||||
| DeserializeFromFuzzingInput(buffer, sub_net_2, | if (!maybe_s1) { | ||||
| INIT_PROTO_VERSION | ADDRV2_FORMAT); | return; | ||||
| AssertEqualAfterSerializeDeserialize(sub_net_2, | } | ||||
| INIT_PROTO_VERSION | ADDRV2_FORMAT); | const CSubNet &sub_net_1{*maybe_s1}; | ||||
| CSubNet sub_net_3; | AssertEqualAfterSerializeDeserialize(sub_net_1, ser_params); | ||||
| DeserializeFromFuzzingInput(buffer, sub_net_3); | |||||
| AssertEqualAfterSerializeDeserialize(sub_net_3, | const auto maybe_s2{ConsumeDeserializable<CSubNet>(fdp, ser_params)}; | ||||
| INIT_PROTO_VERSION | ADDRV2_FORMAT); | if (!maybe_s2) { | ||||
| }) | return; | ||||
| } | |||||
| const CSubNet &sub_net_2{*maybe_s2}; | |||||
| AssertEqualAfterSerializeDeserialize(sub_net_2, ser_params); | |||||
| } | |||||
| FUZZ_TARGET_DESERIALIZE(tx_in_deserialize, { | FUZZ_TARGET_DESERIALIZE(tx_in_deserialize, { | ||||
| CTxIn tx_in; | CTxIn tx_in; | ||||
| DeserializeFromFuzzingInput(buffer, tx_in); | DeserializeFromFuzzingInput(buffer, tx_in); | ||||
| AssertEqualAfterSerializeDeserialize(tx_in); | AssertEqualAfterSerializeDeserialize(tx_in); | ||||
| }) | }) | ||||
| FUZZ_TARGET_DESERIALIZE(flat_file_pos_deserialize, { | FUZZ_TARGET_DESERIALIZE(flat_file_pos_deserialize, { | ||||
| FlatFilePos flat_file_pos; | FlatFilePos flat_file_pos; | ||||
| DeserializeFromFuzzingInput(buffer, flat_file_pos); | DeserializeFromFuzzingInput(buffer, flat_file_pos); | ||||
| Show All 25 Lines | FUZZ_TARGET_DESERIALIZE(blocklocator_deserialize, { | ||||
| DeserializeFromFuzzingInput(buffer, bl); | DeserializeFromFuzzingInput(buffer, bl); | ||||
| }) | }) | ||||
| FUZZ_TARGET_DESERIALIZE(blockmerkleroot, { | FUZZ_TARGET_DESERIALIZE(blockmerkleroot, { | ||||
| CBlock block; | CBlock block; | ||||
| DeserializeFromFuzzingInput(buffer, block); | DeserializeFromFuzzingInput(buffer, block); | ||||
| bool mutated; | bool mutated; | ||||
| BlockMerkleRoot(block, &mutated); | BlockMerkleRoot(block, &mutated); | ||||
| }) | }) | ||||
| FUZZ_TARGET_DESERIALIZE(addrman_deserialize, { | |||||
| AddrMan am(/*asmap=*/std::vector<bool>(), /*deterministic=*/false, | |||||
| /*consistency_check_ratio=*/0); | |||||
| DeserializeFromFuzzingInput(buffer, am); | |||||
| }) | |||||
| FUZZ_TARGET_DESERIALIZE(blockheader_deserialize, { | FUZZ_TARGET_DESERIALIZE(blockheader_deserialize, { | ||||
| CBlockHeader bh; | CBlockHeader bh; | ||||
| DeserializeFromFuzzingInput(buffer, bh); | DeserializeFromFuzzingInput(buffer, bh); | ||||
| }) | }) | ||||
| FUZZ_TARGET_DESERIALIZE(banentry_deserialize, { | FUZZ_TARGET_DESERIALIZE(banentry_deserialize, { | ||||
| CBanEntry be; | CBanEntry be; | ||||
| DeserializeFromFuzzingInput(buffer, be); | DeserializeFromFuzzingInput(buffer, be); | ||||
| }) | }) | ||||
| FUZZ_TARGET_DESERIALIZE(txundo_deserialize, { | FUZZ_TARGET_DESERIALIZE(txundo_deserialize, { | ||||
| CTxUndo tu; | CTxUndo tu; | ||||
| DeserializeFromFuzzingInput(buffer, tu); | DeserializeFromFuzzingInput(buffer, tu); | ||||
| }) | }) | ||||
| FUZZ_TARGET_DESERIALIZE(blockundo_deserialize, { | FUZZ_TARGET_DESERIALIZE(blockundo_deserialize, { | ||||
| CBlockUndo bu; | CBlockUndo bu; | ||||
| DeserializeFromFuzzingInput(buffer, bu); | DeserializeFromFuzzingInput(buffer, bu); | ||||
| }) | }) | ||||
| FUZZ_TARGET_DESERIALIZE(coins_deserialize, { | FUZZ_TARGET_DESERIALIZE(coins_deserialize, { | ||||
| Coin coin; | Coin coin; | ||||
| DeserializeFromFuzzingInput(buffer, coin); | DeserializeFromFuzzingInput(buffer, coin); | ||||
| }) | }) | ||||
| FUZZ_TARGET_DESERIALIZE(netaddr_deserialize, { | FUZZ_TARGET_INIT(netaddr_deserialize, initialize_deserialize) { | ||||
| CNetAddr na; | FuzzedDataProvider fdp{buffer.data(), buffer.size()}; | ||||
| DeserializeFromFuzzingInput(buffer, na); | const auto maybe_na{ConsumeDeserializable<CNetAddr>( | ||||
| fdp, ConsumeDeserializationParams<CNetAddr::SerParams>(fdp))}; | |||||
| if (!maybe_na) { | |||||
| return; | |||||
| } | |||||
| const CNetAddr &na{*maybe_na}; | |||||
| if (na.IsAddrV1Compatible()) { | if (na.IsAddrV1Compatible()) { | ||||
| AssertEqualAfterSerializeDeserialize(na); | AssertEqualAfterSerializeDeserialize( | ||||
| na, ConsumeDeserializationParams<CNetAddr::SerParams>(fdp)); | |||||
| } | } | ||||
| AssertEqualAfterSerializeDeserialize(na, | AssertEqualAfterSerializeDeserialize(na, CNetAddr::V2); | ||||
| INIT_PROTO_VERSION | ADDRV2_FORMAT); | } | ||||
| }) | FUZZ_TARGET_INIT(service_deserialize, initialize_deserialize) { | ||||
| FUZZ_TARGET_DESERIALIZE(service_deserialize, { | FuzzedDataProvider fdp{buffer.data(), buffer.size()}; | ||||
| CService s; | const auto ser_params{ | ||||
| DeserializeFromFuzzingInput(buffer, s); | ConsumeDeserializationParams<CNetAddr::SerParams>(fdp)}; | ||||
| const auto maybe_s{ConsumeDeserializable<CService>(fdp, ser_params)}; | |||||
| if (!maybe_s) { | |||||
| return; | |||||
| } | |||||
| const CService &s{*maybe_s}; | |||||
| if (s.IsAddrV1Compatible()) { | if (s.IsAddrV1Compatible()) { | ||||
| AssertEqualAfterSerializeDeserialize(s); | AssertEqualAfterSerializeDeserialize( | ||||
| s, ConsumeDeserializationParams<CNetAddr::SerParams>(fdp)); | |||||
| } | |||||
| AssertEqualAfterSerializeDeserialize(s, CNetAddr::V2); | |||||
| if (ser_params.enc == CNetAddr::Encoding::V1) { | |||||
| assert(s.IsAddrV1Compatible()); | |||||
| } | |||||
| } | } | ||||
| AssertEqualAfterSerializeDeserialize(s, INIT_PROTO_VERSION | ADDRV2_FORMAT); | |||||
| CService s1; | |||||
| DeserializeFromFuzzingInput(buffer, s1, INIT_PROTO_VERSION); | |||||
| AssertEqualAfterSerializeDeserialize(s1, INIT_PROTO_VERSION); | |||||
| assert(s1.IsAddrV1Compatible()); | |||||
| CService s2; | |||||
| DeserializeFromFuzzingInput(buffer, s2, INIT_PROTO_VERSION | ADDRV2_FORMAT); | |||||
| AssertEqualAfterSerializeDeserialize(s2, | |||||
| INIT_PROTO_VERSION | ADDRV2_FORMAT); | |||||
| }) | |||||
| static const CMessageHeader::MessageMagic pchMessageStart = { | static const CMessageHeader::MessageMagic pchMessageStart = { | ||||
| {0x00, 0x00, 0x00, 0x00}}; | {0x00, 0x00, 0x00, 0x00}}; | ||||
| FUZZ_TARGET_DESERIALIZE(messageheader_deserialize, { | FUZZ_TARGET_DESERIALIZE(messageheader_deserialize, { | ||||
| CMessageHeader mh(pchMessageStart); | CMessageHeader mh(pchMessageStart); | ||||
| DeserializeFromFuzzingInput(buffer, mh); | DeserializeFromFuzzingInput(buffer, mh); | ||||
| (void)mh.IsValidWithoutConfig(pchMessageStart); | (void)mh.IsValidWithoutConfig(pchMessageStart); | ||||
| }) | }) | ||||
| FUZZ_TARGET_DESERIALIZE(address_deserialize_v1_notime, { | FUZZ_TARGET_INIT(address_deserialize, initialize_deserialize) { | ||||
| CAddress a; | FuzzedDataProvider fdp{buffer.data(), buffer.size()}; | ||||
| DeserializeFromFuzzingInput(buffer, a, INIT_PROTO_VERSION); | const auto ser_enc{ConsumeDeserializationParams<CNetAddr::SerParams>(fdp)}; | ||||
| // A CAddress without nTime (as is expected under INIT_PROTO_VERSION) | const auto maybe_a{ConsumeDeserializable<CAddress>( | ||||
| // will roundtrip in all 5 formats (with/without nTime, v1/v2, | fdp, CAddress::SerParams{{ser_enc}, CAddress::Format::Network})}; | ||||
| // network/disk) | if (!maybe_a) { | ||||
| AssertEqualAfterSerializeDeserialize(a, INIT_PROTO_VERSION); | return; | ||||
| AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION); | } | ||||
| AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK); | const CAddress &a{*maybe_a}; | ||||
| AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT); | // A CAddress in V1 mode will roundtrip | ||||
| AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK); | // in all 4 formats (v1/v2, network/disk) | ||||
| }) | if (ser_enc.enc == CNetAddr::Encoding::V1) { | ||||
| FUZZ_TARGET_DESERIALIZE(address_deserialize_v1_withtime, { | AssertEqualAfterSerializeDeserialize(a, CAddress::V1_NETWORK); | ||||
| CAddress a; | AssertEqualAfterSerializeDeserialize(a, CAddress::V1_DISK); | ||||
| DeserializeFromFuzzingInput(buffer, a, PROTOCOL_VERSION); | AssertEqualAfterSerializeDeserialize(a, CAddress::V2_NETWORK); | ||||
| // A CAddress in V1 mode will roundtrip in all 4 formats that have | AssertEqualAfterSerializeDeserialize(a, CAddress::V2_DISK); | ||||
| // nTime. | } else { | ||||
| AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION); | |||||
| AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK); | |||||
| AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT); | |||||
| AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK); | |||||
| }) | |||||
| FUZZ_TARGET_DESERIALIZE(address_deserialize_v2, { | |||||
| CAddress a; | |||||
| DeserializeFromFuzzingInput(buffer, a, PROTOCOL_VERSION | ADDRV2_FORMAT); | |||||
| // A CAddress in V2 mode will roundtrip in both V2 formats, and also in | // A CAddress in V2 mode will roundtrip in both V2 formats, and also in | ||||
| // the V1 formats with time if it's V1 compatible. | // the V1 formats if it's V1 compatible. | ||||
| if (a.IsAddrV1Compatible()) { | if (a.IsAddrV1Compatible()) { | ||||
| AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION); | AssertEqualAfterSerializeDeserialize(a, CAddress::V1_DISK); | ||||
| AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK); | AssertEqualAfterSerializeDeserialize(a, CAddress::V1_NETWORK); | ||||
| } | |||||
| AssertEqualAfterSerializeDeserialize(a, CAddress::V2_NETWORK); | |||||
| AssertEqualAfterSerializeDeserialize(a, CAddress::V2_DISK); | |||||
| } | |||||
| } | } | ||||
| AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT); | |||||
| AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK); | |||||
| }) | |||||
| FUZZ_TARGET_DESERIALIZE(inv_deserialize, { | FUZZ_TARGET_DESERIALIZE(inv_deserialize, { | ||||
| CInv i; | CInv i; | ||||
| DeserializeFromFuzzingInput(buffer, i); | DeserializeFromFuzzingInput(buffer, i); | ||||
| }) | }) | ||||
| FUZZ_TARGET_DESERIALIZE(bloomfilter_deserialize, { | FUZZ_TARGET_DESERIALIZE(bloomfilter_deserialize, { | ||||
| CBloomFilter bf; | CBloomFilter bf; | ||||
| DeserializeFromFuzzingInput(buffer, bf); | DeserializeFromFuzzingInput(buffer, bf); | ||||
| }) | }) | ||||
| Show All 36 Lines | |||||