Branch data Line data Source code
1 : : // Copyright (c) 2021-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 <consensus/amount.h>
6 : : #include <pubkey.h>
7 : : #include <test/fuzz/util.h>
8 : : #include <test/util/script.h>
9 : : #include <util/check.h>
10 : : #include <util/overflow.h>
11 : : #include <util/rbf.h>
12 : : #include <util/time.h>
13 : :
14 : : #include <memory>
15 : :
16 : 3311852 : std::vector<uint8_t> ConstructPubKeyBytes(FuzzedDataProvider& fuzzed_data_provider, std::span<const uint8_t> byte_data, const bool compressed) noexcept
17 : : {
18 : 3311852 : uint8_t pk_type;
19 [ + + ]: 3311852 : if (compressed) {
20 : 2973812 : pk_type = fuzzed_data_provider.PickValueInArray({0x02, 0x03});
21 : : } else {
22 : 338040 : pk_type = fuzzed_data_provider.PickValueInArray({0x04, 0x06, 0x07});
23 : : }
24 [ + + ]: 3649892 : std::vector<uint8_t> pk_data{byte_data.begin(), byte_data.begin() + (compressed ? CPubKey::COMPRESSED_SIZE : CPubKey::SIZE)};
25 : 3311852 : pk_data[0] = pk_type;
26 : 3311852 : return pk_data;
27 : : }
28 : :
29 : 2883225 : CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider, const std::optional<CAmount>& max) noexcept
30 : : {
31 [ + + ]: 5237897 : return fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, max.value_or(MAX_MONEY));
32 : : }
33 : :
34 : 1497623 : NodeSeconds ConsumeTime(FuzzedDataProvider& fuzzed_data_provider, const std::optional<int64_t>& min, const std::optional<int64_t>& max) noexcept
35 : : {
36 : : // Avoid t=0 (1970-01-01T00:00:00Z) since SetMockTime(0) disables mocktime.
37 [ + + + - : 1497623 : static const int64_t time_min{ParseISO8601DateTime("2000-01-01T00:00:01Z").value()};
+ - ]
38 [ + + + - : 1497623 : static const int64_t time_max{ParseISO8601DateTime("2100-12-31T23:59:59Z").value()};
+ - ]
39 [ + + + + ]: 4492869 : return NodeSeconds{ConsumeDuration(fuzzed_data_provider, min.value_or(time_min) * 1s, max.value_or(time_max) * 1s)};
40 : : }
41 : :
42 : 1513860 : std::chrono::seconds ConsumeDuration(FuzzedDataProvider& fuzzed_data_provider, std::chrono::seconds min, std::chrono::seconds max) noexcept
43 : : {
44 : 1513860 : return 1s * fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(min.count(), max.count());
45 : : }
46 : :
47 : 410388 : CMutableTransaction ConsumeTransaction(FuzzedDataProvider& fuzzed_data_provider, const std::optional<std::vector<Txid>>& prevout_txids, const int max_num_in, const int max_num_out) noexcept
48 : : {
49 : 410388 : CMutableTransaction tx_mut;
50 : 410388 : const auto p2wsh_op_true = fuzzed_data_provider.ConsumeBool();
51 [ + + ]: 410388 : tx_mut.version = fuzzed_data_provider.ConsumeBool() ?
52 : : CTransaction::CURRENT_VERSION :
53 : 46163 : fuzzed_data_provider.ConsumeIntegral<uint32_t>();
54 : 410388 : tx_mut.nLockTime = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
55 : 410388 : const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, max_num_in);
56 : 410388 : const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, max_num_out);
57 [ + + ]: 1047654 : for (int i = 0; i < num_in; ++i) {
58 [ + + ]: 637266 : const auto& txid_prev = prevout_txids ?
59 : 634777 : PickValue(fuzzed_data_provider, *prevout_txids) :
60 : 637266 : Txid::FromUint256(ConsumeUInt256(fuzzed_data_provider));
61 : 637266 : const auto index_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, max_num_out);
62 : 637266 : const auto sequence = ConsumeSequence(fuzzed_data_provider);
63 [ + + ]: 637266 : const auto script_sig = p2wsh_op_true ? CScript{} : ConsumeScript(fuzzed_data_provider);
64 : 637266 : CScriptWitness script_wit;
65 [ + + ]: 637266 : if (p2wsh_op_true) {
66 [ + + ]: 1095908 : script_wit.stack = std::vector<std::vector<uint8_t>>{WITNESS_STACK_ELEM_OP_TRUE};
67 : : } else {
68 : 89312 : script_wit = ConsumeScriptWitness(fuzzed_data_provider);
69 : : }
70 : 637266 : CTxIn in;
71 : 637266 : in.prevout = COutPoint{txid_prev, index_out};
72 : 637266 : in.nSequence = sequence;
73 : 637266 : in.scriptSig = script_sig;
74 : 637266 : in.scriptWitness = script_wit;
75 : :
76 : 637266 : tx_mut.vin.push_back(in);
77 : 637266 : }
78 [ + + ]: 1093483 : for (int i = 0; i < num_out; ++i) {
79 : 683095 : const auto amount = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-10, 50 * COIN + 10);
80 [ + + ]: 683095 : const auto script_pk = p2wsh_op_true ?
81 : : P2WSH_OP_TRUE :
82 : 683095 : ConsumeScript(fuzzed_data_provider, /*maybe_p2wsh=*/true);
83 : 683095 : tx_mut.vout.emplace_back(amount, script_pk);
84 : 683095 : }
85 : 410388 : return tx_mut;
86 : 547954 : }
87 : :
88 : 89312 : CScriptWitness ConsumeScriptWitness(FuzzedDataProvider& fuzzed_data_provider, const size_t max_stack_elem_size) noexcept
89 : : {
90 : 89312 : CScriptWitness ret;
91 : 89312 : const auto n_elements = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_stack_elem_size);
92 [ + + ]: 239190 : for (size_t i = 0; i < n_elements; ++i) {
93 : 149878 : ret.stack.push_back(ConsumeRandomLengthByteVector(fuzzed_data_provider));
94 : : }
95 : 89312 : return ret;
96 : : }
97 : :
98 : 998117 : CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const bool maybe_p2wsh) noexcept
99 : : {
100 : 998117 : CScript r_script{};
101 : 998117 : {
102 : : // Keep a buffer of bytes to allow the fuzz engine to produce smaller
103 : : // inputs to generate CScripts with repeated data.
104 : 998117 : static constexpr unsigned MAX_BUFFER_SZ{128};
105 : 998117 : std::vector<uint8_t> buffer(MAX_BUFFER_SZ, uint8_t{'a'});
106 [ + + ]: 40610113 : while (fuzzed_data_provider.ConsumeBool()) {
107 : 38613879 : CallOneOf(
108 : : fuzzed_data_provider,
109 : 9383534 : [&] {
110 : : // Insert byte vector directly to allow malformed or unparsable scripts
111 : 9383534 : r_script.insert(r_script.end(), buffer.begin(), buffer.begin() + fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BUFFER_SZ));
112 : 9383534 : },
113 : 26644946 : [&] {
114 : : // Push a byte vector from the buffer
115 : 53289892 : r_script << std::vector<uint8_t>{buffer.begin(), buffer.begin() + fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BUFFER_SZ)};
116 : 26644946 : },
117 : 363254 : [&] {
118 : : // Push multisig
119 : : // There is a special case for this to aid the fuzz engine
120 : : // navigate the highly structured multisig format.
121 : 363254 : r_script << fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 22);
122 : 363254 : int num_data{fuzzed_data_provider.ConsumeIntegralInRange(1, 22)};
123 [ + + ]: 4030564 : while (num_data--) {
124 [ - + ]: 3304056 : auto pubkey_bytes{ConstructPubKeyBytes(fuzzed_data_provider, buffer, fuzzed_data_provider.ConsumeBool())};
125 [ + + ]: 3304056 : if (fuzzed_data_provider.ConsumeBool()) {
126 : 2978483 : pubkey_bytes.back() = num_data; // Make each pubkey different
127 : : }
128 [ - + ]: 3304056 : r_script << pubkey_bytes;
129 : 3304056 : }
130 : 363254 : r_script << fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 22);
131 : 363254 : },
132 : 253708 : [&] {
133 : : // Mutate the buffer
134 : 253708 : const auto vec{ConsumeRandomLengthByteVector(fuzzed_data_provider, /*max_length=*/MAX_BUFFER_SZ)};
135 : 253708 : std::copy(vec.begin(), vec.end(), buffer.begin());
136 : 253708 : },
137 : 744580 : [&] {
138 : : // Push an integral
139 : 744580 : r_script << fuzzed_data_provider.ConsumeIntegral<int64_t>();
140 : 744580 : },
141 : 872791 : [&] {
142 : : // Push an opcode
143 : 872791 : r_script << ConsumeOpcodeType(fuzzed_data_provider);
144 : 872791 : },
145 : 351066 : [&] {
146 : : // Push a scriptnum
147 : 351066 : r_script << ConsumeScriptNum(fuzzed_data_provider);
148 : 351066 : });
149 : : }
150 : 998117 : }
151 [ + + + + ]: 1101088 : if (maybe_p2wsh && fuzzed_data_provider.ConsumeBool()) {
152 : 33714 : uint256 script_hash;
153 [ + + + + ]: 77509 : CSHA256().Write(r_script.data(), r_script.size()).Finalize(script_hash.begin());
154 : 33714 : r_script.clear();
155 [ - + ]: 33714 : r_script << OP_0 << ToByteVector(script_hash);
156 : : }
157 : 998117 : return r_script;
158 : : }
159 : :
160 : 19856874 : uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept
161 : : {
162 [ + + ]: 19856874 : return fuzzed_data_provider.ConsumeBool() ?
163 : 8657052 : fuzzed_data_provider.PickValueInArray({
164 : : CTxIn::SEQUENCE_FINAL,
165 : : CTxIn::MAX_SEQUENCE_NONFINAL,
166 : : MAX_BIP125_RBF_SEQUENCE,
167 : : }) :
168 : 11199822 : fuzzed_data_provider.ConsumeIntegral<uint32_t>();
169 : : }
170 : :
171 : 12815 : std::map<COutPoint, Coin> ConsumeCoins(FuzzedDataProvider& fuzzed_data_provider) noexcept
172 : : {
173 : 12815 : std::map<COutPoint, Coin> coins;
174 [ + + + - ]: 58059 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
175 : 48652 : const std::optional<COutPoint> outpoint{ConsumeDeserializable<COutPoint>(fuzzed_data_provider)};
176 [ + + ]: 48652 : if (!outpoint) {
177 : : break;
178 : : }
179 : 46626 : const std::optional<Coin> coin{ConsumeDeserializable<Coin>(fuzzed_data_provider)};
180 [ + + ]: 46626 : if (!coin) {
181 : : break;
182 : : }
183 : 45244 : coins[*outpoint] = *coin;
184 : 46626 : }
185 : :
186 : 12815 : return coins;
187 : : }
188 : :
189 : 53047 : CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept
190 : : {
191 : 53047 : CTxDestination tx_destination;
192 : 53047 : const size_t call_size{CallOneOf(
193 : : fuzzed_data_provider,
194 : 7524 : [&] {
195 : 7524 : tx_destination = CNoDestination{};
196 : 7524 : },
197 : 7796 : [&] {
198 : 7796 : bool compressed = fuzzed_data_provider.ConsumeBool();
199 [ - + ]: 15592 : CPubKey pk{ConstructPubKeyBytes(
200 : : fuzzed_data_provider,
201 [ + + - + ]: 12078 : ConsumeFixedLengthByteVector(fuzzed_data_provider, (compressed ? CPubKey::COMPRESSED_SIZE : CPubKey::SIZE)),
202 : : compressed
203 : 7796 : )};
204 : 7796 : tx_destination = PubKeyDestination{pk};
205 : 7796 : },
206 : 3314 : [&] {
207 : 3314 : tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)};
208 : 3314 : },
209 : 3136 : [&] {
210 : 3136 : tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)};
211 : 3136 : },
212 : 7200 : [&] {
213 : 7200 : tx_destination = WitnessV0ScriptHash{ConsumeUInt256(fuzzed_data_provider)};
214 : 7200 : },
215 : 2734 : [&] {
216 : 2734 : tx_destination = WitnessV0KeyHash{ConsumeUInt160(fuzzed_data_provider)};
217 : 2734 : },
218 : 8630 : [&] {
219 : 8630 : tx_destination = WitnessV1Taproot{XOnlyPubKey{ConsumeUInt256(fuzzed_data_provider)}};
220 : 8630 : },
221 : 2022 : [&] {
222 : 2022 : tx_destination = PayToAnchor{};
223 : 2022 : },
224 : 10691 : [&] {
225 : 10691 : std::vector<unsigned char> program{ConsumeRandomLengthByteVector(fuzzed_data_provider, /*max_length=*/40)};
226 [ - + + + ]: 10691 : if (program.size() < 2) {
227 [ + - ]: 3149 : program = {0, 0};
228 : : }
229 [ + - ]: 10691 : tx_destination = WitnessUnknown{fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(2, 16), program};
230 : 10691 : })};
231 [ - + ]: 53047 : Assert(call_size == std::variant_size_v<CTxDestination>);
232 : 53047 : return tx_destination;
233 : : }
234 : :
235 : 54710 : CKey ConsumePrivateKey(FuzzedDataProvider& fuzzed_data_provider, std::optional<bool> compressed) noexcept
236 : : {
237 : 54710 : auto key_data = fuzzed_data_provider.ConsumeBytes<uint8_t>(32);
238 : 54710 : key_data.resize(32);
239 : 54710 : CKey key;
240 [ + + ]: 101244 : bool compressed_value = compressed ? *compressed : fuzzed_data_provider.ConsumeBool();
241 : 54710 : key.Set(key_data.begin(), key_data.end(), compressed_value);
242 : 54710 : return key;
243 : 54710 : }
244 : :
245 : 4306 : bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept
246 : : {
247 [ + + ]: 4306 : for (const CTxIn& tx_in : tx.vin) {
248 : 19 : const Coin& coin = inputs.AccessCoin(tx_in.prevout);
249 [ - + ]: 19 : if (coin.IsSpent()) {
250 : : return true;
251 : : }
252 : : }
253 : : return false;
254 : : }
255 : :
256 : 8107 : FILE* FuzzedFileProvider::open()
257 : : {
258 : 8107 : SetFuzzedErrNo(m_fuzzed_data_provider);
259 [ + + ]: 8107 : if (m_fuzzed_data_provider.ConsumeBool()) {
260 : : return nullptr;
261 : : }
262 [ + - ]: 7803 : std::string mode;
263 [ + - ]: 7803 : CallOneOf(
264 : : m_fuzzed_data_provider,
265 : 5874 : [&] {
266 : 5874 : mode = "r";
267 : 5874 : },
268 : 721 : [&] {
269 : 721 : mode = "r+";
270 : 721 : },
271 : 159 : [&] {
272 : 159 : mode = "w";
273 : 159 : },
274 : 549 : [&] {
275 : 549 : mode = "w+";
276 : 549 : },
277 : 87 : [&] {
278 : 87 : mode = "a";
279 : 87 : },
280 : 413 : [&] {
281 : 413 : mode = "a+";
282 : 413 : });
283 : : #if defined _GNU_SOURCE && (defined(__linux__) || defined(__FreeBSD__))
284 : 7803 : const cookie_io_functions_t io_hooks = {
285 : : FuzzedFileProvider::read,
286 : : FuzzedFileProvider::write,
287 : : FuzzedFileProvider::seek,
288 : : FuzzedFileProvider::close,
289 : : };
290 : 7803 : return fopencookie(this, mode.c_str(), io_hooks);
291 : : #else
292 : : (void)mode;
293 : : return nullptr;
294 : : #endif
295 : 7803 : }
296 : :
297 : 52082 : ssize_t FuzzedFileProvider::read(void* cookie, char* buf, size_t size)
298 : : {
299 : 52082 : FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
300 : 52082 : SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
301 [ + - + + ]: 52082 : if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) {
302 [ + + ]: 8417 : return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
303 : : }
304 : 43665 : const std::vector<uint8_t> random_bytes = fuzzed_file->m_fuzzed_data_provider.ConsumeBytes<uint8_t>(size);
305 [ + + ]: 43665 : if (random_bytes.empty()) {
306 : : return 0;
307 : : }
308 [ - + - + ]: 40245 : std::memcpy(buf, random_bytes.data(), random_bytes.size());
309 [ - + - + ]: 40245 : if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)random_bytes.size())) {
310 [ # # ]: 0 : return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
311 : : }
312 [ - + ]: 40245 : fuzzed_file->m_offset += random_bytes.size();
313 [ - + ]: 43665 : return random_bytes.size();
314 : 43665 : }
315 : :
316 : 3885 : ssize_t FuzzedFileProvider::write(void* cookie, const char* buf, size_t size)
317 : : {
318 : 3885 : FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
319 : 3885 : SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
320 : 3885 : const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(0, size);
321 [ + - ]: 3885 : if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)n)) {
322 : : return 0;
323 : : }
324 : 3885 : fuzzed_file->m_offset += n;
325 : 3885 : return n;
326 : : }
327 : :
328 : 8463 : int FuzzedFileProvider::seek(void* cookie, int64_t* offset, int whence)
329 : : {
330 [ - + ]: 8463 : assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END);
331 : 8463 : FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
332 : 8463 : SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
333 : 8463 : int64_t new_offset = 0;
334 [ - + ]: 8463 : if (whence == SEEK_SET) {
335 : 0 : new_offset = *offset;
336 [ + - ]: 8463 : } else if (whence == SEEK_CUR) {
337 [ + - ]: 8463 : if (AdditionOverflow(fuzzed_file->m_offset, *offset)) {
338 : : return -1;
339 : : }
340 : 8463 : new_offset = fuzzed_file->m_offset + *offset;
341 [ # # ]: 0 : } else if (whence == SEEK_END) {
342 : 0 : const int64_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 4096);
343 [ # # ]: 0 : if (AdditionOverflow(n, *offset)) {
344 : : return -1;
345 : : }
346 : 0 : new_offset = n + *offset;
347 : : }
348 [ + + ]: 8463 : if (new_offset < 0) {
349 : : return -1;
350 : : }
351 : 8268 : fuzzed_file->m_offset = new_offset;
352 : 8268 : *offset = new_offset;
353 : 8268 : return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
354 : : }
355 : :
356 : 7803 : int FuzzedFileProvider::close(void* cookie)
357 : : {
358 : 7803 : FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
359 : 7803 : SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
360 : 7803 : return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
361 : : }
|