Skip to content

Commit

Permalink
merge bitcoin#23408: Rework ConsumeScript
Browse files Browse the repository at this point in the history
  • Loading branch information
kwvg committed Jan 28, 2025
1 parent b8af7ea commit 4ea7c90
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 11 deletions.
4 changes: 1 addition & 3 deletions src/test/fuzz/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ void initialize_script()
FUZZ_TARGET_INIT(script, initialize_script)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const std::optional<CScript> script_opt = ConsumeDeserializable<CScript>(fuzzed_data_provider);
if (!script_opt) return;
const CScript script{*script_opt};
const CScript script{ConsumeScript(fuzzed_data_provider)};

CompressedScript compressed;
if (CompressScript(script, compressed)) {
Expand Down
4 changes: 2 additions & 2 deletions src/test/fuzz/signature_checker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ FUZZ_TARGET(signature_checker)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const unsigned int flags = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
const auto script_1 = ConsumeScript(fuzzed_data_provider, 65536);
const auto script_2 = ConsumeScript(fuzzed_data_provider, 65536);
const auto script_1{ConsumeScript(fuzzed_data_provider)};
const auto script_2{ConsumeScript(fuzzed_data_provider)};
std::vector<std::vector<unsigned char>> stack;
(void)EvalScript(stack, script_1, flags, FuzzedSignatureChecker(fuzzed_data_provider), SigVersion::BASE, nullptr);
if (!IsValidFlagCombination(flags)) {
Expand Down
70 changes: 65 additions & 5 deletions src/test/fuzz/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,16 +349,76 @@ CMutableTransaction ConsumeTransaction(FuzzedDataProvider& fuzzed_data_provider,
}
for (int i = 0; i < num_out; ++i) {
const auto amount = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-10, 50 * COIN + 10);
const auto script_pk = ConsumeScript(fuzzed_data_provider, /* max_length */ 128);
const auto script_pk = ConsumeScript(fuzzed_data_provider);
tx_mut.vout.emplace_back(amount, script_pk);
}
return tx_mut;
}

CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length) noexcept
{
const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length);
return {b.begin(), b.end()};
CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
CScript r_script{};
{
// Keep a buffer of bytes to allow the fuzz engine to produce smaller
// inputs to generate CScripts with repeated data.
static constexpr unsigned MAX_BUFFER_SZ{128};
std::vector<uint8_t> buffer(MAX_BUFFER_SZ, uint8_t{'a'});
while (fuzzed_data_provider.ConsumeBool()) {
CallOneOf(
fuzzed_data_provider,
[&] {
// Insert byte vector directly to allow malformed or unparsable scripts
r_script.insert(r_script.end(), buffer.begin(), buffer.begin() + fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BUFFER_SZ));
},
[&] {
// Push a byte vector from the buffer
r_script << std::vector<uint8_t>{buffer.begin(), buffer.begin() + fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BUFFER_SZ)};
},
[&] {
// Push multisig
// There is a special case for this to aid the fuzz engine
// navigate the highly structured multisig format.
r_script << fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 22);
int num_data{fuzzed_data_provider.ConsumeIntegralInRange(1, 22)};
std::vector<uint8_t> pubkey_comp{buffer.begin(), buffer.begin() + CPubKey::COMPRESSED_SIZE};
pubkey_comp.front() = fuzzed_data_provider.ConsumeIntegralInRange(2, 3); // Set first byte for GetLen() to pass
std::vector<uint8_t> pubkey_uncomp{buffer.begin(), buffer.begin() + CPubKey::SIZE};
pubkey_uncomp.front() = fuzzed_data_provider.ConsumeIntegralInRange(4, 7); // Set first byte for GetLen() to pass
while (num_data--) {
auto& pubkey{fuzzed_data_provider.ConsumeBool() ? pubkey_uncomp : pubkey_comp};
if (fuzzed_data_provider.ConsumeBool()) {
pubkey.back() = num_data; // Make each pubkey different
}
r_script << pubkey;
}
r_script << fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 22);
},
[&] {
// Mutate the buffer
const auto vec{ConsumeRandomLengthByteVector(fuzzed_data_provider, /*max_length=*/MAX_BUFFER_SZ)};
std::copy(vec.begin(), vec.end(), buffer.begin());
},
[&] {
// Push an integral
r_script << fuzzed_data_provider.ConsumeIntegral<int64_t>();
},
[&] {
// Push an opcode
r_script << ConsumeOpcodeType(fuzzed_data_provider);
},
[&] {
// Push a scriptnum
r_script << ConsumeScriptNum(fuzzed_data_provider);
});
}
}
if (fuzzed_data_provider.ConsumeBool()) {
uint256 script_hash;
CSHA256().Write(r_script.data(), r_script.size()).Finalize(script_hash.begin());
r_script.clear();
r_script << OP_0 << ToByteVector(script_hash);
}
return r_script;
}

uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept
Expand Down
2 changes: 1 addition & 1 deletion src/test/fuzz/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ template <typename WeakEnumType, size_t size>

[[nodiscard]] CMutableTransaction ConsumeTransaction(FuzzedDataProvider& fuzzed_data_provider, const std::optional<std::vector<uint256>>& prevout_txids, const int max_num_in = 10, const int max_num_out = 10) noexcept;

[[nodiscard]] CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length = std::nullopt) noexcept;
[[nodiscard]] CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider) noexcept;

[[nodiscard]] uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept;

Expand Down

0 comments on commit 4ea7c90

Please sign in to comment.