Skip to content

Commit

Permalink
Add roundtrip fuzz tests for CAddress serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
sipa committed Nov 30, 2020
1 parent 0de10ed commit 80f5c54
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 14 deletions.
26 changes: 20 additions & 6 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ FUZZ_TARGETS = \
test/fuzz/addition_overflow \
test/fuzz/addr_info_deserialize \
test/fuzz/addrdb \
test/fuzz/address_deserialize \
test/fuzz/address_v1_notime_deserialize \
test/fuzz/address_v1_withtime_deserialize \
test/fuzz/address_v2_deserialize \
test/fuzz/addrman \
test/fuzz/addrman_deserialize \
test/fuzz/asmap \
Expand Down Expand Up @@ -352,11 +354,23 @@ test_fuzz_addrdb_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_addrdb_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_addrdb_SOURCES = test/fuzz/addrdb.cpp

test_fuzz_address_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDRESS_DESERIALIZE=1
test_fuzz_address_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_address_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_address_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_address_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_address_v1_notime_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDRESS_V1_NOTIME_DESERIALIZE=1
test_fuzz_address_v1_notime_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_address_v1_notime_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_address_v1_notime_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_address_v1_notime_deserialize_SOURCES = test/fuzz/deserialize.cpp

test_fuzz_address_v1_withtime_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDRESS_V1_WITHTIME_DESERIALIZE=1
test_fuzz_address_v1_withtime_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_address_v1_withtime_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_address_v1_withtime_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_address_v1_withtime_deserialize_SOURCES = test/fuzz/deserialize.cpp

test_fuzz_address_v2_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDRESS_V2_DESERIALIZE=1
test_fuzz_address_v2_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_address_v2_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_address_v2_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_address_v2_deserialize_SOURCES = test/fuzz/deserialize.cpp

test_fuzz_addrman_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_addrman_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
Expand Down
7 changes: 7 additions & 0 deletions src/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,13 @@ class CAddress : public CService
uint32_t nTime{TIME_INIT};

ServiceFlags nServices{NODE_NONE};

friend bool operator==(const CAddress& a, const CAddress& b)
{
return a.nTime == b.nTime &&
a.nServices == b.nServices &&
static_cast<const CService&>(a) == static_cast<const CService&>(b);
}
};

/** getdata message type flags */
Expand Down
42 changes: 34 additions & 8 deletions src/test/fuzz/deserialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ struct invalid_fuzzing_input_exception : public std::exception {
};

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)
{
CDataStream ds(SER_NETWORK, version);
CDataStream ds(ser_type, version);
ds << obj;
return ds;
}
Expand All @@ -62,9 +62,9 @@ T Deserialize(CDataStream ds)
}

template <typename T>
void DeserializeFromFuzzingInput(const std::vector<uint8_t>& buffer, T& obj, const Optional<int> protocol_version = nullopt)
void DeserializeFromFuzzingInput(const std::vector<uint8_t>& buffer, T& obj, const Optional<int> protocol_version = nullopt, const int ser_type = SER_NETWORK)
{
CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION);
CDataStream ds(buffer, ser_type, INIT_PROTO_VERSION);
if (protocol_version) {
ds.SetVersion(*protocol_version);
} else {
Expand All @@ -85,9 +85,9 @@ void DeserializeFromFuzzingInput(const std::vector<uint8_t>& buffer, T& obj, con
}

template <typename T>
void AssertEqualAfterSerializeDeserialize(const T& obj, const int version = INIT_PROTO_VERSION)
void AssertEqualAfterSerializeDeserialize(const T& obj, const int version = INIT_PROTO_VERSION, const int ser_type = SER_NETWORK)
{
assert(Deserialize<T>(Serialize(obj, version)) == obj);
assert(Deserialize<T>(Serialize(obj, version, ser_type)) == obj);
}

} // namespace
Expand Down Expand Up @@ -217,9 +217,35 @@ void test_one_input(const std::vector<uint8_t>& buffer)
CMessageHeader mh;
DeserializeFromFuzzingInput(buffer, mh);
(void)mh.IsCommandValid();
#elif ADDRESS_DESERIALIZE
#elif ADDRESS_V1_NOTIME_DESERIALIZE
CAddress a;
DeserializeFromFuzzingInput(buffer, 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);
#elif ADDRESS_V1_WITHTIME_DESERIALIZE
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);
#elif ADDRESS_V2_DESERIALIZE
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);
}
AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
#elif INV_DESERIALIZE
CInv i;
DeserializeFromFuzzingInput(buffer, i);
Expand Down

0 comments on commit 80f5c54

Please sign in to comment.