Branch data Line data Source code
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 : 1 : void initialize()
30 : : {
31 : 1 : static const auto testing_setup = MakeNoLogFileContext<TestingSetup>(
32 [ + - + - : 1 : /*chain_type=*/ChainType::REGTEST);
+ - ]
33 : 1 : g_setup = testing_setup.get();
34 : 1 : }
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 [ + - ]: 474 : FUZZ_TARGET(p2p_private_broadcast, .init = ::initialize)
47 : : {
48 : 0 : SeedRandomStateForTest(SeedRand::ZEROS);
49 : 0 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
50 : :
51 : 0 : auto& node{g_setup->m_node};
52 : 0 : auto& connman{static_cast<ConnmanTestMsg&>(*node.connman)};
53 : 0 : connman.Reset();
54 : 0 : auto& chainman{static_cast<TestChainstateManager&>(*node.chainman)};
55 : :
56 : 0 : FakeNodeClock clock_ctx{1610000000s};
57 [ # # ]: 0 : FakeSteadyClock steady_clock;
58 [ # # ]: 0 : chainman.ResetIbd();
59 : : // Sometimes leave IBD: incoming TX processing (the broadcast-abort path)
60 : : // returns early during IBD.
61 [ # # # # ]: 0 : if (fuzzed_data_provider.ConsumeBool()) chainman.JumpOutOfIbd();
62 : :
63 : : // Reset, so that dangling pointers can be detected by sanitizers.
64 [ # # ]: 0 : node.banman.reset();
65 [ # # ]: 0 : node.addrman.reset();
66 [ # # ]: 0 : node.peerman.reset();
67 : 0 : node.addrman = std::make_unique<AddrMan>(
68 [ # # ]: 0 : *node.netgroupman, /*deterministic=*/true, /*consistency_check_ratio=*/0);
69 : 0 : node.peerman = PeerManager::make(connman, *node.addrman,
70 : : /*banman=*/nullptr, chainman,
71 [ # # ]: 0 : *node.mempool, *node.warnings,
72 : : PeerManager::Options{
73 : : .reconcile_txs = true,
74 : : .deterministic_rng = true,
75 : 0 : });
76 [ # # ]: 0 : connman.SetMsgProc(node.peerman.get());
77 : 0 : 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 : 0 : const int num_txs{fuzzed_data_provider.ConsumeIntegralInRange(0, 3)};
82 : 0 : std::vector<CTransactionRef> seeded_txs;
83 [ # # ]: 0 : for (int i = 0; i < num_txs; ++i) {
84 [ # # ]: 0 : auto tx{MakeTransactionRef(ConsumeTransaction(fuzzed_data_provider, /*prevout_txids=*/std::nullopt))};
85 [ # # ]: 0 : (void)node.peerman->InitiateTxBroadcastPrivate(tx);
86 [ # # ]: 0 : seeded_txs.push_back(tx);
87 : 0 : }
88 : :
89 [ # # ]: 0 : LOCK(NetEventsInterface::g_msgproc_mutex);
90 : :
91 : 0 : static NodeId node_id{0};
92 : : // Create at least one PRIVATE_BROADCAST peer, optionally add others of random types.
93 : 0 : std::vector<CNode*> peers;
94 : :
95 : 0 : CNode* pb_node = new CNode(
96 : : /*id=*/node_id++,
97 [ # # # # ]: 0 : /*sock=*/std::make_shared<FuzzedSock>(fuzzed_data_provider, steady_clock),
98 [ # # ]: 0 : /*addrIn=*/ConsumeAddress(fuzzed_data_provider),
99 : : /*nKeyedNetGroupIn=*/0,
100 : : /*nLocalHostNonceIn=*/0,
101 [ # # ]: 0 : /*addrBindIn=*/CService{},
102 : 0 : /*addrNameIn=*/"",
103 : : /*conn_type_in=*/ConnectionType::PRIVATE_BROADCAST,
104 : : /*inbound_onion=*/false,
105 [ # # # # : 0 : /*network_key=*/0);
# # # # ]
106 : :
107 [ # # ]: 0 : peers.push_back(pb_node);
108 [ # # ]: 0 : 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 [ # # ]: 0 : connman.SetCaptureMessages(true);
112 [ # # ]: 0 : const auto CaptureMessageOrig = CaptureMessage;
113 : 0 : const CAddress pb_addr = pb_node->addr;
114 : 0 : std::optional<uint64_t> pb_ping_nonce;
115 : 0 : CaptureMessage = [&](const CAddress& addr, const std::string& msg_type,
116 : : std::span<const unsigned char> data, bool is_incoming) {
117 [ # # # # ]: 0 : if (is_incoming || addr != pb_addr) return;
118 [ # # ]: 0 : if (msg_type == NetMsgType::PING) {
119 [ # # ]: 0 : Assert(data.size() == sizeof(uint64_t));
120 : 0 : uint64_t nonce;
121 : 0 : SpanReader{data} >> nonce;
122 : 0 : pb_ping_nonce = nonce;
123 : 0 : return;
124 : : }
125 [ # # ]: 0 : if (msg_type == NetMsgType::VERSION) {
126 : 0 : SpanReader ds{data};
127 : 0 : int32_t version;
128 : 0 : uint64_t my_services, your_services, my_services_dup, nonce;
129 : 0 : int64_t my_time;
130 [ # # ]: 0 : CService your_addr, my_addr;
131 [ # # ]: 0 : std::string user_agent;
132 : 0 : int32_t height;
133 : 0 : bool relay;
134 [ # # # # : 0 : ds >> version >> my_services >> my_time >>
# # # # ]
135 [ # # # # ]: 0 : your_services >> CNetAddr::V1(your_addr) >>
136 [ # # # # ]: 0 : my_services_dup >> CNetAddr::V1(my_addr) >>
137 [ # # # # : 0 : nonce >> user_agent >> height >> relay;
# # ]
138 [ # # ]: 0 : Assert(version == WTXID_RELAY_VERSION);
139 [ # # # # : 0 : Assert(my_services == NODE_NONE && my_services_dup == NODE_NONE);
# # ]
140 [ # # ]: 0 : Assert(my_time == 0);
141 [ # # ]: 0 : Assert(your_services == NODE_NONE);
142 [ # # # # : 0 : Assert(your_addr == CService{});
# # ]
143 [ # # ]: 0 : Assert(user_agent == "/pynode:0.0.1/");
144 [ # # ]: 0 : Assert(height == 0);
145 [ # # ]: 0 : Assert(!relay);
146 : 0 : return;
147 : 0 : }
148 [ # # ]: 0 : if (msg_type != NetMsgType::INV) return;
149 : 0 : SpanReader ds{data};
150 : 0 : std::vector<CInv> invs;
151 [ # # ]: 0 : ds >> invs;
152 [ # # # # ]: 0 : Assert(invs.size() == 1);
153 [ # # ]: 0 : Assert(invs[0].IsMsgTx());
154 : 0 : };
155 : :
156 : : // Complete handshake so PushPrivateBroadcastTx runs.
157 [ # # ]: 0 : connman.Handshake(
158 : : /*node=*/*pb_node,
159 : : /*successfully_connected=*/true,
160 : : /*remote_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS),
161 : : /*local_services=*/NODE_NONE,
162 : : /*version=*/PROTOCOL_VERSION,
163 : : /*relay_txs=*/true);
164 : :
165 : : // Optionally add extra peers of random connection types.
166 : 0 : const int extra_peers{fuzzed_data_provider.ConsumeIntegralInRange(0, 2)};
167 [ # # ]: 0 : for (int i = 0; i < extra_peers; ++i) {
168 : 0 : 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 [ # # # # ]: 0 : if (extra_peer->addr == pb_addr) continue;
172 [ # # ]: 0 : peers.push_back(extra_peer.release());
173 [ # # ]: 0 : connman.AddTestNode(*peers.back());
174 : 0 : node.peerman->InitializeNode(
175 [ # # ]: 0 : *peers.back(),
176 : 0 : static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()));
177 : 0 : }
178 : :
179 [ # # # # ]: 0 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100)
180 : : {
181 : : // Pick any random peer to test interleaved message handling.
182 : 0 : CNode& p2p_node = *PickValue(fuzzed_data_provider, peers);
183 [ # # ]: 0 : if (p2p_node.fDisconnect) continue;
184 : :
185 [ # # ]: 0 : clock_ctx += ConsumeDuration<std::chrono::seconds>(fuzzed_data_provider, 0s, 600s);
186 : :
187 : 0 : std::optional<CSerializedNetMsg> net_msg;
188 [ # # ]: 0 : CallOneOf(
189 : : fuzzed_data_provider,
190 : 0 : [&] {
191 : 0 : net_msg.emplace();
192 : 0 : net_msg->m_type = std::string{PickValue(fuzzed_data_provider, INBOUND_MSG_TYPES)};
193 : 0 : },
194 : 0 : [&] {
195 : 0 : net_msg.emplace();
196 : 0 : net_msg->m_type = fuzzed_data_provider.ConsumeRandomLengthString(CMessageHeader::MESSAGE_TYPE_SIZE);
197 : 0 : },
198 : 0 : [&] {
199 : 0 : (void)node.peerman->InitiateTxBroadcastPrivate(
200 [ # # # # ]: 0 : MakeTransactionRef(ConsumeTransaction(fuzzed_data_provider, /*prevout_txids=*/std::nullopt)));
201 : 0 : },
202 : 0 : [&] {
203 : : // Construct a valid GETDATA for a seeded tx to exercise the TX send path.
204 [ # # # # ]: 0 : if (p2p_node.IsPrivateBroadcastConn() &&
205 [ # # # # ]: 0 : p2p_node.fSuccessfullyConnected &&
206 [ # # ]: 0 : !seeded_txs.empty()) {
207 : 0 : const auto& tx{PickValue(fuzzed_data_provider, seeded_txs)};
208 [ # # ]: 0 : net_msg.emplace(NetMsg::Make(
209 [ # # ]: 0 : NetMsgType::GETDATA,
210 : 0 : std::vector<CInv>{{MSG_TX, tx->GetHash().ToUint256()}}));
211 : : }
212 : 0 : },
213 : 0 : [&] {
214 : : // Confirm reception of the pushed TX with a PONG matching the captured PING nonce.
215 [ # # # # ]: 0 : if (&p2p_node == pb_node && pb_ping_nonce) {
216 [ # # ]: 0 : net_msg.emplace(NetMsg::Make(NetMsgType::PONG, *pb_ping_nonce));
217 : : }
218 : 0 : },
219 : 0 : [&] {
220 : : // Echo a seeded tx back from a non-private-broadcast peer to exercise
221 : : // the received-from-network broadcast-abort path.
222 [ # # # # ]: 0 : if (!p2p_node.IsPrivateBroadcastConn() &&
223 [ # # # # ]: 0 : p2p_node.fSuccessfullyConnected &&
224 [ # # ]: 0 : !seeded_txs.empty()) {
225 : 0 : const auto& tx{PickValue(fuzzed_data_provider, seeded_txs)};
226 [ # # ]: 0 : net_msg.emplace(NetMsg::Make(NetMsgType::TX, TX_WITH_WITNESS(*tx)));
227 : : }
228 : 0 : });
229 : :
230 [ # # ]: 0 : if (net_msg) {
231 [ # # ]: 0 : if (net_msg->data.empty()) {
232 : 0 : net_msg->data = ConsumeRandomLengthByteVector(fuzzed_data_provider, MAX_PROTOCOL_MESSAGE_LENGTH);
233 : : }
234 [ # # ]: 0 : connman.FlushSendBuffer(p2p_node);
235 [ # # ]: 0 : (void)connman.ReceiveMsgFrom(p2p_node, std::move(*net_msg));
236 : :
237 : : bool more_work{true};
238 [ # # ]: 0 : while (more_work) {
239 [ # # ]: 0 : p2p_node.fPauseSend = false;
240 : 0 : try {
241 [ # # ]: 0 : more_work = connman.ProcessMessagesOnce(p2p_node);
242 [ - - ]: 0 : } catch (const std::ios_base::failure&) {
243 : 0 : }
244 [ # # ]: 0 : node.peerman->SendMessages(p2p_node);
245 : : }
246 : : }
247 : 0 : }
248 : :
249 [ # # ]: 0 : CaptureMessage = CaptureMessageOrig;
250 [ # # ]: 0 : connman.SetCaptureMessages(false);
251 : :
252 [ # # ]: 0 : node.connman->StopNodes();
253 [ # # ]: 0 : }
|