@@ -349,16 +349,76 @@ CMutableTransaction ConsumeTransaction(FuzzedDataProvider& fuzzed_data_provider,
349349 }
350350 for (int i = 0 ; i < num_out; ++i) {
351351 const auto amount = fuzzed_data_provider.ConsumeIntegralInRange <CAmount>(-10 , 50 * COIN + 10 );
352- const auto script_pk = ConsumeScript (fuzzed_data_provider, /* max_length */ 128 );
352+ const auto script_pk = ConsumeScript (fuzzed_data_provider);
353353 tx_mut.vout .emplace_back (amount, script_pk);
354354 }
355355 return tx_mut;
356356}
357357
358- CScript ConsumeScript (FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t >& max_length) noexcept
359- {
360- const std::vector<uint8_t > b = ConsumeRandomLengthByteVector (fuzzed_data_provider, max_length);
361- return {b.begin (), b.end ()};
358+ CScript ConsumeScript (FuzzedDataProvider& fuzzed_data_provider) noexcept
359+ {
360+ CScript r_script{};
361+ {
362+ // Keep a buffer of bytes to allow the fuzz engine to produce smaller
363+ // inputs to generate CScripts with repeated data.
364+ static constexpr unsigned MAX_BUFFER_SZ{128 };
365+ std::vector<uint8_t > buffer (MAX_BUFFER_SZ, uint8_t {' a' });
366+ while (fuzzed_data_provider.ConsumeBool ()) {
367+ CallOneOf (
368+ fuzzed_data_provider,
369+ [&] {
370+ // Insert byte vector directly to allow malformed or unparsable scripts
371+ r_script.insert (r_script.end (), buffer.begin (), buffer.begin () + fuzzed_data_provider.ConsumeIntegralInRange (0U , MAX_BUFFER_SZ));
372+ },
373+ [&] {
374+ // Push a byte vector from the buffer
375+ r_script << std::vector<uint8_t >{buffer.begin (), buffer.begin () + fuzzed_data_provider.ConsumeIntegralInRange (0U , MAX_BUFFER_SZ)};
376+ },
377+ [&] {
378+ // Push multisig
379+ // There is a special case for this to aid the fuzz engine
380+ // navigate the highly structured multisig format.
381+ r_script << fuzzed_data_provider.ConsumeIntegralInRange <int64_t >(0 , 22 );
382+ int num_data{fuzzed_data_provider.ConsumeIntegralInRange (1 , 22 )};
383+ std::vector<uint8_t > pubkey_comp{buffer.begin (), buffer.begin () + CPubKey::COMPRESSED_SIZE};
384+ pubkey_comp.front () = fuzzed_data_provider.ConsumeIntegralInRange (2 , 3 ); // Set first byte for GetLen() to pass
385+ std::vector<uint8_t > pubkey_uncomp{buffer.begin (), buffer.begin () + CPubKey::SIZE};
386+ pubkey_uncomp.front () = fuzzed_data_provider.ConsumeIntegralInRange (4 , 7 ); // Set first byte for GetLen() to pass
387+ while (num_data--) {
388+ auto & pubkey{fuzzed_data_provider.ConsumeBool () ? pubkey_uncomp : pubkey_comp};
389+ if (fuzzed_data_provider.ConsumeBool ()) {
390+ pubkey.back () = num_data; // Make each pubkey different
391+ }
392+ r_script << pubkey;
393+ }
394+ r_script << fuzzed_data_provider.ConsumeIntegralInRange <int64_t >(0 , 22 );
395+ },
396+ [&] {
397+ // Mutate the buffer
398+ const auto vec{ConsumeRandomLengthByteVector (fuzzed_data_provider, /* max_length=*/ MAX_BUFFER_SZ)};
399+ std::copy (vec.begin (), vec.end (), buffer.begin ());
400+ },
401+ [&] {
402+ // Push an integral
403+ r_script << fuzzed_data_provider.ConsumeIntegral <int64_t >();
404+ },
405+ [&] {
406+ // Push an opcode
407+ r_script << ConsumeOpcodeType (fuzzed_data_provider);
408+ },
409+ [&] {
410+ // Push a scriptnum
411+ r_script << ConsumeScriptNum (fuzzed_data_provider);
412+ });
413+ }
414+ }
415+ if (fuzzed_data_provider.ConsumeBool ()) {
416+ uint256 script_hash;
417+ CSHA256 ().Write (r_script.data (), r_script.size ()).Finalize (script_hash.begin ());
418+ r_script.clear ();
419+ r_script << OP_0 << ToByteVector (script_hash);
420+ }
421+ return r_script;
362422}
363423
364424uint32_t ConsumeSequence (FuzzedDataProvider& fuzzed_data_provider) noexcept
0 commit comments