/root/bitcoin/src/test/fuzz/util/net.h
Line | Count | Source |
1 | | // Copyright (c) 2009-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 | | #ifndef BITCOIN_TEST_FUZZ_UTIL_NET_H |
6 | | #define BITCOIN_TEST_FUZZ_UTIL_NET_H |
7 | | |
8 | | #include <addrman.h> |
9 | | #include <addrman_impl.h> |
10 | | #include <net.h> |
11 | | #include <net_permissions.h> |
12 | | #include <netaddress.h> |
13 | | #include <node/connection_types.h> |
14 | | #include <node/eviction.h> |
15 | | #include <protocol.h> |
16 | | #include <sync.h> |
17 | | #include <test/fuzz/FuzzedDataProvider.h> |
18 | | #include <test/fuzz/util.h> |
19 | | #include <test/util/net.h> |
20 | | #include <util/asmap.h> |
21 | | #include <util/sock.h> |
22 | | |
23 | | #include <chrono> |
24 | | #include <cstddef> |
25 | | #include <cstdint> |
26 | | #include <limits> |
27 | | #include <memory> |
28 | | #include <optional> |
29 | | #include <string> |
30 | | |
31 | | /** |
32 | | * Create a CNetAddr. It may have `addr.IsValid() == false`. |
33 | | * @param[in,out] fuzzed_data_provider Take data for the address from this, if `rand` is `nullptr`. |
34 | | * @param[in,out] rand If not nullptr, take data from it instead of from `fuzzed_data_provider`. |
35 | | * Prefer generating addresses using `fuzzed_data_provider` because it is not uniform. Only use |
36 | | * `rand` if `fuzzed_data_provider` is exhausted or its data is needed for other things. |
37 | | * @return a "random" network address. |
38 | | */ |
39 | | CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider, FastRandomContext* rand = nullptr) noexcept; |
40 | | |
41 | | class AddrManDeterministic : public AddrMan |
42 | | { |
43 | | public: |
44 | | explicit AddrManDeterministic(const NetGroupManager& netgroupman, FuzzedDataProvider& fuzzed_data_provider, int32_t check_ratio) |
45 | 0 | : AddrMan(netgroupman, /*deterministic=*/true, check_ratio) |
46 | 0 | { |
47 | 0 | WITH_LOCK(m_impl->cs, m_impl->insecure_rand.Reseed(ConsumeUInt256(fuzzed_data_provider))); |
48 | 0 | } |
49 | | |
50 | | /** |
51 | | * Compare with another AddrMan. |
52 | | * This compares: |
53 | | * - the values in `mapInfo` (the keys aka ids are ignored) |
54 | | * - vvNew entries refer to the same addresses |
55 | | * - vvTried entries refer to the same addresses |
56 | | */ |
57 | | bool operator==(const AddrManDeterministic& other) const |
58 | 0 | { |
59 | 0 | LOCK2(m_impl->cs, other.m_impl->cs); |
60 | |
|
61 | 0 | if (m_impl->mapInfo.size() != other.m_impl->mapInfo.size() || m_impl->nNew != other.m_impl->nNew || Branch (61:13): [True: 0, False: 0]
Branch (61:71): [True: 0, False: 0]
|
62 | 0 | m_impl->nTried != other.m_impl->nTried) { Branch (62:13): [True: 0, False: 0]
|
63 | 0 | return false; |
64 | 0 | } |
65 | | |
66 | | // Check that all values in `mapInfo` are equal to all values in `other.mapInfo`. |
67 | | // Keys may be different. |
68 | | |
69 | 0 | auto addrinfo_hasher = [](const AddrInfo& a) { |
70 | 0 | CSipHasher hasher(0, 0); |
71 | 0 | auto addr_key = a.GetKey(); |
72 | 0 | auto source_key = a.source.GetAddrBytes(); |
73 | 0 | hasher.Write(TicksSinceEpoch<std::chrono::seconds>(a.m_last_success)); |
74 | 0 | hasher.Write(a.nAttempts); |
75 | 0 | hasher.Write(a.nRefCount); |
76 | 0 | hasher.Write(a.fInTried); |
77 | 0 | hasher.Write(a.GetNetwork()); |
78 | 0 | hasher.Write(a.source.GetNetwork()); |
79 | 0 | hasher.Write(addr_key.size()); |
80 | 0 | hasher.Write(source_key.size()); |
81 | 0 | hasher.Write(addr_key); |
82 | 0 | hasher.Write(source_key); |
83 | 0 | return (size_t)hasher.Finalize(); |
84 | 0 | }; |
85 | |
|
86 | 0 | auto addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) { |
87 | 0 | return std::tie(static_cast<const CService&>(lhs), lhs.source, lhs.m_last_success, lhs.nAttempts, lhs.nRefCount, lhs.fInTried) == |
88 | 0 | std::tie(static_cast<const CService&>(rhs), rhs.source, rhs.m_last_success, rhs.nAttempts, rhs.nRefCount, rhs.fInTried); |
89 | 0 | }; |
90 | |
|
91 | 0 | using Addresses = std::unordered_set<AddrInfo, decltype(addrinfo_hasher), decltype(addrinfo_eq)>; |
92 | |
|
93 | 0 | const size_t num_addresses{m_impl->mapInfo.size()}; |
94 | |
|
95 | 0 | Addresses addresses{num_addresses, addrinfo_hasher, addrinfo_eq}; |
96 | 0 | for (const auto& [id, addr] : m_impl->mapInfo) { Branch (96:37): [True: 0, False: 0]
|
97 | 0 | addresses.insert(addr); |
98 | 0 | } |
99 | |
|
100 | 0 | Addresses other_addresses{num_addresses, addrinfo_hasher, addrinfo_eq}; |
101 | 0 | for (const auto& [id, addr] : other.m_impl->mapInfo) { Branch (101:37): [True: 0, False: 0]
|
102 | 0 | other_addresses.insert(addr); |
103 | 0 | } |
104 | |
|
105 | 0 | if (addresses != other_addresses) { Branch (105:13): [True: 0, False: 0]
|
106 | 0 | return false; |
107 | 0 | } |
108 | | |
109 | 0 | auto IdsReferToSameAddress = [&](nid_type id, nid_type other_id) EXCLUSIVE_LOCKS_REQUIRED(m_impl->cs, other.m_impl->cs) { |
110 | 0 | if (id == -1 && other_id == -1) { Branch (110:17): [True: 0, False: 0]
Branch (110:29): [True: 0, False: 0]
|
111 | 0 | return true; |
112 | 0 | } |
113 | 0 | if ((id == -1 && other_id != -1) || (id != -1 && other_id == -1)) { Branch (113:18): [True: 0, False: 0]
Branch (113:30): [True: 0, False: 0]
Branch (113:50): [True: 0, False: 0]
Branch (113:62): [True: 0, False: 0]
|
114 | 0 | return false; |
115 | 0 | } |
116 | 0 | return m_impl->mapInfo.at(id) == other.m_impl->mapInfo.at(other_id); |
117 | 0 | }; |
118 | | |
119 | | // Check that `vvNew` contains the same addresses as `other.vvNew`. Notice - `vvNew[i][j]` |
120 | | // contains just an id and the address is to be found in `mapInfo.at(id)`. The ids |
121 | | // themselves may differ between `vvNew` and `other.vvNew`. |
122 | 0 | for (size_t i = 0; i < ADDRMAN_NEW_BUCKET_COUNT; ++i) { Branch (122:28): [True: 0, False: 0]
|
123 | 0 | for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) { Branch (123:32): [True: 0, False: 0]
|
124 | 0 | if (!IdsReferToSameAddress(m_impl->vvNew[i][j], other.m_impl->vvNew[i][j])) { Branch (124:21): [True: 0, False: 0]
|
125 | 0 | return false; |
126 | 0 | } |
127 | 0 | } |
128 | 0 | } |
129 | | |
130 | | // Same for `vvTried`. |
131 | 0 | for (size_t i = 0; i < ADDRMAN_TRIED_BUCKET_COUNT; ++i) { Branch (131:28): [True: 0, False: 0]
|
132 | 0 | for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) { Branch (132:32): [True: 0, False: 0]
|
133 | 0 | if (!IdsReferToSameAddress(m_impl->vvTried[i][j], other.m_impl->vvTried[i][j])) { Branch (133:21): [True: 0, False: 0]
|
134 | 0 | return false; |
135 | 0 | } |
136 | 0 | } |
137 | 0 | } |
138 | | |
139 | 0 | return true; |
140 | 0 | } |
141 | | }; |
142 | | |
143 | | class FuzzedNetEvents : public NetEventsInterface |
144 | | { |
145 | | public: |
146 | 0 | FuzzedNetEvents(FuzzedDataProvider& fdp) : m_fdp(fdp) {} |
147 | | |
148 | 0 | virtual void InitializeNode(const CNode&, ServiceFlags) override {} |
149 | | |
150 | 0 | virtual void FinalizeNode(const CNode&) override {} |
151 | | |
152 | 0 | virtual bool HasAllDesirableServiceFlags(ServiceFlags) const override { return m_fdp.ConsumeBool(); } |
153 | | |
154 | 0 | virtual bool ProcessMessages(CNode&, std::atomic<bool>&) override { return m_fdp.ConsumeBool(); } |
155 | | |
156 | 0 | virtual bool SendMessages(CNode&) override { return m_fdp.ConsumeBool(); } |
157 | | |
158 | | private: |
159 | | FuzzedDataProvider& m_fdp; |
160 | | }; |
161 | | |
162 | | class FuzzedSock : public Sock |
163 | | { |
164 | | FuzzedDataProvider& m_fuzzed_data_provider; |
165 | | |
166 | | /** |
167 | | * Data to return when `MSG_PEEK` is used as a `Recv()` flag. |
168 | | * If `MSG_PEEK` is used, then our `Recv()` returns some random data as usual, but on the next |
169 | | * `Recv()` call we must return the same data, thus we remember it here. |
170 | | */ |
171 | | mutable std::vector<uint8_t> m_peek_data; |
172 | | |
173 | | /** |
174 | | * Whether to pretend that the socket is select(2)-able. This is randomly set in the |
175 | | * constructor. It should remain constant so that repeated calls to `IsSelectable()` |
176 | | * return the same value. |
177 | | */ |
178 | | const bool m_selectable; |
179 | | |
180 | | /** |
181 | | * Used to mock the steady clock in methods waiting for a given duration. |
182 | | */ |
183 | | mutable std::chrono::milliseconds m_time; |
184 | | |
185 | | /** |
186 | | * Set the value of the mocked steady clock such as that many ms have passed. |
187 | | */ |
188 | | void ElapseTime(std::chrono::milliseconds duration) const; |
189 | | |
190 | | public: |
191 | | explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider); |
192 | | |
193 | | ~FuzzedSock() override; |
194 | | |
195 | | FuzzedSock& operator=(Sock&& other) override; |
196 | | |
197 | | ssize_t Send(const void* data, size_t len, int flags) const override; |
198 | | |
199 | | ssize_t Recv(void* buf, size_t len, int flags) const override; |
200 | | |
201 | | int Connect(const sockaddr*, socklen_t) const override; |
202 | | |
203 | | int Bind(const sockaddr*, socklen_t) const override; |
204 | | |
205 | | int Listen(int backlog) const override; |
206 | | |
207 | | std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const override; |
208 | | |
209 | | int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override; |
210 | | |
211 | | int SetSockOpt(int level, int opt_name, const void* opt_val, socklen_t opt_len) const override; |
212 | | |
213 | | int GetSockName(sockaddr* name, socklen_t* name_len) const override; |
214 | | |
215 | | bool SetNonBlocking() const override; |
216 | | |
217 | | bool IsSelectable() const override; |
218 | | |
219 | | bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override; |
220 | | |
221 | | bool WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const override; |
222 | | |
223 | | bool IsConnected(std::string& errmsg) const override; |
224 | | }; |
225 | | |
226 | | [[nodiscard]] inline FuzzedNetEvents ConsumeNetEvents(FuzzedDataProvider& fdp) noexcept |
227 | 0 | { |
228 | 0 | return FuzzedNetEvents{fdp}; |
229 | 0 | } |
230 | | |
231 | | [[nodiscard]] inline FuzzedSock ConsumeSock(FuzzedDataProvider& fuzzed_data_provider) |
232 | 0 | { |
233 | 0 | return FuzzedSock{fuzzed_data_provider}; |
234 | 0 | } |
235 | | |
236 | | [[nodiscard]] inline NetGroupManager ConsumeNetGroupManager(FuzzedDataProvider& fuzzed_data_provider) noexcept |
237 | 0 | { |
238 | 0 | std::vector<std::byte> asmap{ConsumeRandomLengthByteVector<std::byte>(fuzzed_data_provider)}; |
239 | 0 | if (!CheckStandardAsmap(asmap)) { Branch (239:9): [True: 0, False: 0]
|
240 | 0 | return NetGroupManager::NoAsmap(); |
241 | 0 | } |
242 | 0 | return NetGroupManager::WithLoadedAsmap(std::move(asmap)); |
243 | 0 | } |
244 | | |
245 | | inline CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept |
246 | 0 | { |
247 | 0 | return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint8_t>()}; |
248 | 0 | } |
249 | | |
250 | | inline CService ConsumeService(FuzzedDataProvider& fuzzed_data_provider) noexcept |
251 | 0 | { |
252 | 0 | return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint16_t>()}; |
253 | 0 | } |
254 | | |
255 | | inline std::vector<CService> ConsumeServiceVector(FuzzedDataProvider& fuzzed_data_provider, |
256 | | size_t max_vector_size = 5) noexcept |
257 | 0 | { |
258 | 0 | std::vector<CService> ret; |
259 | 0 | const size_t size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size); |
260 | 0 | ret.reserve(size); |
261 | 0 | for (size_t i = 0; i < size; ++i) { Branch (261:24): [True: 0, False: 0]
|
262 | 0 | ret.emplace_back(ConsumeService(fuzzed_data_provider)); |
263 | 0 | } |
264 | 0 | return ret; |
265 | 0 | } |
266 | | |
267 | | CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept; |
268 | | |
269 | | template <bool ReturnUniquePtr = false> |
270 | | auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<NodeId>& node_id_in = std::nullopt) noexcept |
271 | 0 | { |
272 | 0 | const NodeId node_id = node_id_in.value_or(fuzzed_data_provider.ConsumeIntegralInRange<NodeId>(0, std::numeric_limits<NodeId>::max())); |
273 | 0 | const auto sock = std::make_shared<FuzzedSock>(fuzzed_data_provider); |
274 | 0 | const CAddress address = ConsumeAddress(fuzzed_data_provider); |
275 | 0 | const uint64_t keyed_net_group = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); |
276 | 0 | const uint64_t local_host_nonce = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); |
277 | 0 | const CAddress addr_bind = ConsumeAddress(fuzzed_data_provider); |
278 | 0 | const std::string addr_name = fuzzed_data_provider.ConsumeRandomLengthString(64); |
279 | 0 | const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray(ALL_CONNECTION_TYPES); |
280 | 0 | const bool inbound_onion{conn_type == ConnectionType::INBOUND ? fuzzed_data_provider.ConsumeBool() : false}; Branch (280:30): [True: 0, False: 0]
Branch (280:30): [True: 0, False: 0]
|
281 | 0 | const uint64_t network_id = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); |
282 | |
|
283 | 0 | NetPermissionFlags permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS); |
284 | 0 | if constexpr (ReturnUniquePtr) { |
285 | 0 | return std::make_unique<CNode>(node_id, |
286 | 0 | sock, |
287 | 0 | address, |
288 | 0 | keyed_net_group, |
289 | 0 | local_host_nonce, |
290 | 0 | addr_bind, |
291 | 0 | addr_name, |
292 | 0 | conn_type, |
293 | 0 | inbound_onion, |
294 | 0 | network_id, |
295 | 0 | CNodeOptions{ .permission_flags = permission_flags }); |
296 | 0 | } else { |
297 | 0 | return CNode{node_id, |
298 | 0 | sock, |
299 | 0 | address, |
300 | 0 | keyed_net_group, |
301 | 0 | local_host_nonce, |
302 | 0 | addr_bind, |
303 | 0 | addr_name, |
304 | 0 | conn_type, |
305 | 0 | inbound_onion, |
306 | 0 | network_id, |
307 | 0 | CNodeOptions{ .permission_flags = permission_flags }}; |
308 | 0 | } |
309 | 0 | } Unexecuted instantiation: _Z11ConsumeNodeILb1EEDaR18FuzzedDataProviderRKSt8optionalIlE Unexecuted instantiation: _Z11ConsumeNodeILb0EEDaR18FuzzedDataProviderRKSt8optionalIlE |
310 | 0 | inline std::unique_ptr<CNode> ConsumeNodeAsUniquePtr(FuzzedDataProvider& fdp, const std::optional<NodeId>& node_id_in = std::nullopt) { return ConsumeNode<true>(fdp, node_id_in); } |
311 | | |
312 | | void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman, CNode& node) noexcept EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex); |
313 | | |
314 | | #endif // BITCOIN_TEST_FUZZ_UTIL_NET_H |