Skip to content

Commit 33dc79f

Browse files
committed
feat(avm): range check fuzzer
1 parent f884280 commit 33dc79f

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#include <cassert>
2+
#include <cstdint>
3+
#include <fuzzer/FuzzedDataProvider.h>
4+
5+
#include "barretenberg/common/serialize.hpp"
6+
#include "barretenberg/numeric/uint128/uint128.hpp"
7+
#include "barretenberg/vm2/constraining/testing/check_relation.hpp"
8+
#include "barretenberg/vm2/generated/columns.hpp"
9+
#include "barretenberg/vm2/simulation/events/event_emitter.hpp"
10+
#include "barretenberg/vm2/simulation/events/range_check_event.hpp"
11+
#include "barretenberg/vm2/simulation/gadgets/range_check.hpp"
12+
#include "barretenberg/vm2/tooling/debugger.hpp"
13+
#include "barretenberg/vm2/tracegen/precomputed_trace.hpp"
14+
#include "barretenberg/vm2/tracegen/range_check_trace.hpp"
15+
#include "barretenberg/vm2/tracegen/test_trace_container.hpp"
16+
17+
using namespace bb::avm2::simulation;
18+
using namespace bb::avm2::tracegen;
19+
using namespace bb::avm2::constraining;
20+
21+
using bb::avm2::FF;
22+
23+
using range_check_rel = bb::avm2::range_check<FF>;
24+
25+
// We initialize it here once so it can be shared to other threads.
26+
// We don't use LLVMFuzzerInitialize since (IIUC) it is not thread safe and we want to run this
27+
// with multiple worker threads.
28+
static const TestTraceContainer precomputed_trace = []() {
29+
TestTraceContainer t;
30+
PrecomputedTraceBuilder precomputed_builder;
31+
precomputed_builder.process_sel_range_16(t);
32+
precomputed_builder.process_sel_range_8(t);
33+
precomputed_builder.process_power_of_2(t);
34+
precomputed_builder.process_misc(t, 1 << 16);
35+
return t;
36+
}();
37+
38+
// Each worker thread gets its own trace, initialized from precomputed_trace
39+
thread_local static TestTraceContainer trace = precomputed_trace;
40+
41+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
42+
{
43+
// We need at least 17 bytes: 16 bytes for uint128_t value + 1 byte for num_bits
44+
size_t minimum_size = 17;
45+
46+
if (size < minimum_size) {
47+
return 0;
48+
}
49+
50+
// Fuzzed Data Provider helps with extracting typed data from the raw byte stream.
51+
FuzzedDataProvider fuzzed_data(data, size);
52+
53+
// Read a uint128_t value (16 bytes)
54+
std::array<uint8_t, 16> value_bytes;
55+
for (size_t i = 0; i < 16; i++) {
56+
value_bytes[i] = fuzzed_data.ConsumeIntegral<uint8_t>();
57+
}
58+
uint128_t value = 0;
59+
for (size_t i = 0; i < 16; i++) {
60+
value |= (static_cast<uint128_t>(value_bytes[i]) << (i * 8));
61+
}
62+
63+
// Read num_bits (1-128)
64+
uint8_t num_bits = fuzzed_data.ConsumeIntegralInRange<uint8_t>(1, 128);
65+
66+
// Truncate value to fit within num_bits
67+
if (num_bits < 128) {
68+
uint128_t mask = (uint128_t(1) << num_bits) - 1;
69+
value = value & mask;
70+
}
71+
72+
// Set up gadget and event emitter
73+
DeduplicatingEventEmitter<RangeCheckEvent> range_check_emitter;
74+
RangeCheck range_check(range_check_emitter);
75+
76+
// Execute the range check operation
77+
try {
78+
// info("Asserting range for value: ", value, " with num_bits: ", static_cast<int>(num_bits));
79+
range_check.assert_range(value, num_bits);
80+
} catch (const std::exception& e) {
81+
// If any exception occurs, we cannot proceed further.
82+
return 0;
83+
}
84+
85+
// Process the events to build the trace (using the thread-local trace)
86+
RangeCheckTraceBuilder range_check_builder;
87+
range_check_builder.process(range_check_emitter.dump_events(), trace);
88+
89+
if (getenv("AVM_DEBUG") != nullptr) {
90+
info("Debugging trace:");
91+
bb::avm2::InteractiveDebugger debugger(trace);
92+
debugger.run();
93+
}
94+
95+
// Check the relation
96+
check_relation<range_check_rel>(trace);
97+
check_all_interactions<RangeCheckTraceBuilder>(trace);
98+
99+
return 0;
100+
}

0 commit comments

Comments
 (0)