Skip to content

Commit 58fb01c

Browse files
committed
restore prover
1 parent 363f2ef commit 58fb01c

File tree

2 files changed

+95
-1
lines changed

2 files changed

+95
-1
lines changed

barretenberg/cpp/src/barretenberg/avm_fuzzer/common/copyable_trace_container.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <cstddef>
66
#include <functional>
77
#include <memory>
8+
#include <mutex>
89
#include <shared_mutex>
910
#include <span>
1011
#include <unordered_map>

barretenberg/cpp/src/barretenberg/avm_fuzzer/prover.fuzzer.cpp

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,94 @@
1111

1212
using namespace bb::avm2::fuzzer;
1313

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.
17+
constexpr 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
28+
constexpr size_t MAX_PUBLIC_LOGS_COUNTER = 16;
29+
__attribute__((section("__libfuzzer_extra_counters"))) uint8_t public_logs_counter[MAX_PUBLIC_LOGS_COUNTER];
30+
31+
namespace {
32+
33+
void update_effects_counters(const TxSimulationResult& 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+
1492
extern "C" int LLVMFuzzerInitialize(int*, char***)
1593
{
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+
16102
FuzzerWorldStateManager::initialize();
17103
return 0;
18104
}
@@ -45,12 +131,19 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
45131
tx_data = create_default_tx_data(context);
46132
}
47133

134+
// Signal coverage for number of enqueued calls to guide fuzzer towards more calls
135+
size_t num_calls = tx_data.tx.setup_enqueued_calls.size() + tx_data.tx.app_logic_enqueued_calls.size();
136+
if (num_calls > 0 && num_calls <= MAX_ENQUEUED_CALLS_COUNTER) {
137+
enqueued_calls_counter[num_calls - 1]++;
138+
}
139+
48140
// Setup contracts and fund fee payer
49141
// Fuzzer state is dependent on the tx data
50142
setup_fuzzer_state(*ws_mgr, contract_db, tx_data);
51143
fund_fee_payer(*ws_mgr, tx_data.tx);
52144

53-
fuzz_prover(*ws_mgr, contract_db, tx_data);
145+
TxSimulationResult result = fuzz_prover(*ws_mgr, contract_db, tx_data);
146+
update_effects_counters(result);
54147

55148
// Print timing stats for this iteration
56149
vinfo("Timing stats:\n", bb::avm2::Stats::get().to_string());

0 commit comments

Comments
 (0)