deserialize.cpp raw
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 #include <addrdb.h>
6 #include <addrman.h>
7 #include <addrman_impl.h>
8 #include <blockencodings.h>
9 #include <blockfilter.h>
10 #include <chain.h>
11 #include <coins.h>
12 #include <common/args.h>
13 #include <compressor.h>
14 #include <consensus/merkle.h>
15 #include <key.h>
16 #include <merkleblock.h>
17 #include <net.h>
18 #include <netbase.h>
19 #include <netgroup.h>
20 #include <node/blockstorage.h>
21 #include <node/utxo_snapshot.h>
22 #include <primitives/block.h>
23 #include <protocol.h>
24 #include <psbt.h>
25 #include <pubkey.h>
26 #include <script/keyorigin.h>
27 #include <streams.h>
28 #include <test/fuzz/fuzz.h>
29 #include <test/fuzz/util.h>
30 #include <test/util/setup_common.h>
31 #include <undo.h>
32
33 #include <cstdint>
34 #include <exception>
35 #include <optional>
36 #include <stdexcept>
37
38 using kernel::CBlockFileInfo;
39 using node::SnapshotMetadata;
40
41 void initialize_deserialize()
42 {
43 static const auto testing_setup = MakeNoLogFileContext<>();
44 }
45
46 #define FUZZ_TARGET_DESERIALIZE(name, code) \
47 FUZZ_TARGET(name, .init = initialize_deserialize) \
48 { \
49 try { \
50 code \
51 } catch (const invalid_fuzzing_input_exception&) { \
52 } \
53 }
54
55 namespace {
56
57 struct invalid_fuzzing_input_exception : public std::exception {
58 };
59
60 template <typename T, typename P>
61 DataStream Serialize(const T& obj, const P& params)
62 {
63 DataStream ds{};
64 ds << params(obj);
65 return ds;
66 }
67
68 template <typename T, typename P>
69 T Deserialize(DataStream&& ds, const P& params)
70 {
71 T obj;
72 ds >> params(obj);
73 return obj;
74 }
75
76 template <typename T, typename P>
77 void DeserializeFromFuzzingInput(FuzzBufferType buffer, T&& obj, const P& params)
78 {
79 try {
80 SpanReader{buffer} >> params(obj);
81 } catch (const std::ios_base::failure&) {
82 throw invalid_fuzzing_input_exception();
83 }
84 assert(buffer.empty() || !Serialize(obj, params).empty());
85 }
86
87 template <typename T>
88 DataStream Serialize(const T& obj)
89 {
90 DataStream ds{};
91 ds << obj;
92 return ds;
93 }
94
95 template <typename T>
96 T Deserialize(DataStream ds)
97 {
98 T obj;
99 ds >> obj;
100 return obj;
101 }
102
103 template <typename T>
104 void DeserializeFromFuzzingInput(FuzzBufferType buffer, T&& obj)
105 {
106 try {
107 SpanReader{buffer} >> obj;
108 } catch (const std::ios_base::failure&) {
109 throw invalid_fuzzing_input_exception();
110 }
111 assert(buffer.empty() || !Serialize(obj).empty());
112 }
113
114 template <typename T>
115 T DeserializeConstructFromFuzzingInput(FuzzBufferType buffer)
116 {
117 try {
118 SpanReader reader{buffer};
119 T obj(deserialize, reader);
120 assert(buffer.empty() || !Serialize(obj).empty());
121 return obj;
122 } catch (const std::ios_base::failure&) {
123 throw invalid_fuzzing_input_exception();
124 }
125 }
126
127 template <typename T, typename P>
128 void AssertEqualAfterSerializeDeserialize(const T& obj, const P& params)
129 {
130 assert(Deserialize<T>(Serialize(obj, params), params) == obj);
131 }
132 template <typename T>
133 void AssertEqualAfterSerializeDeserialize(const T& obj)
134 {
135 assert(Deserialize<T>(Serialize(obj)) == obj);
136 }
137
138 } // namespace
139
140 FUZZ_TARGET_DESERIALIZE(block_filter_deserialize, {
141 BlockFilter block_filter;
142 DeserializeFromFuzzingInput(buffer, block_filter);
143 })
144 FUZZ_TARGET(addr_info_deserialize, .init = initialize_deserialize)
145 {
146 FuzzedDataProvider fdp{buffer.data(), buffer.size()};
147 (void)ConsumeDeserializable<AddrInfo>(fdp, ConsumeDeserializationParams<CAddress::SerParams>(fdp));
148 }
149 FUZZ_TARGET_DESERIALIZE(block_file_info_deserialize, {
150 CBlockFileInfo block_file_info;
151 DeserializeFromFuzzingInput(buffer, block_file_info);
152 })
153 FUZZ_TARGET_DESERIALIZE(block_header_and_short_txids_deserialize, {
154 CBlockHeaderAndShortTxIDs block_header_and_short_txids;
155 DeserializeFromFuzzingInput(buffer, block_header_and_short_txids);
156 })
157 FUZZ_TARGET_DESERIALIZE(fee_rate_deserialize, {
158 CFeeRate fee_rate;
159 DeserializeFromFuzzingInput(buffer, fee_rate);
160 AssertEqualAfterSerializeDeserialize(fee_rate);
161 })
162 FUZZ_TARGET_DESERIALIZE(merkle_block_deserialize, {
163 CMerkleBlock merkle_block;
164 DeserializeFromFuzzingInput(buffer, merkle_block);
165 })
166 FUZZ_TARGET_DESERIALIZE(out_point_deserialize, {
167 COutPoint out_point;
168 DeserializeFromFuzzingInput(buffer, out_point);
169 AssertEqualAfterSerializeDeserialize(out_point);
170 })
171 FUZZ_TARGET_DESERIALIZE(partial_merkle_tree_deserialize, {
172 CPartialMerkleTree partial_merkle_tree;
173 DeserializeFromFuzzingInput(buffer, partial_merkle_tree);
174 })
175 FUZZ_TARGET_DESERIALIZE(pub_key_deserialize, {
176 CPubKey pub_key;
177 DeserializeFromFuzzingInput(buffer, pub_key);
178 AssertEqualAfterSerializeDeserialize(pub_key);
179 })
180 FUZZ_TARGET_DESERIALIZE(script_deserialize, {
181 CScript script;
182 DeserializeFromFuzzingInput(buffer, script);
183 })
184 FUZZ_TARGET_DESERIALIZE(tx_in_deserialize, {
185 CTxIn tx_in;
186 DeserializeFromFuzzingInput(buffer, tx_in);
187 AssertEqualAfterSerializeDeserialize(tx_in);
188 })
189 FUZZ_TARGET_DESERIALIZE(flat_file_pos_deserialize, {
190 FlatFilePos flat_file_pos;
191 DeserializeFromFuzzingInput(buffer, flat_file_pos);
192 AssertEqualAfterSerializeDeserialize(flat_file_pos);
193 })
194 FUZZ_TARGET_DESERIALIZE(key_origin_info_deserialize, {
195 KeyOriginInfo key_origin_info;
196 DeserializeFromFuzzingInput(buffer, key_origin_info);
197 AssertEqualAfterSerializeDeserialize(key_origin_info);
198 })
199 FUZZ_TARGET_DESERIALIZE(partially_signed_transaction_deserialize, {
200 PartiallySignedTransaction partially_signed_transaction = DeserializeConstructFromFuzzingInput<PartiallySignedTransaction>(buffer);
201 })
202 FUZZ_TARGET_DESERIALIZE(prefilled_transaction_deserialize, {
203 PrefilledTransaction prefilled_transaction;
204 DeserializeFromFuzzingInput(buffer, prefilled_transaction);
205 })
206 FUZZ_TARGET_DESERIALIZE(psbt_input_deserialize, {
207 PSBTInput psbt_input(0, Txid{}, 0);
208 DeserializeFromFuzzingInput(buffer, psbt_input);
209 })
210 FUZZ_TARGET_DESERIALIZE(psbt_output_deserialize, {
211 PSBTOutput psbt_output(0, 0, CScript());
212 DeserializeFromFuzzingInput(buffer, psbt_output);
213 })
214 FUZZ_TARGET_DESERIALIZE(block_deserialize, {
215 CBlock block;
216 DeserializeFromFuzzingInput(buffer, TX_WITH_WITNESS(block));
217 })
218 FUZZ_TARGET_DESERIALIZE(blocklocator_deserialize, {
219 CBlockLocator bl;
220 DeserializeFromFuzzingInput(buffer, bl);
221 })
222 FUZZ_TARGET_DESERIALIZE(blockmerkleroot, {
223 CBlock block;
224 DeserializeFromFuzzingInput(buffer, TX_WITH_WITNESS(block));
225 bool mutated;
226 BlockMerkleRoot(block, &mutated);
227 })
228 FUZZ_TARGET_DESERIALIZE(blockheader_deserialize, {
229 CBlockHeader bh;
230 DeserializeFromFuzzingInput(buffer, bh);
231 })
232 FUZZ_TARGET_DESERIALIZE(txundo_deserialize, {
233 CTxUndo tu;
234 DeserializeFromFuzzingInput(buffer, tu);
235 })
236 FUZZ_TARGET_DESERIALIZE(blockundo_deserialize, {
237 CBlockUndo bu;
238 DeserializeFromFuzzingInput(buffer, bu);
239 })
240 FUZZ_TARGET_DESERIALIZE(coins_deserialize, {
241 Coin coin;
242 DeserializeFromFuzzingInput(buffer, coin);
243 })
244 FUZZ_TARGET(netaddr_deserialize, .init = initialize_deserialize)
245 {
246 FuzzedDataProvider fdp{buffer.data(), buffer.size()};
247 const auto maybe_na{ConsumeDeserializable<CNetAddr>(fdp, ConsumeDeserializationParams<CNetAddr::SerParams>(fdp))};
248 if (!maybe_na) return;
249 const CNetAddr& na{*maybe_na};
250 if (na.IsAddrV1Compatible()) {
251 AssertEqualAfterSerializeDeserialize(na, CNetAddr::V1);
252 }
253 AssertEqualAfterSerializeDeserialize(na, CNetAddr::V2);
254 }
255 FUZZ_TARGET(service_deserialize, .init = initialize_deserialize)
256 {
257 FuzzedDataProvider fdp{buffer.data(), buffer.size()};
258 const auto ser_params{ConsumeDeserializationParams<CNetAddr::SerParams>(fdp)};
259 const auto maybe_s{ConsumeDeserializable<CService>(fdp, ser_params)};
260 if (!maybe_s) return;
261 const CService& s{*maybe_s};
262 if (s.IsAddrV1Compatible()) {
263 AssertEqualAfterSerializeDeserialize(s, CNetAddr::V1);
264 }
265 AssertEqualAfterSerializeDeserialize(s, CNetAddr::V2);
266 if (ser_params.enc == CNetAddr::Encoding::V1) {
267 assert(s.IsAddrV1Compatible());
268 }
269 }
270 FUZZ_TARGET_DESERIALIZE(messageheader_deserialize, {
271 CMessageHeader mh;
272 DeserializeFromFuzzingInput(buffer, mh);
273 (void)mh.IsMessageTypeValid();
274 })
275 FUZZ_TARGET(address_deserialize, .init = initialize_deserialize)
276 {
277 FuzzedDataProvider fdp{buffer.data(), buffer.size()};
278 const auto ser_enc{ConsumeDeserializationParams<CAddress::SerParams>(fdp)};
279 const auto maybe_a{ConsumeDeserializable<CAddress>(fdp, ser_enc)};
280 if (!maybe_a) return;
281 const CAddress& a{*maybe_a};
282 // A CAddress in V1 mode will roundtrip
283 // in all 4 formats (v1/v2, network/disk)
284 if (ser_enc.enc == CNetAddr::Encoding::V1) {
285 AssertEqualAfterSerializeDeserialize(a, CAddress::V1_NETWORK);
286 AssertEqualAfterSerializeDeserialize(a, CAddress::V1_DISK);
287 AssertEqualAfterSerializeDeserialize(a, CAddress::V2_NETWORK);
288 AssertEqualAfterSerializeDeserialize(a, CAddress::V2_DISK);
289 } else {
290 // A CAddress in V2 mode will roundtrip in both V2 formats, and also in the V1 formats
291 // if it's V1 compatible.
292 if (a.IsAddrV1Compatible()) {
293 AssertEqualAfterSerializeDeserialize(a, CAddress::V1_DISK);
294 AssertEqualAfterSerializeDeserialize(a, CAddress::V1_NETWORK);
295 }
296 AssertEqualAfterSerializeDeserialize(a, CAddress::V2_NETWORK);
297 AssertEqualAfterSerializeDeserialize(a, CAddress::V2_DISK);
298 }
299 }
300 FUZZ_TARGET_DESERIALIZE(inv_deserialize, {
301 CInv i;
302 DeserializeFromFuzzingInput(buffer, i);
303 })
304 FUZZ_TARGET_DESERIALIZE(bloomfilter_deserialize, {
305 CBloomFilter bf;
306 DeserializeFromFuzzingInput(buffer, bf);
307 })
308 FUZZ_TARGET_DESERIALIZE(diskblockindex_deserialize, {
309 CDiskBlockIndex dbi;
310 DeserializeFromFuzzingInput(buffer, dbi);
311 })
312 FUZZ_TARGET_DESERIALIZE(txoutcompressor_deserialize, {
313 CTxOut to;
314 auto toc = Using<TxOutCompression>(to);
315 DeserializeFromFuzzingInput(buffer, toc);
316 })
317 FUZZ_TARGET_DESERIALIZE(blocktransactions_deserialize, {
318 BlockTransactions bt;
319 DeserializeFromFuzzingInput(buffer, bt);
320 })
321 FUZZ_TARGET_DESERIALIZE(blocktransactionsrequest_deserialize, {
322 BlockTransactionsRequest btr;
323 DeserializeFromFuzzingInput(buffer, btr);
324 })
325 FUZZ_TARGET_DESERIALIZE(snapshotmetadata_deserialize, {
326 auto msg_start = Params().MessageStart();
327 SnapshotMetadata snapshot_metadata{msg_start};
328 DeserializeFromFuzzingInput(buffer, snapshot_metadata);
329 })
330 FUZZ_TARGET_DESERIALIZE(uint160_deserialize, {
331 uint160 u160;
332 DeserializeFromFuzzingInput(buffer, u160);
333 AssertEqualAfterSerializeDeserialize(u160);
334 })
335 FUZZ_TARGET_DESERIALIZE(uint256_deserialize, {
336 uint256 u256;
337 DeserializeFromFuzzingInput(buffer, u256);
338 AssertEqualAfterSerializeDeserialize(u256);
339 })
340 // Classes intentionally not covered in this file since their deserialization code is
341 // fuzzed elsewhere:
342 // * Deserialization of CTxOut is fuzzed in test/fuzz/tx_out.cpp
343 // * Deserialization of CMutableTransaction is fuzzed in src/test/fuzz/transaction.cpp
344