Skip to content

Commit e6d52ef

Browse files
committed
feat(avm): avm fuzzer bytecode mutation
1 parent d39d11f commit e6d52ef

File tree

12 files changed

+224
-44
lines changed

12 files changed

+224
-44
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
@@ -45,14 +45,14 @@ SimulatorResult fuzz_against_ts_simulator(FuzzerData& fuzzer_data)
4545

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

5454
ws_mgr->checkpoint();
55-
auto js_result = js_simulator->simulate(*ws_mgr, contract_db, tx);
55+
auto js_result = js_simulator->simulate(*ws_mgr, contract_db, tx, /*public_data_writes=*/{});
5656

5757
ContractDBProxy::reset_instance();
5858

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ SimulatorResult simulate_with_default_tx(std::vector<uint8_t>& bytecode, std::ve
4949

5050
ws_mgr->checkpoint();
5151
try {
52-
auto result = cpp_simulator.simulate(*ws_mgr, contract_db, tx);
52+
auto result = cpp_simulator.simulate(*ws_mgr, contract_db, tx, /*public_data_writes=*/empty_writes);
5353
ws_mgr->revert();
5454
ws_mgr->reset_world_state();
5555
return result;

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

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@ using namespace bb::world_state;
3333
const auto MAX_RETURN_DATA_SIZE_IN_FIELDS = 1024;
3434

3535
// Helper function to serialize simulation request via msgpack
36-
std::string serialize_simulation_request(const Tx& tx,
37-
const GlobalVariables& globals,
38-
const FuzzerContractDB& contract_db)
36+
std::string serialize_simulation_request(
37+
const Tx& tx,
38+
const GlobalVariables& globals,
39+
const FuzzerContractDB& contract_db,
40+
const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes)
3941
{
4042
// Build vectors from contract_db
4143
std::vector<ContractClass> classes_vec = contract_db.get_contract_classes();
@@ -48,6 +50,7 @@ std::string serialize_simulation_request(const Tx& tx,
4850
.globals = globals,
4951
.contract_classes = std::move(classes_vec),
5052
.contract_instances = std::move(instances_vec),
53+
.public_data_writes = public_data_writes,
5154
};
5255

5356
auto [buffer, size] = msgpack_encode_buffer(request);
@@ -71,10 +74,13 @@ GlobalVariables create_default_globals()
7174
};
7275
}
7376

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

7985
const PublicSimulatorConfig config{
8086
.skip_fee_enforcement = false,
@@ -142,13 +148,15 @@ void JsSimulator::initialize(std::string& simulator_path)
142148
instance = new JsSimulator(simulator_path);
143149
}
144150

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

151-
std::string serialized = serialize_simulation_request(tx, globals, contract_db);
159+
std::string serialized = serialize_simulation_request(tx, globals, contract_db, public_data_writes);
152160

153161
// Send the request
154162
process.write_line(serialized);

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)) {
@@ -270,14 +283,23 @@ size_t mutate_tx_data(uint8_t* serialized_fuzzer_data,
270283
tx_data.contract_classes.clear();
271284
tx_data.contract_instances.clear();
272285
tx_data.contract_addresses.clear();
286+
tx_data.public_data_writes.clear();
273287
std::vector<AztecAddress> contract_addresses;
274288

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

278293
auto contract_address = simulation::compute_contract_address(contract_instance);
279-
contract_addresses.push_back(contract_address);
280294

295+
// Skip duplicate addresses - multiple input_programs can generate the same address
296+
if (seen_addresses.contains(contract_address)) {
297+
fuzz_info("Skipping duplicate contract address: ", contract_address);
298+
continue;
299+
}
300+
seen_addresses.insert(contract_address);
301+
302+
contract_addresses.push_back(contract_address);
281303
tx_data.contract_classes.push_back(contract_class);
282304
tx_data.contract_instances.push_back(contract_instance);
283305
}
@@ -297,17 +319,21 @@ size_t mutate_tx_data(uint8_t* serialized_fuzzer_data,
297319
}
298320

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

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

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
#include "barretenberg/avm_fuzzer/common/interfaces/dbs.hpp"
99
#include "barretenberg/avm_fuzzer/fuzz_lib/fuzzer_data.hpp"
1010
#include "barretenberg/avm_fuzzer/fuzz_lib/simulator.hpp"
11+
#include "barretenberg/avm_fuzzer/mutations/bytecode.hpp"
12+
#include "barretenberg/avm_fuzzer/mutations/tx_data.hpp"
13+
#include "barretenberg/crypto/merkle_tree/indexed_tree/indexed_leaf.hpp"
1114
#include "barretenberg/serialize/msgpack_impl.hpp"
1215
#include "barretenberg/vm2/common/avm_io.hpp"
1316
#include "barretenberg/vm2/common/aztec_types.hpp"
@@ -27,13 +30,17 @@ struct FuzzerTxData {
2730
GlobalVariables global_variables;
2831
ProtocolContracts protocol_contracts;
2932

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

3946
inline std::ostream& operator<<(std::ostream& os, const FuzzerTxData& data)
@@ -49,16 +56,22 @@ using Bytecode = std::vector<uint8_t>;
4956
using ContractArtifacts = std::tuple<Bytecode, ContractClass, ContractInstance>;
5057

5158
// Mutation configuration
52-
enum class TxDataMutationType : uint8_t {
59+
enum class FuzzerTxDataMutationType : uint8_t {
5360
TxMutation,
54-
// todo: implement other mutation types
55-
// BytecodeMutation,
61+
BytecodeMutation,
5662
// ContractClassMutation,
5763
// ContractInstanceMutation,
5864
// GlobalVariablesMutation,
5965
// ProtocolContractsMutation
6066
};
6167

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

0 commit comments

Comments
 (0)