Skip to content

Commit 9e9104f

Browse files
committed
fuzz: add raw deserialization targets for SV2 message types
Cover all client-to-TP message types that accept untrusted input: SetupConnection, RequestTransactionData, SubmitSolution, CoinbaseOutputConstraints, NetHeader, and NetMsg. Types with both Serialize and Unserialize include roundtrip invariant checks.
1 parent 7575210 commit 9e9104f

2 files changed

Lines changed: 118 additions & 0 deletions

File tree

src/test/fuzz/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ target_sources(fuzz
99
PRIVATE
1010
fuzz.cpp
1111
check_globals.cpp
12+
sv2_messages.cpp
1213
sv2_noise.cpp
1314
../sv2_test_setup.cpp
1415
)

src/test/fuzz/sv2_messages.cpp

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright (c) 2026-present The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <sv2/messages.h>
6+
#include <streams.h>
7+
#include <test/fuzz/fuzz.h>
8+
#include <test/fuzz/sv2_fuzz_util.h>
9+
10+
#include <cstdint>
11+
#include <vector>
12+
13+
using node::Sv2MsgType;
14+
using node::Sv2NetHeader;
15+
using node::Sv2NetMsg;
16+
using node::Sv2SetupConnectionMsg;
17+
using node::Sv2CoinbaseOutputConstraintsMsg;
18+
using node::Sv2RequestTransactionDataMsg;
19+
using node::Sv2SubmitSolutionMsg;
20+
21+
// Feed arbitrary bytes into a deserializer.
22+
// Sanitizers (ASan/UBSan/MSan) catch memory errors along the way.
23+
template <typename T>
24+
static void FuzzDeserialize(FuzzBufferType buffer)
25+
{
26+
DataStream ds{buffer};
27+
try {
28+
T msg;
29+
ds >> msg;
30+
} catch (const std::ios_base::failure&) {
31+
}
32+
}
33+
34+
// Deserialize, then verify the roundtrip invariant: serialize the
35+
// result and deserialize again. Only for types with both Serialize
36+
// and Unserialize.
37+
template <typename T>
38+
static void FuzzDeserializeRoundtrip(FuzzBufferType buffer)
39+
{
40+
DataStream ds{buffer};
41+
try {
42+
T msg;
43+
ds >> msg;
44+
45+
DataStream rt{};
46+
rt << msg;
47+
T msg2;
48+
rt >> msg2;
49+
} catch (const std::ios_base::failure&) {
50+
}
51+
}
52+
53+
// Client -> TP messages: these arrive over the network from untrusted
54+
// peers and are the primary deserialization attack surface.
55+
// These types have Unserialize only (no Serialize).
56+
57+
FUZZ_TARGET(sv2_setup_connection_raw, .init = Sv2FuzzInitialize)
58+
{
59+
FuzzDeserialize<Sv2SetupConnectionMsg>(buffer);
60+
}
61+
62+
FUZZ_TARGET(sv2_request_transaction_data_raw, .init = Sv2FuzzInitialize)
63+
{
64+
FuzzDeserialize<Sv2RequestTransactionDataMsg>(buffer);
65+
}
66+
67+
FUZZ_TARGET(sv2_submit_solution_raw, .init = Sv2FuzzInitialize)
68+
{
69+
FuzzDeserialize<Sv2SubmitSolutionMsg>(buffer);
70+
}
71+
72+
// CoinbaseOutputConstraints has both Serialize and Unserialize,
73+
// and uses catch(...) for the optional sigops field -- roundtrip it.
74+
FUZZ_TARGET(sv2_coinbase_output_constraints_raw, .init = Sv2FuzzInitialize)
75+
{
76+
FuzzDeserializeRoundtrip<Sv2CoinbaseOutputConstraintsMsg>(buffer);
77+
}
78+
79+
// Sv2NetHeader uses a 24-bit little-endian length encoding and ignores
80+
// a 2-byte extension type prefix -- unusual parsing worth fuzzing.
81+
FUZZ_TARGET(sv2_net_header_raw, .init = Sv2FuzzInitialize)
82+
{
83+
DataStream ds{buffer};
84+
try {
85+
Sv2NetHeader hdr;
86+
ds >> hdr;
87+
88+
// Roundtrip
89+
DataStream rt{};
90+
rt << hdr;
91+
Sv2NetHeader hdr2;
92+
rt >> hdr2;
93+
assert(hdr.m_msg_type == hdr2.m_msg_type);
94+
assert(hdr.m_msg_len == hdr2.m_msg_len);
95+
} catch (const std::ios_base::failure&) {
96+
}
97+
}
98+
99+
// Sv2NetMsg::Unserialize calls m_msg.resize(s.size()) which allocates
100+
// based on remaining stream size -- test with arbitrary input lengths.
101+
FUZZ_TARGET(sv2_net_msg_raw, .init = Sv2FuzzInitialize)
102+
{
103+
DataStream ds{buffer};
104+
try {
105+
Sv2NetMsg msg(Sv2MsgType::SETUP_CONNECTION, {});
106+
ds >> msg;
107+
108+
// Roundtrip
109+
DataStream rt{};
110+
rt << msg;
111+
Sv2NetMsg msg2(Sv2MsgType::SETUP_CONNECTION, {});
112+
rt >> msg2;
113+
assert(msg.m_msg_type == msg2.m_msg_type);
114+
assert(msg.m_msg == msg2.m_msg);
115+
} catch (const std::ios_base::failure&) {
116+
}
117+
}

0 commit comments

Comments
 (0)