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 : 2935660 : std::vector<uint8_t> ConstructPubKeyBytes(FuzzedDataProvider& fuzzed_data_provider, std::span<const uint8_t> byte_data, const bool compressed) noexcept
17 : : {
18 : 2935660 : uint8_t pk_type;
19 [ + + ]: 2935660 : if (compressed) {
20 : 2632934 : pk_type = fuzzed_data_provider.PickValueInArray({0x02, 0x03});
21 : : } else {
22 : 302726 : pk_type = fuzzed_data_provider.PickValueInArray({0x04, 0x06, 0x07});
23 : : }
24 [ + + ]: 3238386 : std::vector<uint8_t> pk_data{byte_data.begin(), byte_data.begin() + (compressed ? CPubKey::COMPRESSED_SIZE : CPubKey::SIZE)};
25 : 2935660 : pk_data[0] = pk_type;
26 : 2935660 : return pk_data;
27 : : }
28 : :
29 : 2338991 : CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider, const std::optional<CAmount>& max) noexcept
30 : : {
31 [ + + ]: 4182676 : return fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, max.value_or(MAX_MONEY));
32 : : }
33 : :
34 : 1222803 : 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 [ + + + - : 1222803 : static const int64_t time_min{ParseISO8601DateTime("2000-01-01T00:00:01Z").value()};
+ - ]
38 [ + + + - : 1222803 : static const int64_t time_max{ParseISO8601DateTime("2100-12-31T23:59:59Z").value()};
+ - ]
39 [ + + + + ]: 3668409 : return NodeSeconds{ConsumeDuration(fuzzed_data_provider, min.value_or(time_min) * 1s, max.value_or(time_max) * 1s)};
40 : : }
41 : :
42 : 1236691 : std::chrono::seconds ConsumeDuration(FuzzedDataProvider& fuzzed_data_provider, std::chrono::seconds min, std::chrono::seconds max) noexcept
43 : : {
44 : 1236691 : return 1s * fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(min.count(), max.count());
45 : : }
46 : :
47 : 255973 : 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 : 255973 : CMutableTransaction tx_mut;
50 : 255973 : const auto p2wsh_op_true = fuzzed_data_provider.ConsumeBool();
51 [ + + ]: 255973 : tx_mut.version = fuzzed_data_provider.ConsumeBool() ?
52 : : CTransaction::CURRENT_VERSION :
53 : 22611 : fuzzed_data_provider.ConsumeIntegral<uint32_t>();
54 : 255973 : tx_mut.nLockTime = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
55 : 255973 : const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, max_num_in);
56 : 255973 : const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, max_num_out);
57 [ + + ]: 661319 : for (int i = 0; i < num_in; ++i) {
58 [ + + ]: 405346 : const auto& txid_prev = prevout_txids ?
59 : 403313 : PickValue(fuzzed_data_provider, *prevout_txids) :
60 : 405346 : Txid::FromUint256(ConsumeUInt256(fuzzed_data_provider));
61 : 405346 : const auto index_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, max_num_out);
62 : 405346 : const auto sequence = ConsumeSequence(fuzzed_data_provider);
63 [ + + ]: 405346 : const auto script_sig = p2wsh_op_true ? CScript{} : ConsumeScript(fuzzed_data_provider);
64 : 405346 : CScriptWitness script_wit;
65 [ + + ]: 405346 : if (p2wsh_op_true) {
66 [ + + ]: 709674 : script_wit.stack = std::vector<std::vector<uint8_t>>{WITNESS_STACK_ELEM_OP_TRUE};
67 : : } else {
68 : 50509 : script_wit = ConsumeScriptWitness(fuzzed_data_provider);
69 : : }
70 : 405346 : CTxIn in;
71 : 405346 : in.prevout = COutPoint{txid_prev, index_out};
72 : 405346 : in.nSequence = sequence;
73 : 405346 : in.scriptSig = script_sig;
74 : 405346 : in.scriptWitness = script_wit;
75 : :
76 : 405346 : tx_mut.vin.push_back(in);
77 : 405346 : }
78 [ + + ]: 690111 : for (int i = 0; i < num_out; ++i) {
79 : 434138 : const auto amount = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-10, 50 * COIN + 10);
80 [ + + ]: 434138 : const auto script_pk = p2wsh_op_true ?
81 : : P2WSH_OP_TRUE :
82 : 434138 : ConsumeScript(fuzzed_data_provider, /*maybe_p2wsh=*/true);
83 : 434138 : tx_mut.vout.emplace_back(amount, script_pk);
84 : 434138 : }
85 : 255973 : return tx_mut;
86 : 354837 : }
87 : :
88 : 50509 : CScriptWitness ConsumeScriptWitness(FuzzedDataProvider& fuzzed_data_provider, const size_t max_stack_elem_size) noexcept
89 : : {
90 : 50509 : CScriptWitness ret;
91 : 50509 : const auto n_elements = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_stack_elem_size);
92 [ + + ]: 135668 : for (size_t i = 0; i < n_elements; ++i) {
93 : 85159 : ret.stack.push_back(ConsumeRandomLengthByteVector(fuzzed_data_provider));
94 : : }
95 : 50509 : return ret;
96 : : }
97 : :
98 : 895259 : CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const bool maybe_p2wsh) noexcept
99 : : {
100 : 895259 : CScript r_script{};
101 : 895259 : {
102 : : // Keep a buffer of bytes to allow the fuzz engine to produce smaller
103 : : // inputs to generate CScripts with repeated data.
104 : 895259 : static constexpr unsigned MAX_BUFFER_SZ{128};
105 : 895259 : std::vector<uint8_t> buffer(MAX_BUFFER_SZ, uint8_t{'a'});
106 [ + + ]: 34295183 : while (fuzzed_data_provider.ConsumeBool()) {
107 : 32504665 : CallOneOf(
108 : : fuzzed_data_provider,
109 : 7839581 : [&] {
110 : : // Insert byte vector directly to allow malformed or unparsable scripts
111 : 7839581 : r_script.insert(r_script.end(), buffer.begin(), buffer.begin() + fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BUFFER_SZ));
112 : 7839581 : },
113 : 22499069 : [&] {
114 : : // Push a byte vector from the buffer
115 : 44998138 : r_script << std::vector<uint8_t>{buffer.begin(), buffer.begin() + fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BUFFER_SZ)};
116 : 22499069 : },
117 : 317577 : [&] {
118 : : // Push multisig
119 : : // There is a special case for this to aid the fuzz engine
120 : : // navigate the highly structured multisig format.
121 : 317577 : r_script << fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 22);
122 : 317577 : int num_data{fuzzed_data_provider.ConsumeIntegralInRange(1, 22)};
123 [ + + ]: 3564809 : while (num_data--) {
124 [ - + ]: 2929655 : auto pubkey_bytes{ConstructPubKeyBytes(fuzzed_data_provider, buffer, fuzzed_data_provider.ConsumeBool())};
125 [ + + ]: 2929655 : if (fuzzed_data_provider.ConsumeBool()) {
126 : 2636058 : pubkey_bytes.back() = num_data; // Make each pubkey different
127 : : }
128 [ - + ]: 2929655 : r_script << pubkey_bytes;
129 : 2929655 : }
130 : 317577 : r_script << fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 22);
131 : 317577 : },
132 : 206064 : [&] {
133 : : // Mutate the buffer
134 : 206064 : const auto vec{ConsumeRandomLengthByteVector(fuzzed_data_provider, /*max_length=*/MAX_BUFFER_SZ)};
135 : 206064 : std::copy(vec.begin(), vec.end(), buffer.begin());
136 : 206064 : },
137 : 637538 : [&] {
138 : : // Push an integral
139 : 637538 : r_script << fuzzed_data_provider.ConsumeIntegral<int64_t>();
140 : 637538 : },
141 : 706324 : [&] {
142 : : // Push an opcode
143 : 706324 : r_script << ConsumeOpcodeType(fuzzed_data_provider);
144 : 706324 : },
145 : 298512 : [&] {
146 : : // Push a scriptnum
147 : 298512 : r_script << ConsumeScriptNum(fuzzed_data_provider);
148 : 298512 : });
149 : : }
150 : 895259 : }
151 [ + + + + ]: 953557 : if (maybe_p2wsh && fuzzed_data_provider.ConsumeBool()) {
152 : 17283 : uint256 script_hash;
153 [ + + + + ]: 39447 : CSHA256().Write(r_script.data(), r_script.size()).Finalize(script_hash.begin());
154 : 17283 : r_script.clear();
155 [ - + ]: 17283 : r_script << OP_0 << ToByteVector(script_hash);
156 : : }
157 : 895259 : return r_script;
158 : : }
159 : :
160 : 16575745 : uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept
161 : : {
162 [ + + ]: 16575745 : return fuzzed_data_provider.ConsumeBool() ?
163 : 7336214 : fuzzed_data_provider.PickValueInArray({
164 : : CTxIn::SEQUENCE_FINAL,
165 : : CTxIn::MAX_SEQUENCE_NONFINAL,
166 : : MAX_BIP125_RBF_SEQUENCE,
167 : : }) :
168 : 9239531 : fuzzed_data_provider.ConsumeIntegral<uint32_t>();
169 : : }
170 : :
171 : 10425 : std::map<COutPoint, Coin> ConsumeCoins(FuzzedDataProvider& fuzzed_data_provider) noexcept
172 : : {
173 : 10425 : std::map<COutPoint, Coin> coins;
174 [ + + + - ]: 47079 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
175 : 39440 : const std::optional<COutPoint> outpoint{ConsumeDeserializable<COutPoint>(fuzzed_data_provider)};
176 [ + + ]: 39440 : if (!outpoint) {
177 : : break;
178 : : }
179 : 37790 : const std::optional<Coin> coin{ConsumeDeserializable<Coin>(fuzzed_data_provider)};
180 [ + + ]: 37790 : if (!coin) {
181 : : break;
182 : : }
183 : 36654 : coins[*outpoint] = *coin;
184 : 37790 : }
185 : :
186 : 10425 : return coins;
187 : : }
188 : :
189 : 43901 : CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept
190 : : {
191 : 43901 : CTxDestination tx_destination;
192 : 43901 : const size_t call_size{CallOneOf(
193 : : fuzzed_data_provider,
194 : 6576 : [&] {
195 : 6576 : tx_destination = CNoDestination{};
196 : 6576 : },
197 : 6005 : [&] {
198 : 6005 : bool compressed = fuzzed_data_provider.ConsumeBool();
199 [ - + ]: 12010 : CPubKey pk{ConstructPubKeyBytes(
200 : : fuzzed_data_provider,
201 [ + + - + ]: 9408 : ConsumeFixedLengthByteVector(fuzzed_data_provider, (compressed ? CPubKey::COMPRESSED_SIZE : CPubKey::SIZE)),
202 : : compressed
203 : 6005 : )};
204 : 6005 : tx_destination = PubKeyDestination{pk};
205 : 6005 : },
206 : 2835 : [&] {
207 : 2835 : tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)};
208 : 2835 : },
209 : 2853 : [&] {
210 : 2853 : tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)};
211 : 2853 : },
212 : 5740 : [&] {
213 : 5740 : tx_destination = WitnessV0ScriptHash{ConsumeUInt256(fuzzed_data_provider)};
214 : 5740 : },
215 : 2349 : [&] {
216 : 2349 : tx_destination = WitnessV0KeyHash{ConsumeUInt160(fuzzed_data_provider)};
217 : 2349 : },
218 : 6981 : [&] {
219 : 6981 : tx_destination = WitnessV1Taproot{XOnlyPubKey{ConsumeUInt256(fuzzed_data_provider)}};
220 : 6981 : },
221 : 1647 : [&] {
222 : 1647 : tx_destination = PayToAnchor{};
223 : 1647 : },
224 : 8915 : [&] {
225 : 8915 : std::vector<unsigned char> program{ConsumeRandomLengthByteVector(fuzzed_data_provider, /*max_length=*/40)};
226 [ - + + + ]: 8915 : if (program.size() < 2) {
227 [ + - ]: 2656 : program = {0, 0};
228 : : }
229 [ + - ]: 8915 : tx_destination = WitnessUnknown{fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(2, 16), program};
230 : 8915 : })};
231 [ - + ]: 43901 : Assert(call_size == std::variant_size_v<CTxDestination>);
232 : 43901 : return tx_destination;
233 : : }
234 : :
235 : 50068 : CKey ConsumePrivateKey(FuzzedDataProvider& fuzzed_data_provider, std::optional<bool> compressed) noexcept
236 : : {
237 : 50068 : auto key_data = fuzzed_data_provider.ConsumeBytes<uint8_t>(32);
238 : 50068 : key_data.resize(32);
239 : 50068 : CKey key;
240 [ + + ]: 92100 : bool compressed_value = compressed ? *compressed : fuzzed_data_provider.ConsumeBool();
241 : 50068 : key.Set(key_data.begin(), key_data.end(), compressed_value);
242 : 50068 : return key;
243 : 50068 : }
244 : :
245 : 2774 : bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept
246 : : {
247 [ + + ]: 7307 : for (const CTxIn& tx_in : tx.vin) {
248 : 5769 : const Coin& coin = inputs.AccessCoin(tx_in.prevout);
249 [ + + ]: 5769 : if (coin.IsSpent()) {
250 : : return true;
251 : : }
252 : : }
253 : : return false;
254 : : }
255 : :
256 : 6908 : FILE* FuzzedFileProvider::open()
257 : : {
258 : 6908 : SetFuzzedErrNo(m_fuzzed_data_provider);
259 [ + + ]: 6908 : if (m_fuzzed_data_provider.ConsumeBool()) {
260 : : return nullptr;
261 : : }
262 [ + - ]: 6640 : std::string mode;
263 [ + - ]: 6640 : CallOneOf(
264 : : m_fuzzed_data_provider,
265 : 4975 : [&] {
266 : 4975 : mode = "r";
267 : 4975 : },
268 : 618 : [&] {
269 : 618 : mode = "r+";
270 : 618 : },
271 : 140 : [&] {
272 : 140 : mode = "w";
273 : 140 : },
274 : 475 : [&] {
275 : 475 : mode = "w+";
276 : 475 : },
277 : 74 : [&] {
278 : 74 : mode = "a";
279 : 74 : },
280 : 358 : [&] {
281 : 358 : mode = "a+";
282 : 358 : });
283 : : #if defined _GNU_SOURCE && (defined(__linux__) || defined(__FreeBSD__))
284 : 6640 : const cookie_io_functions_t io_hooks = {
285 : : FuzzedFileProvider::read,
286 : : FuzzedFileProvider::write,
287 : : FuzzedFileProvider::seek,
288 : : FuzzedFileProvider::close,
289 : : };
290 : 6640 : return fopencookie(this, mode.c_str(), io_hooks);
291 : : #else
292 : : (void)mode;
293 : : return nullptr;
294 : : #endif
295 : 6640 : }
296 : :
297 : 45413 : ssize_t FuzzedFileProvider::read(void* cookie, char* buf, size_t size)
298 : : {
299 : 45413 : FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
300 : 45413 : SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
301 [ + - + + ]: 45413 : if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) {
302 [ + + ]: 7080 : return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
303 : : }
304 : 38333 : const std::vector<uint8_t> random_bytes = fuzzed_file->m_fuzzed_data_provider.ConsumeBytes<uint8_t>(size);
305 [ + + ]: 38333 : if (random_bytes.empty()) {
306 : : return 0;
307 : : }
308 [ - + - + ]: 35448 : std::memcpy(buf, random_bytes.data(), random_bytes.size());
309 [ - + - + ]: 35448 : 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 [ - + ]: 35448 : fuzzed_file->m_offset += random_bytes.size();
313 [ - + ]: 38333 : return random_bytes.size();
314 : 38333 : }
315 : :
316 : 3318 : ssize_t FuzzedFileProvider::write(void* cookie, const char* buf, size_t size)
317 : : {
318 : 3318 : FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
319 : 3318 : SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
320 : 3318 : const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(0, size);
321 [ + - ]: 3318 : if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)n)) {
322 : : return 0;
323 : : }
324 : 3318 : fuzzed_file->m_offset += n;
325 : 3318 : return n;
326 : : }
327 : :
328 : 7233 : int FuzzedFileProvider::seek(void* cookie, int64_t* offset, int whence)
329 : : {
330 [ - + ]: 7233 : assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END);
331 : 7233 : FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
332 : 7233 : SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
333 : 7233 : int64_t new_offset = 0;
334 [ - + ]: 7233 : if (whence == SEEK_SET) {
335 : 0 : new_offset = *offset;
336 [ + - ]: 7233 : } else if (whence == SEEK_CUR) {
337 [ + - ]: 7233 : if (AdditionOverflow(fuzzed_file->m_offset, *offset)) {
338 : : return -1;
339 : : }
340 : 7233 : 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 [ + + ]: 7233 : if (new_offset < 0) {
349 : : return -1;
350 : : }
351 : 7044 : fuzzed_file->m_offset = new_offset;
352 : 7044 : *offset = new_offset;
353 : 7044 : return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
354 : : }
355 : :
356 : 6640 : int FuzzedFileProvider::close(void* cookie)
357 : : {
358 : 6640 : FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
359 : 6640 : SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
360 : 6640 : return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
361 : : }
|