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