Skip to content

Commit a9f59ee

Browse files
committed
feat(avm): avm fuzzer bytecode mutation
1 parent 35ef4be commit a9f59ee

File tree

12 files changed

+227
-47
lines changed

12 files changed

+227
-47
lines changed

barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,4 +237,10 @@ void FuzzerWorldStateManager::write_fee_payer_balance(const AztecAddress& fee_pa
237237
ws->update_public_data(PublicDataLeafValue(leaf_slot, balance), fork_id);
238238
}
239239

240+
void FuzzerWorldStateManager::public_data_write(const bb::crypto::merkle_tree::PublicDataLeafValue& public_data)
241+
{
242+
auto fork_id = fork_ids.top();
243+
ws->update_public_data(public_data, fork_id);
244+
}
245+
240246
} // namespace bb::avm2::fuzzer

barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class FuzzerWorldStateManager {
8888
void reset_world_state();
8989
void register_contract_address(const AztecAddress& contract_address);
9090
void write_fee_payer_balance(const AztecAddress& fee_payer, const FF& balance);
91+
void public_data_write(const bb::crypto::merkle_tree::PublicDataLeafValue& public_data);
9192

9293
world_state::WorldStateRevision get_current_revision() const;
9394
world_state::WorldStateRevision fork();

barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzz.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ SimulatorResult fuzz_against_ts_simulator(FuzzerData& fuzzer_data, FuzzerContext
4242

4343
try {
4444
ws_mgr->checkpoint();
45-
cpp_result = cpp_simulator.simulate(*ws_mgr, contract_db, tx);
45+
cpp_result = cpp_simulator.simulate(*ws_mgr, contract_db, tx, /*public_data_writes=*/{});
4646
ws_mgr->revert();
4747
} catch (const std::exception& e) {
4848
throw std::runtime_error(std::string("CppSimulator threw an exception: ") + e.what());
4949
}
5050

5151
ws_mgr->checkpoint();
52-
auto js_result = js_simulator->simulate(*ws_mgr, contract_db, tx);
52+
auto js_result = js_simulator->simulate(*ws_mgr, contract_db, tx, /*public_data_writes=*/{});
5353

5454
context.reset();
5555

barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/simulator.cpp

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@ using namespace bb::avm2::simulation;
3131
using namespace bb::avm2::fuzzer;
3232
using namespace bb::world_state;
3333

34-
const auto MAX_RETURN_DATA_SIZE_IN_FIELDS = 1024;
34+
constexpr auto MAX_RETURN_DATA_SIZE_IN_FIELDS = 1024;
3535

3636
// Helper function to serialize simulation request via msgpack
37-
std::string serialize_simulation_request(const Tx& tx,
38-
const GlobalVariables& globals,
39-
const FuzzerContractDB& contract_db)
37+
std::string serialize_simulation_request(
38+
const Tx& tx,
39+
const GlobalVariables& globals,
40+
const FuzzerContractDB& contract_db,
41+
const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes)
4042
{
4143
// Build vectors from contract_db
4244
std::vector<ContractClass> classes_vec = contract_db.get_contract_classes();
@@ -49,6 +51,7 @@ std::string serialize_simulation_request(const Tx& tx,
4951
.globals = globals,
5052
.contract_classes = std::move(classes_vec),
5153
.contract_instances = std::move(instances_vec),
54+
.public_data_writes = public_data_writes,
5255
};
5356

5457
auto [buffer, size] = msgpack_encode_buffer(request);
@@ -72,10 +75,13 @@ GlobalVariables create_default_globals()
7275
};
7376
}
7477

75-
SimulatorResult CppSimulator::simulate(fuzzer::FuzzerWorldStateManager& ws_mgr,
76-
fuzzer::FuzzerContractDB& contract_db,
77-
const Tx& tx)
78+
SimulatorResult CppSimulator::simulate(
79+
fuzzer::FuzzerWorldStateManager& ws_mgr,
80+
fuzzer::FuzzerContractDB& contract_db,
81+
const Tx& tx,
82+
[[maybe_unused]] const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes)
7883
{
84+
// Note: public_data_writes are already applied to C++ world state in setup_fuzzer_state
7985

8086
const PublicSimulatorConfig config{
8187
.skip_fee_enforcement = false,
@@ -143,13 +149,15 @@ void JsSimulator::initialize(std::string& simulator_path)
143149
instance = new JsSimulator(simulator_path);
144150
}
145151

146-
SimulatorResult JsSimulator::simulate([[maybe_unused]] fuzzer::FuzzerWorldStateManager& ws_mgr,
147-
fuzzer::FuzzerContractDB& contract_db,
148-
const Tx& tx)
152+
SimulatorResult JsSimulator::simulate(
153+
[[maybe_unused]] fuzzer::FuzzerWorldStateManager& ws_mgr,
154+
fuzzer::FuzzerContractDB& contract_db,
155+
const Tx& tx,
156+
const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes)
149157
{
150158
auto globals = create_default_globals();
151159

152-
std::string serialized = serialize_simulation_request(tx, globals, contract_db);
160+
std::string serialized = serialize_simulation_request(tx, globals, contract_db, public_data_writes);
153161

154162
// Send the request
155163
process.write_line(serialized);
@@ -172,7 +180,7 @@ SimulatorResult JsSimulator::simulate([[maybe_unused]] fuzzer::FuzzerWorldStateM
172180
bool compare_simulator_results(SimulatorResult& result1, SimulatorResult& result2)
173181
{
174182
// Since the simulator results are interchangeable between TS and C++, we limit the return data size for comparison
175-
// todo(ilyas): we ideally specfify one param as the TS result and truncate only that one
183+
// todo(ilyas): we ideally specify one param as the TS result and truncate only that one
176184
if (result1.output.size() > MAX_RETURN_DATA_SIZE_IN_FIELDS) {
177185
result1.output.resize(MAX_RETURN_DATA_SIZE_IN_FIELDS);
178186
}

barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/simulator.hpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "barretenberg/avm_fuzzer/common/interfaces/dbs.hpp"
66
#include "barretenberg/avm_fuzzer/common/process.hpp"
7+
#include "barretenberg/crypto/merkle_tree/indexed_tree/indexed_leaf.hpp"
78
#include "barretenberg/vm2/common/avm_io.hpp"
89
#include "barretenberg/vm2/common/aztec_types.hpp"
910
#include "barretenberg/vm2/common/field.hpp"
@@ -24,8 +25,11 @@ struct FuzzerSimulationRequest {
2425
std::vector<ContractClass> contract_classes;
2526
// Having addresses here avoids doing re-work in TS.
2627
std::vector<std::pair<AztecAddress, ContractInstance>> contract_instances;
28+
// Public data tree writes to apply before simulation (e.g., for bytecode upgrades)
29+
std::vector<bb::crypto::merkle_tree::PublicDataLeafValue> public_data_writes;
2730

28-
MSGPACK_CAMEL_CASE_FIELDS(ws_data_dir, ws_map_size_kb, tx, globals, contract_classes, contract_instances);
31+
MSGPACK_CAMEL_CASE_FIELDS(
32+
ws_data_dir, ws_map_size_kb, tx, globals, contract_classes, contract_instances, public_data_writes);
2933
};
3034

3135
struct SimulatorResult {
@@ -45,17 +49,21 @@ class Simulator {
4549
Simulator(Simulator&&) = delete;
4650
Simulator& operator=(Simulator&&) = delete;
4751
Simulator() = default;
48-
virtual SimulatorResult simulate(fuzzer::FuzzerWorldStateManager& ws_mgr,
49-
fuzzer::FuzzerContractDB& contract_db,
50-
const Tx& tx) = 0;
52+
virtual SimulatorResult simulate(
53+
fuzzer::FuzzerWorldStateManager& ws_mgr,
54+
fuzzer::FuzzerContractDB& contract_db,
55+
const Tx& tx,
56+
const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes) = 0;
5157
};
5258

5359
/// @brief uses barretenberg/vm2 to simulate the bytecode
5460
class CppSimulator : public Simulator {
5561
public:
56-
SimulatorResult simulate(fuzzer::FuzzerWorldStateManager& ws_mgr,
57-
fuzzer::FuzzerContractDB& contract_db,
58-
const Tx& tx) override;
62+
SimulatorResult simulate(
63+
fuzzer::FuzzerWorldStateManager& ws_mgr,
64+
fuzzer::FuzzerContractDB& contract_db,
65+
const Tx& tx,
66+
const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes) override;
5967
};
6068

6169
/// @brief uses the yarn-project/simulator to simulate the bytecode
@@ -77,9 +85,11 @@ class JsSimulator : public Simulator {
7785
static JsSimulator* getInstance();
7886
static void initialize(std::string& simulator_path);
7987

80-
SimulatorResult simulate(fuzzer::FuzzerWorldStateManager& ws_mgr,
81-
fuzzer::FuzzerContractDB& contract_db,
82-
const Tx& tx) override;
88+
SimulatorResult simulate(
89+
fuzzer::FuzzerWorldStateManager& ws_mgr,
90+
fuzzer::FuzzerContractDB& contract_db,
91+
const Tx& tx,
92+
const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes) override;
8393
};
8494

8595
GlobalVariables create_default_globals();

barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.cpp

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,34 @@ using namespace bb::avm2::fuzzer;
2525
using namespace bb::avm2::simulation;
2626
using namespace bb::world_state;
2727

28+
extern size_t LLVMFuzzerMutate(uint8_t* Data, size_t Size, size_t MaxSize);
29+
2830
void setup_fuzzer_state(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contract_db, const FuzzerTxData& tx_data)
2931
{
3032
// Add all contract classes and instances to the contract DB
33+
// There may be more classes than instances because of the possibility of mutated bytecodes that are used in
34+
// upgrades - these are not directly instantiated
3135
for (size_t i = 0; i < tx_data.contract_classes.size(); ++i) {
3236
const auto& contract_class = tx_data.contract_classes[i];
37+
contract_db.add_contract_class(contract_class.id, contract_class);
38+
}
39+
40+
// Add contract instances to the contract DB
41+
for (size_t i = 0; i < tx_data.contract_instances.size(); ++i) {
3342
const auto& contract_instance = tx_data.contract_instances[i];
3443
auto contract_address = tx_data.contract_addresses[i];
35-
contract_db.add_contract_class(contract_class.id, contract_class);
3644
contract_db.add_contract_instance(contract_address, contract_instance);
3745
}
3846

39-
// Register the de-duplicated set of contract addresses to the world state (in insertion order)
40-
std::unordered_set<AztecAddress> seen_addresses;
47+
// Register contract addresses in the world state
4148
for (const auto& addr : tx_data.contract_addresses) {
42-
if (seen_addresses.insert(addr).second) {
43-
fuzz_info("Registering contract address in world state: ", addr);
44-
ws_mgr.register_contract_address(addr);
49+
ws_mgr.register_contract_address(addr);
50+
}
51+
52+
// Apply public data tree writes (e.g., for contract instance upgrades)
53+
if (!tx_data.public_data_writes.empty()) {
54+
for (const auto& write : tx_data.public_data_writes) {
55+
ws_mgr.public_data_write(write);
4556
}
4657
}
4758
}
@@ -69,7 +80,9 @@ SimulatorResult fuzz_tx(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contr
6980

7081
try {
7182
ws_mgr.checkpoint();
72-
cpp_result = cpp_simulator.simulate(ws_mgr, contract_db, tx_data.tx);
83+
cpp_result = cpp_simulator.simulate(ws_mgr, contract_db, tx_data.tx, tx_data.public_data_writes);
84+
fuzz_info("CppSimulator completed without exception");
85+
fuzz_info("CppSimulator result: ", cpp_result);
7386
ws_mgr.revert();
7487
} catch (const std::exception& e) {
7588
fuzz_info("CppSimulator threw an exception: ", e.what());
@@ -83,7 +96,7 @@ SimulatorResult fuzz_tx(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contr
8396
}
8497

8598
ws_mgr.checkpoint();
86-
auto js_result = js_simulator->simulate(ws_mgr, contract_db, tx_data.tx);
99+
auto js_result = js_simulator->simulate(ws_mgr, contract_db, tx_data.tx, tx_data.public_data_writes);
87100

88101
// If the results do not match
89102
if (!compare_simulator_results(cpp_result, js_result)) {
@@ -271,14 +284,23 @@ size_t mutate_tx_data(FuzzerContext& context,
271284
tx_data.contract_classes.clear();
272285
tx_data.contract_instances.clear();
273286
tx_data.contract_addresses.clear();
287+
tx_data.public_data_writes.clear();
274288
std::vector<AztecAddress> contract_addresses;
275289

290+
std::unordered_set<AztecAddress> seen_addresses;
276291
for (auto& fuzzer_data : tx_data.input_programs) {
277292
const auto [bytecode, contract_class, contract_instance] = build_bytecode_and_artifacts(fuzzer_data);
278293

279294
auto contract_address = simulation::compute_contract_address(contract_instance);
280-
contract_addresses.push_back(contract_address);
281295

296+
// Skip duplicate addresses - multiple input_programs can generate the same address
297+
if (seen_addresses.contains(contract_address)) {
298+
fuzz_info("Skipping duplicate contract address: ", contract_address);
299+
continue;
300+
}
301+
seen_addresses.insert(contract_address);
302+
303+
contract_addresses.push_back(contract_address);
282304
tx_data.contract_classes.push_back(contract_class);
283305
tx_data.contract_instances.push_back(contract_instance);
284306
}
@@ -298,17 +320,21 @@ size_t mutate_tx_data(FuzzerContext& context,
298320
}
299321

300322
// Select mutation type (weighted against bytecode mutations) -- todo
301-
auto mutation_type = std::uniform_int_distribution<uint8_t>(0, 0);
302-
TxDataMutationType mutation_choice = static_cast<TxDataMutationType>(mutation_type(rng));
323+
FuzzerTxDataMutationType mutation_choice = FUZZER_TX_DATA_MUTATION_CONFIGURATION.select(rng);
303324

304325
switch (mutation_choice) {
305-
case TxDataMutationType::TxMutation:
326+
case FuzzerTxDataMutationType::TxMutation:
306327
mutate_tx(tx_data.tx, contract_addresses, rng);
307328
break;
308-
// case TxDataMutationType::BytecodeMutation:
309-
// // todo: Maybe here we can do some direct mutations on the bytecode
310-
// // Mutations here are likely to cause immediate failure
311-
// break;
329+
case FuzzerTxDataMutationType::BytecodeMutation: {
330+
// Mutate bytecode and append public data writes for world state setup
331+
mutate_bytecode(tx_data.contract_classes,
332+
tx_data.contract_instances,
333+
tx_data.contract_addresses,
334+
tx_data.public_data_writes,
335+
rng);
336+
break;
337+
}
312338
// case TxDataMutationType::ContractClassMutation:
313339
// // Mutations here are likely to cause immediate failure
314340
// break;

barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.hpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
#include "barretenberg/avm_fuzzer/fuzz_lib/fuzzer_context.hpp"
1010
#include "barretenberg/avm_fuzzer/fuzz_lib/fuzzer_data.hpp"
1111
#include "barretenberg/avm_fuzzer/fuzz_lib/simulator.hpp"
12+
#include "barretenberg/avm_fuzzer/mutations/bytecode.hpp"
13+
#include "barretenberg/avm_fuzzer/mutations/tx_data.hpp"
14+
#include "barretenberg/crypto/merkle_tree/indexed_tree/indexed_leaf.hpp"
1215
#include "barretenberg/serialize/msgpack_impl.hpp"
1316
#include "barretenberg/vm2/common/avm_io.hpp"
1417
#include "barretenberg/vm2/common/aztec_types.hpp"
@@ -28,13 +31,17 @@ struct FuzzerTxData {
2831
GlobalVariables global_variables;
2932
ProtocolContracts protocol_contracts;
3033

34+
// Public data tree writes to be applied during state setup (e.g., for bytecode upgrades)
35+
std::vector<bb::crypto::merkle_tree::PublicDataLeafValue> public_data_writes;
36+
3137
MSGPACK_FIELDS(input_programs,
3238
contract_classes,
3339
contract_instances,
3440
contract_addresses,
3541
tx,
3642
global_variables,
37-
protocol_contracts);
43+
protocol_contracts,
44+
public_data_writes);
3845
};
3946

4047
inline std::ostream& operator<<(std::ostream& os, const FuzzerTxData& data)
@@ -51,16 +58,22 @@ using ContractArtifacts = std::tuple<Bytecode, ContractClass, ContractInstance>;
5158
using FuzzerContext = bb::avm2::fuzzer::FuzzerContext;
5259

5360
// Mutation configuration
54-
enum class TxDataMutationType : uint8_t {
61+
enum class FuzzerTxDataMutationType : uint8_t {
5562
TxMutation,
56-
// todo: implement other mutation types
57-
// BytecodeMutation,
63+
BytecodeMutation,
5864
// ContractClassMutation,
5965
// ContractInstanceMutation,
6066
// GlobalVariablesMutation,
6167
// ProtocolContractsMutation
6268
};
6369

70+
using FuzzerTxDataMutationConfig = WeightedSelectionConfig<FuzzerTxDataMutationType, 2>;
71+
72+
constexpr FuzzerTxDataMutationConfig FUZZER_TX_DATA_MUTATION_CONFIGURATION = FuzzerTxDataMutationConfig({
73+
{ FuzzerTxDataMutationType::TxMutation, 10 },
74+
{ FuzzerTxDataMutationType::BytecodeMutation, 1 },
75+
});
76+
6477
// Build bytecode and contract artifacts from fuzzer data
6578
ContractArtifacts build_bytecode_and_artifacts(FuzzerData& fuzzer_data);
6679

0 commit comments

Comments
 (0)