Branch data 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 : 0 : CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider, const std::optional<CAmount>& max) noexcept
31 : : {
32 [ # # ][ # # ]: 0 : return fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, max.value_or(MAX_MONEY));
33 : : }
34 : :
35 : 5389 : 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 [ + - ][ + - ]: 5389 : 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 : 0 : uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept
157 : : {
158 [ # # ][ # # ]: 0 : return fuzzed_data_provider.ConsumeBool() ?
159 [ # # ]: 0 : fuzzed_data_provider.PickValueInArray({
160 : : CTxIn::SEQUENCE_FINAL,
161 : : CTxIn::MAX_SEQUENCE_NONFINAL,
162 : : MAX_BIP125_RBF_SEQUENCE,
163 : : }) :
164 [ # # ]: 0 : 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 : : }
|