mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-02 09:15:04 +02:00
Use serialization parameters for CAddress serialization
This also cleans up the addrman (de)serialization code paths to only allow `Disk` serialization. Some unit tests previously forced a `Network` serialization, which does not make sense, because Bitcoin Core in production will always `Disk` serialize. This cleanup idea was suggested by Pieter Wuille and implemented by Anthony Towns. Co-authored-by: Pieter Wuille <pieter@wuille.net> Co-authored-by: Anthony Towns <aj@erisian.com.au>
This commit is contained in:
@@ -49,7 +49,7 @@ void initialize_addrman()
|
||||
FUZZ_TARGET(data_stream_addr_man, .init = initialize_addrman)
|
||||
{
|
||||
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
|
||||
CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
|
||||
DataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
|
||||
NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
|
||||
AddrMan addr_man(netgroupman, /*deterministic=*/false, GetCheckRatio());
|
||||
try {
|
||||
@@ -78,12 +78,12 @@ CNetAddr RandAddr(FuzzedDataProvider& fuzzed_data_provider, FastRandomContext& f
|
||||
net = 6;
|
||||
}
|
||||
|
||||
CDataStream s(SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
|
||||
DataStream s{};
|
||||
|
||||
s << net;
|
||||
s << fast_random_context.randbytes(net_len_map.at(net));
|
||||
|
||||
s >> addr;
|
||||
s >> WithParams(CAddress::V2_NETWORK, addr);
|
||||
}
|
||||
|
||||
// Return a dummy IPv4 5.5.5.5 if we generated an invalid address.
|
||||
@@ -241,9 +241,7 @@ FUZZ_TARGET(addrman, .init = initialize_addrman)
|
||||
auto addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider);
|
||||
if (fuzzed_data_provider.ConsumeBool()) {
|
||||
const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
|
||||
CDataStream ds(serialized_data, SER_DISK, INIT_PROTO_VERSION);
|
||||
const auto ser_version{fuzzed_data_provider.ConsumeIntegral<int32_t>()};
|
||||
ds.SetVersion(ser_version);
|
||||
DataStream ds{serialized_data};
|
||||
try {
|
||||
ds >> *addr_man_ptr;
|
||||
} catch (const std::ios_base::failure&) {
|
||||
@@ -295,7 +293,7 @@ FUZZ_TARGET(addrman, .init = initialize_addrman)
|
||||
in_new = fuzzed_data_provider.ConsumeBool();
|
||||
}
|
||||
(void)const_addr_man.Size(network, in_new);
|
||||
CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION);
|
||||
DataStream data_stream{};
|
||||
data_stream << const_addr_man;
|
||||
}
|
||||
|
||||
@@ -309,10 +307,10 @@ FUZZ_TARGET(addrman_serdeser, .init = initialize_addrman)
|
||||
AddrManDeterministic addr_man1{netgroupman, fuzzed_data_provider};
|
||||
AddrManDeterministic addr_man2{netgroupman, fuzzed_data_provider};
|
||||
|
||||
CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION);
|
||||
DataStream data_stream{};
|
||||
|
||||
FillAddrman(addr_man1, fuzzed_data_provider);
|
||||
data_stream << addr_man1;
|
||||
data_stream >> addr_man2;
|
||||
assert(addr_man1 == addr_man2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include <pubkey.h>
|
||||
#include <script/keyorigin.h>
|
||||
#include <streams.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <undo.h>
|
||||
#include <version.h>
|
||||
@@ -34,8 +36,6 @@
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <test/fuzz/fuzz.h>
|
||||
|
||||
using node::SnapshotMetadata;
|
||||
|
||||
namespace {
|
||||
@@ -62,6 +62,34 @@ namespace {
|
||||
struct invalid_fuzzing_input_exception : public std::exception {
|
||||
};
|
||||
|
||||
template <typename T, typename P>
|
||||
DataStream Serialize(const T& obj, const P& params)
|
||||
{
|
||||
DataStream ds{};
|
||||
ds << WithParams(params, obj);
|
||||
return ds;
|
||||
}
|
||||
|
||||
template <typename T, typename P>
|
||||
T Deserialize(DataStream&& ds, const P& params)
|
||||
{
|
||||
T obj;
|
||||
ds >> WithParams(params, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
template <typename T, typename P>
|
||||
void DeserializeFromFuzzingInput(FuzzBufferType buffer, T&& obj, const P& params)
|
||||
{
|
||||
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>
|
||||
CDataStream Serialize(const T& obj, const int version = INIT_PROTO_VERSION, const int ser_type = SER_NETWORK)
|
||||
{
|
||||
@@ -79,7 +107,7 @@ T Deserialize(CDataStream ds)
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const std::optional<int> protocol_version = std::nullopt, const int ser_type = SER_NETWORK)
|
||||
void DeserializeFromFuzzingInput(FuzzBufferType buffer, T&& obj, const std::optional<int> protocol_version = std::nullopt, const int ser_type = SER_NETWORK)
|
||||
{
|
||||
CDataStream ds(buffer, ser_type, INIT_PROTO_VERSION);
|
||||
if (protocol_version) {
|
||||
@@ -101,6 +129,11 @@ void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const std::optio
|
||||
assert(buffer.empty() || !Serialize(obj).empty());
|
||||
}
|
||||
|
||||
template <typename T, typename P>
|
||||
void AssertEqualAfterSerializeDeserialize(const T& obj, const P& params)
|
||||
{
|
||||
assert(Deserialize<T>(Serialize(obj, params), params) == obj);
|
||||
}
|
||||
template <typename T>
|
||||
void AssertEqualAfterSerializeDeserialize(const T& obj, const int version = INIT_PROTO_VERSION, const int ser_type = SER_NETWORK)
|
||||
{
|
||||
@@ -113,10 +146,11 @@ FUZZ_TARGET_DESERIALIZE(block_filter_deserialize, {
|
||||
BlockFilter block_filter;
|
||||
DeserializeFromFuzzingInput(buffer, block_filter);
|
||||
})
|
||||
FUZZ_TARGET_DESERIALIZE(addr_info_deserialize, {
|
||||
AddrInfo addr_info;
|
||||
DeserializeFromFuzzingInput(buffer, addr_info);
|
||||
})
|
||||
FUZZ_TARGET(addr_info_deserialize, .init = initialize_deserialize)
|
||||
{
|
||||
FuzzedDataProvider fdp{buffer.data(), buffer.size()};
|
||||
(void)ConsumeDeserializable<AddrInfo>(fdp, ConsumeDeserializationParams<CAddress::SerParams>(fdp));
|
||||
}
|
||||
FUZZ_TARGET_DESERIALIZE(block_file_info_deserialize, {
|
||||
CBlockFileInfo block_file_info;
|
||||
DeserializeFromFuzzingInput(buffer, block_file_info);
|
||||
@@ -197,13 +231,6 @@ FUZZ_TARGET_DESERIALIZE(blockmerkleroot, {
|
||||
bool mutated;
|
||||
BlockMerkleRoot(block, &mutated);
|
||||
})
|
||||
FUZZ_TARGET_DESERIALIZE(addrman_deserialize, {
|
||||
NetGroupManager netgroupman{std::vector<bool>()};
|
||||
AddrMan am(netgroupman,
|
||||
/*deterministic=*/false,
|
||||
g_setup->m_node.args->GetIntArg("-checkaddrman", 0));
|
||||
DeserializeFromFuzzingInput(buffer, am);
|
||||
})
|
||||
FUZZ_TARGET_DESERIALIZE(blockheader_deserialize, {
|
||||
CBlockHeader bh;
|
||||
DeserializeFromFuzzingInput(buffer, bh);
|
||||
@@ -220,66 +247,62 @@ FUZZ_TARGET_DESERIALIZE(coins_deserialize, {
|
||||
Coin coin;
|
||||
DeserializeFromFuzzingInput(buffer, coin);
|
||||
})
|
||||
FUZZ_TARGET_DESERIALIZE(netaddr_deserialize, {
|
||||
CNetAddr na;
|
||||
DeserializeFromFuzzingInput(buffer, na);
|
||||
FUZZ_TARGET(netaddr_deserialize, .init = initialize_deserialize)
|
||||
{
|
||||
FuzzedDataProvider fdp{buffer.data(), buffer.size()};
|
||||
const auto maybe_na{ConsumeDeserializable<CNetAddr>(fdp, ConsumeDeserializationParams<CNetAddr::SerParams>(fdp))};
|
||||
if (!maybe_na) return;
|
||||
const CNetAddr& na{*maybe_na};
|
||||
if (na.IsAddrV1Compatible()) {
|
||||
AssertEqualAfterSerializeDeserialize(na);
|
||||
AssertEqualAfterSerializeDeserialize(na, ConsumeDeserializationParams<CNetAddr::SerParams>(fdp));
|
||||
}
|
||||
AssertEqualAfterSerializeDeserialize(na, INIT_PROTO_VERSION | ADDRV2_FORMAT);
|
||||
})
|
||||
FUZZ_TARGET_DESERIALIZE(service_deserialize, {
|
||||
CService s;
|
||||
DeserializeFromFuzzingInput(buffer, s);
|
||||
AssertEqualAfterSerializeDeserialize(na, CNetAddr::V2);
|
||||
}
|
||||
FUZZ_TARGET(service_deserialize, .init = initialize_deserialize)
|
||||
{
|
||||
FuzzedDataProvider fdp{buffer.data(), buffer.size()};
|
||||
const auto ser_params{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()) {
|
||||
AssertEqualAfterSerializeDeserialize(s);
|
||||
AssertEqualAfterSerializeDeserialize(s, ConsumeDeserializationParams<CNetAddr::SerParams>(fdp));
|
||||
}
|
||||
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);
|
||||
})
|
||||
AssertEqualAfterSerializeDeserialize(s, CNetAddr::V2);
|
||||
if (ser_params.enc == CNetAddr::Encoding::V1) {
|
||||
assert(s.IsAddrV1Compatible());
|
||||
}
|
||||
}
|
||||
FUZZ_TARGET_DESERIALIZE(messageheader_deserialize, {
|
||||
CMessageHeader mh;
|
||||
DeserializeFromFuzzingInput(buffer, mh);
|
||||
(void)mh.IsCommandValid();
|
||||
})
|
||||
FUZZ_TARGET_DESERIALIZE(address_deserialize_v1_notime, {
|
||||
CAddress a;
|
||||
DeserializeFromFuzzingInput(buffer, a, INIT_PROTO_VERSION);
|
||||
// A CAddress without nTime (as is expected under INIT_PROTO_VERSION) will roundtrip
|
||||
// in all 5 formats (with/without nTime, v1/v2, network/disk)
|
||||
AssertEqualAfterSerializeDeserialize(a, INIT_PROTO_VERSION);
|
||||
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_v1_withtime, {
|
||||
CAddress a;
|
||||
DeserializeFromFuzzingInput(buffer, a, PROTOCOL_VERSION);
|
||||
// A CAddress in V1 mode will roundtrip in all 4 formats that have nTime.
|
||||
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 the V1 formats
|
||||
// with time if it's V1 compatible.
|
||||
if (a.IsAddrV1Compatible()) {
|
||||
AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
|
||||
AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
|
||||
FUZZ_TARGET(address_deserialize, .init = initialize_deserialize)
|
||||
{
|
||||
FuzzedDataProvider fdp{buffer.data(), buffer.size()};
|
||||
const auto ser_enc{ConsumeDeserializationParams<CNetAddr::SerParams>(fdp)};
|
||||
const auto maybe_a{ConsumeDeserializable<CAddress>(fdp, CAddress::SerParams{{ser_enc}, CAddress::Format::Network})};
|
||||
if (!maybe_a) return;
|
||||
const CAddress& a{*maybe_a};
|
||||
// A CAddress in V1 mode will roundtrip
|
||||
// in all 4 formats (v1/v2, network/disk)
|
||||
if (ser_enc.enc == CNetAddr::Encoding::V1) {
|
||||
AssertEqualAfterSerializeDeserialize(a, CAddress::V1_NETWORK);
|
||||
AssertEqualAfterSerializeDeserialize(a, CAddress::V1_DISK);
|
||||
AssertEqualAfterSerializeDeserialize(a, CAddress::V2_NETWORK);
|
||||
AssertEqualAfterSerializeDeserialize(a, CAddress::V2_DISK);
|
||||
} else {
|
||||
// A CAddress in V2 mode will roundtrip in both V2 formats, and also in the V1 formats
|
||||
// if it's V1 compatible.
|
||||
if (a.IsAddrV1Compatible()) {
|
||||
AssertEqualAfterSerializeDeserialize(a, CAddress::V1_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, {
|
||||
CInv i;
|
||||
DeserializeFromFuzzingInput(buffer, i);
|
||||
|
||||
@@ -53,7 +53,7 @@ FUZZ_TARGET(net, .init = initialize_net)
|
||||
}
|
||||
},
|
||||
[&] {
|
||||
const std::optional<CService> service_opt = ConsumeDeserializable<CService>(fuzzed_data_provider);
|
||||
const std::optional<CService> service_opt = ConsumeDeserializable<CService>(fuzzed_data_provider, ConsumeDeserializationParams<CNetAddr::SerParams>(fuzzed_data_provider));
|
||||
if (!service_opt) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ FUZZ_TARGET(script_sign, .init = initialize_script_sign)
|
||||
const std::vector<uint8_t> key = ConsumeRandomLengthByteVector(fuzzed_data_provider, 128);
|
||||
|
||||
{
|
||||
CDataStream random_data_stream = ConsumeDataStream(fuzzed_data_provider);
|
||||
DataStream stream{ConsumeDataStream(fuzzed_data_provider)};
|
||||
CDataStream random_data_stream{stream, SER_NETWORK, INIT_PROTO_VERSION}; // temporary copy, to be removed along with the version flag SERIALIZE_TRANSACTION_NO_WITNESS
|
||||
std::map<CPubKey, KeyOriginInfo> hd_keypaths;
|
||||
try {
|
||||
DeserializeHDKeypaths(random_data_stream, key, hd_keypaths);
|
||||
|
||||
@@ -70,9 +70,9 @@ template<typename B = uint8_t>
|
||||
return BytesToBits(ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline CDataStream ConsumeDataStream(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length = std::nullopt) noexcept
|
||||
[[nodiscard]] inline DataStream ConsumeDataStream(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length = std::nullopt) noexcept
|
||||
{
|
||||
return CDataStream{ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length), SER_NETWORK, INIT_PROTO_VERSION};
|
||||
return DataStream{ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length)};
|
||||
}
|
||||
|
||||
[[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
|
||||
@@ -96,6 +96,23 @@ template <typename T>
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
[[nodiscard]] P ConsumeDeserializationParams(FuzzedDataProvider& fuzzed_data_provider) noexcept;
|
||||
|
||||
template <typename T, typename P>
|
||||
[[nodiscard]] std::optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_data_provider, const P& params, const std::optional<size_t>& max_length = std::nullopt) noexcept
|
||||
{
|
||||
const std::vector<uint8_t> buffer{ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length)};
|
||||
DataStream ds{buffer};
|
||||
T obj;
|
||||
try {
|
||||
ds >> WithParams(params, obj);
|
||||
} catch (const std::ios_base::failure&) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] inline std::optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length = std::nullopt) noexcept
|
||||
{
|
||||
|
||||
@@ -55,6 +55,27 @@ CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept
|
||||
return {ConsumeService(fuzzed_data_provider), ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS), NodeSeconds{std::chrono::seconds{fuzzed_data_provider.ConsumeIntegral<uint32_t>()}}};
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
P ConsumeDeserializationParams(FuzzedDataProvider& fuzzed_data_provider) noexcept
|
||||
{
|
||||
constexpr std::array ADDR_ENCODINGS{
|
||||
CNetAddr::Encoding::V1,
|
||||
CNetAddr::Encoding::V2,
|
||||
};
|
||||
constexpr std::array ADDR_FORMATS{
|
||||
CAddress::Format::Disk,
|
||||
CAddress::Format::Network,
|
||||
};
|
||||
if constexpr (std::is_same_v<P, CNetAddr::SerParams>) {
|
||||
return P{PickValue(fuzzed_data_provider, ADDR_ENCODINGS)};
|
||||
}
|
||||
if constexpr (std::is_same_v<P, CAddress::SerParams>) {
|
||||
return P{{PickValue(fuzzed_data_provider, ADDR_ENCODINGS)}, PickValue(fuzzed_data_provider, ADDR_FORMATS)};
|
||||
}
|
||||
}
|
||||
template CNetAddr::SerParams ConsumeDeserializationParams(FuzzedDataProvider&) noexcept;
|
||||
template CAddress::SerParams ConsumeDeserializationParams(FuzzedDataProvider&) noexcept;
|
||||
|
||||
FuzzedSock::FuzzedSock(FuzzedDataProvider& fuzzed_data_provider)
|
||||
: m_fuzzed_data_provider{fuzzed_data_provider}, m_selectable{fuzzed_data_provider.ConsumeBool()}
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user