/root/bitcoin/src/test/fuzz/p2p_private_broadcast.cpp
Line | Count | Source |
1 | | // Copyright (c) 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 <banman.h> |
6 | | #include <net.h> |
7 | | #include <net_processing.h> |
8 | | #include <protocol.h> |
9 | | #include <sync.h> |
10 | | #include <test/fuzz/FuzzedDataProvider.h> |
11 | | #include <test/fuzz/fuzz.h> |
12 | | #include <test/fuzz/util.h> |
13 | | #include <test/fuzz/util/net.h> |
14 | | #include <test/util/net.h> |
15 | | #include <test/util/setup_common.h> |
16 | | #include <test/util/time.h> |
17 | | #include <test/util/validation.h> |
18 | | #include <util/time.h> |
19 | | #include <validationinterface.h> |
20 | | |
21 | | #include <array> |
22 | | #include <ios> |
23 | | #include <memory> |
24 | | #include <vector> |
25 | | |
26 | | namespace { |
27 | | TestingSetup* g_setup; |
28 | | |
29 | | void initialize() |
30 | 0 | { |
31 | 0 | static const auto testing_setup = MakeNoLogFileContext<TestingSetup>( |
32 | 0 | /*chain_type=*/ChainType::REGTEST); |
33 | 0 | g_setup = testing_setup.get(); |
34 | 0 | } |
35 | | |
36 | | // Inbound message types with private broadcast specific handling. |
37 | | // Used as the guided path in the CallOneOf() below. |
38 | | constexpr std::array INBOUND_MSG_TYPES{ |
39 | | NetMsgType::VERSION, |
40 | | NetMsgType::VERACK, |
41 | | NetMsgType::GETDATA, |
42 | | NetMsgType::PONG, |
43 | | }; |
44 | | } // namespace |
45 | | |
46 | | FUZZ_TARGET(p2p_private_broadcast, .init = ::initialize) |
47 | 3.11k | { |
48 | 3.11k | SeedRandomStateForTest(SeedRand::ZEROS); |
49 | 3.11k | FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); |
50 | | |
51 | 3.11k | auto& node{g_setup->m_node}; |
52 | 3.11k | auto& connman{static_cast<ConnmanTestMsg&>(*node.connman)}; |
53 | 3.11k | connman.Reset(); |
54 | 3.11k | auto& chainman{static_cast<TestChainstateManager&>(*node.chainman)}; |
55 | | |
56 | 3.11k | FakeNodeClock clock_ctx{1610000000s}; // 2021-01-07, arbitrary |
57 | 3.11k | FakeSteadyClock steady_clock; |
58 | 3.11k | chainman.ResetIbd(); |
59 | | // Sometimes leave IBD: incoming TX processing (the broadcast-abort path) |
60 | | // returns early during IBD. |
61 | 3.11k | if (fuzzed_data_provider.ConsumeBool()) chainman.JumpOutOfIbd(); Branch (61:9): [True: 2.57k, False: 543]
|
62 | | |
63 | | // Reset, so that dangling pointers can be detected by sanitizers. |
64 | 3.11k | node.banman.reset(); |
65 | 3.11k | node.addrman.reset(); |
66 | 3.11k | node.peerman.reset(); |
67 | 3.11k | node.addrman = std::make_unique<AddrMan>( |
68 | 3.11k | *node.netgroupman, /*deterministic=*/true, /*consistency_check_ratio=*/0); |
69 | 3.11k | node.peerman = PeerManager::make(connman, *node.addrman, |
70 | 3.11k | /*banman=*/nullptr, chainman, |
71 | 3.11k | *node.mempool, *node.warnings, |
72 | 3.11k | PeerManager::Options{ |
73 | 3.11k | .reconcile_txs = true, |
74 | 3.11k | .deterministic_rng = true, |
75 | 3.11k | }); |
76 | 3.11k | connman.SetMsgProc(node.peerman.get()); |
77 | 3.11k | connman.SetAddrman(*node.addrman); |
78 | | |
79 | | // Seed with 0-3 transactions to test multiple pending broadcasts; zero exercises |
80 | | // the connected-in-vain disconnect in PushPrivateBroadcastTx(). |
81 | 3.11k | const int num_txs{fuzzed_data_provider.ConsumeIntegralInRange(0, 3)}; |
82 | 3.11k | std::vector<CTransactionRef> seeded_txs; |
83 | 6.70k | for (int i = 0; i < num_txs; ++i) { Branch (83:21): [True: 3.59k, False: 3.11k]
|
84 | 3.59k | auto tx{MakeTransactionRef(ConsumeTransaction(fuzzed_data_provider, /*prevout_txids=*/std::nullopt))}; |
85 | 3.59k | (void)node.peerman->InitiateTxBroadcastPrivate(tx); |
86 | 3.59k | seeded_txs.push_back(tx); |
87 | 3.59k | } |
88 | | |
89 | 3.11k | LOCK(NetEventsInterface::g_msgproc_mutex); |
90 | | |
91 | 3.11k | static NodeId node_id{0}; |
92 | | // Create at least one PRIVATE_BROADCAST peer, optionally add others of random types. |
93 | 3.11k | std::vector<CNode*> peers; |
94 | | |
95 | 3.11k | CNode* pb_node = new CNode( |
96 | 3.11k | /*id=*/node_id++, |
97 | 3.11k | /*sock=*/std::make_shared<FuzzedSock>(fuzzed_data_provider, steady_clock), |
98 | 3.11k | /*addrIn=*/ConsumeAddress(fuzzed_data_provider), |
99 | 3.11k | /*nKeyedNetGroupIn=*/0, |
100 | 3.11k | /*nLocalHostNonceIn=*/0, |
101 | 3.11k | /*addrBindIn=*/CService{}, |
102 | 3.11k | /*addrNameIn=*/"", |
103 | 3.11k | /*conn_type_in=*/ConnectionType::PRIVATE_BROADCAST, |
104 | 3.11k | /*inbound_onion=*/false, |
105 | 3.11k | /*network_key=*/0); |
106 | | |
107 | 3.11k | peers.push_back(pb_node); |
108 | 3.11k | connman.AddTestNode(*pb_node); |
109 | | // Capture outbound messages to verify if well formed (and to learn the PING |
110 | | // nonce), before SocketSendData drains vSendMsg. |
111 | 3.11k | connman.SetCaptureMessages(true); |
112 | 3.11k | const auto CaptureMessageOrig = CaptureMessage; |
113 | 3.11k | const CAddress pb_addr = pb_node->addr; |
114 | 3.11k | std::optional<uint64_t> pb_ping_nonce; |
115 | 3.11k | CaptureMessage = [&](const CAddress& addr, const std::string& msg_type, |
116 | 38.4k | std::span<const unsigned char> data, bool is_incoming) { |
117 | 38.4k | if (is_incoming || addr != pb_addr) return; Branch (117:13): [True: 0, False: 38.4k]
Branch (117:28): [True: 30.5k, False: 7.95k]
|
118 | 7.95k | if (msg_type == NetMsgType::PING) { Branch (118:13): [True: 316, False: 7.63k]
|
119 | 316 | Assert(data.size() == sizeof(uint64_t)); |
120 | 316 | uint64_t nonce; |
121 | 316 | SpanReader{data} >> nonce; |
122 | 316 | pb_ping_nonce = nonce; |
123 | 316 | return; |
124 | 316 | } |
125 | 7.63k | if (msg_type == NetMsgType::VERSION) { Branch (125:13): [True: 3.11k, False: 4.52k]
|
126 | 3.11k | SpanReader ds{data}; |
127 | 3.11k | int32_t version; |
128 | 3.11k | uint64_t my_services, your_services, my_services_dup, nonce; |
129 | 3.11k | int64_t my_time; |
130 | 3.11k | CService your_addr, my_addr; |
131 | 3.11k | std::string user_agent; |
132 | 3.11k | int32_t height; |
133 | 3.11k | bool relay; |
134 | 3.11k | ds >> version >> my_services >> my_time >> |
135 | 3.11k | your_services >> CNetAddr::V1(your_addr) >> |
136 | 3.11k | my_services_dup >> CNetAddr::V1(my_addr) >> |
137 | 3.11k | nonce >> user_agent >> height >> relay; |
138 | 3.11k | Assert(version == WTXID_RELAY_VERSION); |
139 | 3.11k | Assert(my_services == NODE_NONE && my_services_dup == NODE_NONE); |
140 | 3.11k | Assert(my_time == 0); |
141 | 3.11k | Assert(your_services == NODE_NONE); |
142 | 3.11k | Assert(your_addr == CService{}); |
143 | 3.11k | Assert(user_agent == "/pynode:0.0.1/"); |
144 | 3.11k | Assert(height == 0); |
145 | 3.11k | Assert(!relay); |
146 | 3.11k | return; |
147 | 3.11k | } |
148 | 4.52k | if (msg_type != NetMsgType::INV) return; Branch (148:13): [True: 2.96k, False: 1.55k]
|
149 | 1.55k | SpanReader ds{data}; |
150 | 1.55k | std::vector<CInv> invs; |
151 | 1.55k | ds >> invs; |
152 | 1.55k | Assert(invs.size() == 1); |
153 | 1.55k | Assert(invs[0].IsMsgTx()); |
154 | 1.55k | }; |
155 | | |
156 | | // Complete handshake so PushPrivateBroadcastTx runs. |
157 | 3.11k | connman.Handshake( |
158 | 3.11k | /*node=*/*pb_node, |
159 | 3.11k | /*successfully_connected=*/true, |
160 | 3.11k | /*remote_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS), |
161 | 3.11k | /*local_services=*/NODE_NONE, |
162 | 3.11k | /*version=*/PROTOCOL_VERSION, |
163 | 3.11k | /*relay_txs=*/true); |
164 | | |
165 | | // Optionally add extra peers of random connection types. |
166 | 3.11k | const int extra_peers{fuzzed_data_provider.ConsumeIntegralInRange(0, 2)}; |
167 | 6.07k | for (int i = 0; i < extra_peers; ++i) { Branch (167:21): [True: 2.96k, False: 3.11k]
|
168 | 2.96k | auto extra_peer{ConsumeNodeAsUniquePtr(fuzzed_data_provider, steady_clock, node_id++)}; |
169 | | // An address collision would match the capture hook's filter and fail |
170 | | // its assertions on this peer's (legitimate) other-typed messages. |
171 | 2.96k | if (extra_peer->addr == pb_addr) continue; Branch (171:13): [True: 4, False: 2.95k]
|
172 | 2.95k | peers.push_back(extra_peer.release()); |
173 | 2.95k | connman.AddTestNode(*peers.back()); |
174 | 2.95k | node.peerman->InitializeNode( |
175 | 2.95k | *peers.back(), |
176 | 2.95k | static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>())); |
177 | 2.95k | } |
178 | | |
179 | 3.11k | LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) |
180 | 57.8k | { |
181 | | // Pick any random peer to test interleaved message handling. |
182 | 57.8k | CNode& p2p_node = *PickValue(fuzzed_data_provider, peers); |
183 | 57.8k | if (p2p_node.fDisconnect) continue; Branch (183:13): [True: 2.64k, False: 55.2k]
|
184 | | |
185 | 55.2k | clock_ctx += ConsumeDuration<std::chrono::seconds>(fuzzed_data_provider, 0s, 600s); |
186 | | |
187 | 55.2k | std::optional<CSerializedNetMsg> net_msg; |
188 | 55.2k | CallOneOf( |
189 | 55.2k | fuzzed_data_provider, |
190 | 55.2k | [&] { |
191 | 6.31k | net_msg.emplace(); |
192 | 6.31k | net_msg->m_type = std::string{PickValue(fuzzed_data_provider, INBOUND_MSG_TYPES)}; |
193 | 6.31k | }, |
194 | 55.2k | [&] { |
195 | 16.9k | net_msg.emplace(); |
196 | 16.9k | net_msg->m_type = fuzzed_data_provider.ConsumeRandomLengthString(CMessageHeader::MESSAGE_TYPE_SIZE); |
197 | 16.9k | }, |
198 | 55.2k | [&] { |
199 | 654 | (void)node.peerman->InitiateTxBroadcastPrivate( |
200 | 654 | MakeTransactionRef(ConsumeTransaction(fuzzed_data_provider, /*prevout_txids=*/std::nullopt))); |
201 | 654 | }, |
202 | 55.2k | [&] { |
203 | | // Construct a valid GETDATA for a seeded tx to exercise the TX send path. |
204 | 13.8k | if (p2p_node.IsPrivateBroadcastConn() && Branch (204:21): [True: 874, False: 12.9k]
|
205 | 13.8k | p2p_node.fSuccessfullyConnected && Branch (205:21): [True: 351, False: 523]
|
206 | 13.8k | !seeded_txs.empty()) { Branch (206:21): [True: 351, False: 0]
|
207 | 351 | const auto& tx{PickValue(fuzzed_data_provider, seeded_txs)}; |
208 | 351 | net_msg.emplace(NetMsg::Make( |
209 | 351 | NetMsgType::GETDATA, |
210 | 351 | std::vector<CInv>{{MSG_TX, tx->GetHash().ToUint256()}})); |
211 | 351 | } |
212 | 13.8k | }, |
213 | 55.2k | [&] { |
214 | | // Confirm reception of the pushed TX with a PONG matching the captured PING nonce. |
215 | 285 | if (&p2p_node == pb_node && pb_ping_nonce) { Branch (215:21): [True: 78, False: 207]
Branch (215:45): [True: 9, False: 69]
|
216 | 9 | net_msg.emplace(NetMsg::Make(NetMsgType::PONG, *pb_ping_nonce)); |
217 | 9 | } |
218 | 285 | }, |
219 | 55.2k | [&] { |
220 | | // Echo a seeded tx back from a non-private-broadcast peer to exercise |
221 | | // the received-from-network broadcast-abort path. |
222 | 17.2k | if (!p2p_node.IsPrivateBroadcastConn() && Branch (222:21): [True: 16.6k, False: 566]
|
223 | 17.2k | p2p_node.fSuccessfullyConnected && Branch (223:21): [True: 13.7k, False: 2.91k]
|
224 | 17.2k | !seeded_txs.empty()) { Branch (224:21): [True: 10.4k, False: 3.32k]
|
225 | 10.4k | const auto& tx{PickValue(fuzzed_data_provider, seeded_txs)}; |
226 | 10.4k | net_msg.emplace(NetMsg::Make(NetMsgType::TX, TX_WITH_WITNESS(*tx))); |
227 | 10.4k | } |
228 | 17.2k | }); |
229 | | |
230 | 55.2k | if (net_msg) { Branch (230:13): [True: 34.0k, False: 21.2k]
|
231 | 34.0k | if (net_msg->data.empty()) { Branch (231:17): [True: 23.2k, False: 10.7k]
|
232 | 23.2k | net_msg->data = ConsumeRandomLengthByteVector(fuzzed_data_provider, MAX_PROTOCOL_MESSAGE_LENGTH); |
233 | 23.2k | } |
234 | 34.0k | connman.FlushSendBuffer(p2p_node); |
235 | | |
236 | | // ConsumeTransaction() can produce messages larger than the |
237 | | // maximum payload accepted by the P2P transport. |
238 | 34.0k | if (net_msg->data.size() > MAX_PROTOCOL_MESSAGE_LENGTH) continue; Branch (238:17): [True: 0, False: 34.0k]
|
239 | | |
240 | 34.0k | (void)connman.ReceiveMsgFrom(p2p_node, std::move(*net_msg)); |
241 | | |
242 | 34.0k | bool more_work{true}; |
243 | 70.0k | while (more_work) { Branch (243:20): [True: 36.0k, False: 34.0k]
|
244 | 36.0k | p2p_node.fPauseSend = false; |
245 | 36.0k | try { |
246 | 36.0k | more_work = connman.ProcessMessagesOnce(p2p_node); |
247 | 36.0k | } catch (const std::ios_base::failure&) { |
248 | 0 | } |
249 | 36.0k | node.peerman->SendMessages(p2p_node); |
250 | 36.0k | } |
251 | 34.0k | } |
252 | 55.2k | } |
253 | | |
254 | 3.11k | CaptureMessage = CaptureMessageOrig; |
255 | 3.11k | connman.SetCaptureMessages(false); |
256 | | |
257 | 3.11k | node.connman->StopNodes(); |
258 | 3.11k | } |