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