Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
tx.fuzzer.cpp
Go to the documentation of this file.
1#include <cstdint>
2#include <string>
3
10
11using namespace bb::avm2::fuzzer;
12using namespace bb::avm2::simulation;
13
14// Extra counters to guide libfuzzer towards inputs with more enqueued calls.
15// Index 0 = 1 call, index 1 = 2 calls, etc. When an input has N enqueued calls,
16// we increment counter[N-1], signaling new coverage to libfuzzer.
17constexpr size_t MAX_ENQUEUED_CALLS_COUNTER = 32;
18__attribute__((section("__libfuzzer_extra_counters"))) uint8_t enqueued_calls_counter[MAX_ENQUEUED_CALLS_COUNTER];
19
20// Counters for tracking transaction effects to guide libfuzzer.
21__attribute__((section(
22 "__libfuzzer_extra_counters"))) uint8_t public_data_writes_counter[MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX];
23__attribute__((section("__libfuzzer_extra_counters"))) uint8_t note_hashes_counter[MAX_NOTE_HASHES_PER_TX];
24__attribute__((section("__libfuzzer_extra_counters"))) uint8_t nullifiers_counter[MAX_NULLIFIERS_PER_TX];
25__attribute__((section("__libfuzzer_extra_counters"))) uint8_t l2_to_l1_msgs_counter[MAX_L2_TO_L1_MSGS_PER_TX];
26
27// Public logs use logarithmic bucketing due to large range
28constexpr size_t MAX_PUBLIC_LOGS_COUNTER = 16;
29__attribute__((section("__libfuzzer_extra_counters"))) uint8_t public_logs_counter[MAX_PUBLIC_LOGS_COUNTER];
30
31namespace {
32
33void update_effects_counters(const SimulatorResult& result)
34{
35 const auto& tx_effect = result.public_tx_effect;
36
37 size_t public_data_writes_size = tx_effect.public_data_writes.size();
38 if (public_data_writes_size > 0) {
39 if (public_data_writes_size > MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX) {
40 throw std::runtime_error(
41 "Should be unreachable: generated " + std::to_string(public_data_writes_size) +
42 " public data writes, max: " + std::to_string(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX));
43 }
44 public_data_writes_counter[public_data_writes_size - 1]++;
45 }
46
47 size_t note_hashes_size = tx_effect.note_hashes.size();
48 if (note_hashes_size > 0) {
49 if (note_hashes_size > MAX_NOTE_HASHES_PER_TX) {
50 throw std::runtime_error("Should be unreachable: generated " + std::to_string(note_hashes_size) +
51 " note hashes, max: " + std::to_string(MAX_NOTE_HASHES_PER_TX));
52 }
53 note_hashes_counter[note_hashes_size - 1]++;
54 }
55
56 size_t nullifiers_size = tx_effect.nullifiers.size();
57 if (nullifiers_size > 0) {
58 if (nullifiers_size > MAX_NULLIFIERS_PER_TX) {
59 throw std::runtime_error("Should be unreachable: generated " + std::to_string(nullifiers_size) +
60 " nullifiers, max: " + std::to_string(MAX_NULLIFIERS_PER_TX));
61 }
62 nullifiers_counter[nullifiers_size - 1]++;
63 }
64
65 size_t l2_to_l1_size = tx_effect.l2_to_l1_msgs.size();
66 if (l2_to_l1_size > 0) {
67 if (l2_to_l1_size > MAX_L2_TO_L1_MSGS_PER_TX) {
68 throw std::runtime_error("Should be unreachable: generated " + std::to_string(l2_to_l1_size) +
69 " L2-to-L1 messages, max: " + std::to_string(MAX_L2_TO_L1_MSGS_PER_TX));
70 }
71 l2_to_l1_msgs_counter[l2_to_l1_size - 1]++;
72 }
73
74 // Public logs: calculate total field count across all logs.
75 // Each log contributes 2 header fields (length + contract_address) plus data fields.
76 uint32_t logs_field_count = 0;
77 for (const auto& log : tx_effect.public_logs) {
78 logs_field_count += 2 + static_cast<uint32_t>(log.fields.size());
79 }
80 if (logs_field_count > 0) {
81 uint8_t bucket = static_cast<uint8_t>(31 - std::countl_zero(logs_field_count));
82 if (bucket >= MAX_PUBLIC_LOGS_COUNTER) {
83 throw std::runtime_error("Should be unreachable: generated " + std::to_string(logs_field_count) +
84 " fields, max log2(count): " + std::to_string(MAX_PUBLIC_LOGS_COUNTER));
85 }
86 public_logs_counter[bucket]++;
87 }
88}
89
90} // namespace
91
92extern "C" int LLVMFuzzerInitialize(int*, char***)
93{
94 // Zero all counters
95 memset(enqueued_calls_counter, 0, sizeof(enqueued_calls_counter));
96 memset(public_data_writes_counter, 0, sizeof(public_data_writes_counter));
97 memset(note_hashes_counter, 0, sizeof(note_hashes_counter));
98 memset(nullifiers_counter, 0, sizeof(nullifiers_counter));
99 memset(l2_to_l1_msgs_counter, 0, sizeof(l2_to_l1_msgs_counter));
100 memset(public_logs_counter, 0, sizeof(public_logs_counter));
101
102 const char* simulator_path = std::getenv("AVM_SIMULATOR_BIN");
103 if (simulator_path == nullptr) {
104 throw std::runtime_error("AVM_SIMULATOR_BIN is not set");
105 }
106 std::string simulator_path_str(simulator_path);
107 JsSimulator::initialize(simulator_path_str);
109 return 0;
110}
111
112extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* serialized_fuzzer_data,
113 size_t serialized_fuzzer_data_size,
114 size_t max_size,
115 unsigned int seed)
116{
117 // Haven't thought much about the lifecycle of this in the tx fuzzer. Maybe we want it in the serialized data?
118 // Or we can regenerate from the serialized data.
120 return mutate_tx_data(context, serialized_fuzzer_data, serialized_fuzzer_data_size, max_size, seed);
121}
122
123extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
124{
127 ws_mgr->fork();
128
130
131 FuzzerTxData tx_data;
132 try {
133 msgpack::unpack((reinterpret_cast<const char*>(data)), size).get().convert(tx_data);
134 } catch (const std::exception& e) {
135 fuzz_info("Failed to deserialize input in TestOneInput, using default. Exception: ", e.what());
137 }
138
139 // Signal coverage for number of enqueued calls to guide fuzzer towards more calls
140 size_t num_calls = tx_data.tx.setup_enqueued_calls.size() + tx_data.tx.app_logic_enqueued_calls.size();
141 if (num_calls > 0 && num_calls <= MAX_ENQUEUED_CALLS_COUNTER) {
142 enqueued_calls_counter[num_calls - 1]++;
143 }
144
145 // Setup contracts and fund fee payer
147 fund_fee_payer(*ws_mgr, tx_data.tx);
148
149 auto simulation_result = fuzz_tx(*ws_mgr, contract_db, tx_data);
150 update_effects_counters(simulation_result);
152
153 return 0;
154}
#define fuzz_info(...)
Definition constants.hpp:51
#define MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
#define MAX_L2_TO_L1_MSGS_PER_TX
#define MAX_NOTE_HASHES_PER_TX
#define MAX_NULLIFIERS_PER_TX
StrictMock< MockContractDB > contract_db
static void initialize(std::string &simulator_path)
static FuzzerWorldStateManager * getInstance()
Definition dbs.hpp:80
world_state::WorldStateRevision fork()
Definition dbs.cpp:211
const std::vector< MemoryValue > data
FuzzerWorldStateManager * ws_mgr
Definition fuzz.test.cpp:16
SimulatorResult fuzz_tx(FuzzerWorldStateManager &ws_mgr, FuzzerContractDB &contract_db, FuzzerTxData &tx_data)
Fuzz CPP vs JS simulator with a full transaction containing multiple enqueued calls.
void setup_fuzzer_state(FuzzerWorldStateManager &ws_mgr, FuzzerContractDB &contract_db, const FuzzerTxData &tx_data)
size_t mutate_tx_data(FuzzerContext &context, uint8_t *serialized_fuzzer_data, size_t serialized_fuzzer_data_size, size_t max_size, unsigned int seed)
FuzzerTxData create_default_tx_data(std::mt19937_64 &rng, FuzzerContext &context)
void fund_fee_payer(FuzzerWorldStateManager &ws_mgr, const Tx &tx)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
PublicTxEffect public_tx_effect
Definition simulator.hpp:51
std::vector< PublicDataWrite > public_data_writes
Definition avm_io.hpp:541
std::vector< PublicCallRequestWithCalldata > setup_enqueued_calls
Definition avm_io.hpp:337
std::vector< PublicCallRequestWithCalldata > app_logic_enqueued_calls
Definition avm_io.hpp:338
constexpr size_t MAX_ENQUEUED_CALLS_COUNTER
Definition tx.fuzzer.cpp:17
__attribute__((section("__libfuzzer_extra_counters"))) uint8_t enqueued_calls_counter[MAX_ENQUEUED_CALLS_COUNTER]
int LLVMFuzzerInitialize(int *, char ***)
Definition tx.fuzzer.cpp:92
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
constexpr size_t MAX_PUBLIC_LOGS_COUNTER
Definition tx.fuzzer.cpp:28
size_t LLVMFuzzerCustomMutator(uint8_t *serialized_fuzzer_data, size_t serialized_fuzzer_data_size, size_t max_size, unsigned int seed)