Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
multi_field.fuzzer.cpp
Go to the documentation of this file.
1
43#include <cassert>
44#include <cstddef>
45#include <cstring>
46#include <vector>
47using namespace bb;
48
52enum class FieldType : uint8_t {
53 BN254_FQ = 0,
54 BN254_FR = 1,
55 SECP256K1_FQ = 2,
56 SECP256K1_FR = 3,
57 SECP256R1_FQ = 4,
58 SECP256R1_FR = 5,
59};
60
61constexpr size_t NUM_FIELD_TYPES = 6;
62constexpr size_t MAX_STEPS = 64;
63
71 uint8_t field_type;
72 uint8_t steps;
73};
74
75static_assert(sizeof(VMPhaseHeader) == 2, "VMPhaseHeader must be exactly 2 bytes");
76
77const size_t PHASE_HEADER_SIZE = sizeof(VMPhaseHeader);
78
87{
88 if (value < Field::modulus) {
89 return value;
90 }
91 return value % Field::modulus;
92}
93
101template <typename Field> FieldVM<Field> create_field_vm(size_t max_steps)
102{
103 return FieldVM<Field>(false, max_steps);
104}
105
113template <typename Field>
115{
116 for (size_t i = 0; i < INTERNAL_STATE_SIZE && i < state.size(); i++) {
117 vm.uint_internal_state[i] = reduce_to_modulus<Field>(state[i]);
118 vm.field_internal_state[i] = Field(vm.uint_internal_state[i]);
119 }
120}
121
137template <typename Field>
139 FieldVM<Field>& vm, const unsigned char* data, size_t size, const VMPhaseHeader& header, size_t& data_offset)
140{
141 // Set the step limit for this phase
142 vm.set_max_steps(header.steps);
143
144 // Run the VM for this phase and get the number of bytes consumed
145 size_t bytes_consumed = vm.run(data + data_offset, size - data_offset, true);
146
147 // If no bytes were consumed, there wasn't enough data
148 if (bytes_consumed == 0) {
149 return 0; // Not enough data for settings
150 }
151
152 // Check internal state consistency
153 if (!vm.check_internal_state()) {
154 std::cout << "Internal state check failed for field type: " << typeid(Field).name() << std::endl;
155 return -1; // Error
156 }
157
158 // Export the state for potential import into next VM
159 auto exported_state = vm.export_uint_state();
160
161 // Update data offset based on how much data the VM actually consumed
162 data_offset += bytes_consumed;
163
164 return static_cast<int>(bytes_consumed); // Success
165}
166
182template <typename Field>
183int run_field_vm(const VMPhaseHeader& header,
184 const unsigned char* Data,
185 size_t Size,
186 size_t& data_offset,
187 std::vector<numeric::uint256_t>& current_state)
188{
189 FieldVM<Field> vm(false, header.steps);
190 if (!current_state.empty()) {
191 import_state_with_reduction<Field>(vm, current_state);
192 }
193 int phase_result = run_vm_phase(vm, Data, Size, header, data_offset);
194 if (phase_result > 0) {
195 current_state = vm.export_uint_state();
196 }
197 return phase_result;
198}
199
214template <typename Field>
215void run_debug_vm(const VMPhaseHeader& header,
216 const unsigned char* Data,
217 size_t Size,
218 size_t original_data_offset,
219 const std::vector<numeric::uint256_t>& current_state)
220{
221 FieldVM<Field> vm_debug(true, header.steps);
222 if (!current_state.empty()) {
223 std::cout << "Importing state with " << current_state.size() << " elements" << std::endl;
224 import_state_with_reduction<Field>(vm_debug, current_state);
225 // Verify initial state is correctly loaded
226 assert(vm_debug.verify_initial_state(current_state));
227 std::cout << "State import verified successfully" << std::endl;
228 } else {
229 std::cout << "No state to import" << std::endl;
230 }
231 // Run debug VM on the same data that the pre-debug VM ran on
232 size_t bytes_consumed =
233 vm_debug.run(Data + original_data_offset + PHASE_HEADER_SIZE, Size - original_data_offset - PHASE_HEADER_SIZE);
234 std::cout << "Debug VM consumed " << bytes_consumed << " bytes" << std::endl;
235 if (vm_debug.check_internal_state()) {
236 std::cout << "[Debug VM] State is still correct after execution! No discrepancy detected." << std::endl;
237 // If debug VM reports state is correct, then there's no real discrepancy
238 return; // Don't treat this as a failure
239 } else {
240 std::cout << "[Debug VM] State discrepancy detected! This is a real failure." << std::endl;
241 assert(false); // Only assert if there's a real state discrepancy
242 }
243}
244
257extern "C" int LLVMFuzzerTestOneInput(const unsigned char* Data, size_t Size)
258{
259 if (Size < PHASE_HEADER_SIZE) {
260 return 0; // Not enough data for at least one phase header
261 }
262
264 size_t data_offset = 0;
265
266 while (data_offset + PHASE_HEADER_SIZE <= Size) {
267 // Read phase header
268 const VMPhaseHeader* header_ptr = reinterpret_cast<const VMPhaseHeader*>(Data + data_offset);
269 VMPhaseHeader header = *header_ptr;
270
271 // Always select a valid field type and step count
272 FieldType selected_field_type = static_cast<FieldType>(header.field_type % NUM_FIELD_TYPES);
273 uint8_t selected_steps = header.steps % MAX_STEPS;
274 if (selected_steps == 0) {
275 selected_steps = 1; // Always do at least one step
276 }
277 header.field_type = static_cast<uint8_t>(selected_field_type);
278 header.steps = selected_steps;
279
280 // Execute the phase based on field type
281 int phase_result = 0;
282 switch (selected_field_type) {
283 case FieldType::BN254_FQ: {
284 phase_result = run_field_vm<fq>(header, Data, Size, data_offset, current_state);
285 break;
286 }
287 case FieldType::BN254_FR: {
288 phase_result = run_field_vm<fr>(header, Data, Size, data_offset, current_state);
289 break;
290 }
292 phase_result = run_field_vm<secp256k1::fq>(header, Data, Size, data_offset, current_state);
293 break;
294 }
296 phase_result = run_field_vm<secp256k1::fr>(header, Data, Size, data_offset, current_state);
297 break;
298 }
300 phase_result = run_field_vm<secp256r1::fq>(header, Data, Size, data_offset, current_state);
301 break;
302 }
304 phase_result = run_field_vm<secp256r1::fr>(header, Data, Size, data_offset, current_state);
305 break;
306 }
307 }
308
309 if (phase_result < 0) {
310 // If a phase fails (internal state error), run with debug VM to get more information
311 std::cout << "Phase failed for field type: " << static_cast<int>(selected_field_type)
312 << " with steps: " << static_cast<int>(header.steps) << std::endl;
313
314 // Show the instruction stream that caused the failure
315 std::cout << "Instruction stream (first 64 bytes): ";
316 size_t debug_size = std::min(Size - data_offset - PHASE_HEADER_SIZE, size_t(64));
317 for (size_t i = 0; i < debug_size; i++) {
318 printf("%02x ", Data[data_offset + PHASE_HEADER_SIZE + i]);
319 }
321
322 // Run debug VM based on field type
323 switch (selected_field_type) {
325 run_debug_vm<fq>(header, Data, Size, data_offset, current_state);
326 break;
328 run_debug_vm<fr>(header, Data, Size, data_offset, current_state);
329 break;
331 run_debug_vm<secp256k1::fq>(header, Data, Size, data_offset, current_state);
332 break;
334 run_debug_vm<secp256k1::fr>(header, Data, Size, data_offset, current_state);
335 break;
337 run_debug_vm<secp256r1::fq>(header, Data, Size, data_offset, current_state);
338 break;
340 run_debug_vm<secp256r1::fr>(header, Data, Size, data_offset, current_state);
341 break;
342 }
343 return 1;
344 } else if (phase_result == 0) {
345 // Not enough data for this phase, stop processing
346 break;
347 }
348 // phase_result > 0 means success, continue to next phase
349 }
350
351 return 0;
352}
const std::vector< FF > data
Field arithmetic fuzzer for testing cryptographic field operations.
FieldType
Enumeration of supported field types for the multi-field fuzzer.
@ BN254_FQ
BN254 curve base field.
@ SECP256R1_FR
Secp256r1 curve scalar field.
@ BN254_FR
BN254 curve scalar field.
@ SECP256K1_FR
Secp256k1 curve scalar field.
@ SECP256K1_FQ
Secp256k1 curve base field.
@ SECP256R1_FQ
Secp256r1 curve base field.
int run_field_vm(const VMPhaseHeader &header, const unsigned char *Data, size_t Size, size_t &data_offset, std::vector< numeric::uint256_t > &current_state)
Creates and runs a VM for a specific field type with state management.
int run_vm_phase(FieldVM< Field > &vm, const unsigned char *data, size_t size, const VMPhaseHeader &header, size_t &data_offset)
Runs a single VM phase with error handling and state management.
const size_t PHASE_HEADER_SIZE
void run_debug_vm(const VMPhaseHeader &header, const unsigned char *Data, size_t Size, size_t original_data_offset, const std::vector< numeric::uint256_t > &current_state)
Runs a debug VM to analyze failed phases.
FieldVM< Field > create_field_vm(size_t max_steps)
Creates a field VM with specified maximum steps.
numeric::uint256_t reduce_to_modulus(const numeric::uint256_t &value)
Reduces a uint256_t value to be less than the field's modulus.
constexpr size_t MAX_STEPS
Maximum number of VM steps per phase.
void import_state_with_reduction(FieldVM< Field > &vm, const std::vector< numeric::uint256_t > &state)
Imports state into a field VM with automatic modulus reduction.
constexpr size_t NUM_FIELD_TYPES
Total number of supported field types.
int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size)
Main fuzzer entry point for multi-field testing.
Entry point for Barretenberg command-line interface.
const size_t INTERNAL_STATE_SIZE
Constant defining the number of elements in the VM's internal state.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Header structure for each VM execution phase.
uint8_t field_type
Field type identifier (0-5)
uint8_t steps
Number of VM steps to execute (0-63)
Virtual machine for field arithmetic operations.
size_t run(const unsigned char *Data, size_t Size, bool reset_steps=true)
Run the VM on input data.
std::vector< numeric::uint256_t > export_uint_state() const
Export the final uint state as a vector of uint256_t values.
std::array< numeric::uint256_t, INTERNAL_STATE_SIZE > uint_internal_state
Internal state array of uint256_t values.
void set_max_steps(size_t new_max_steps)
Set a new step limit for the VM.
std::array< Field, INTERNAL_STATE_SIZE > field_internal_state
Internal state array of field elements.
bool verify_initial_state(const std::vector< numeric::uint256_t > &state) const
Verify that the initial state is correctly loaded.
bool check_internal_state() const
Check internal state consistency between field and uint256_t representations.