10#define EXPAND(arg) EXPAND1(EXPAND1(EXPAND1(EXPAND1(arg))))
11#define EXPAND1(arg) EXPAND2(EXPAND2(EXPAND2(EXPAND2(arg))))
12#define EXPAND2(arg) EXPAND3(EXPAND3(EXPAND3(EXPAND3(arg))))
13#define EXPAND3(arg) EXPAND4(EXPAND4(EXPAND4(EXPAND4(arg))))
14#define EXPAND4(arg) arg
16#define FOR_EACH(macro, ...) __VA_OPT__(EXPAND(FOR_EACH_HELPER(macro, __VA_ARGS__)))
17#define FOR_EACH_HELPER(macro, a1, ...) macro(a1) __VA_OPT__(FOR_EACH_AGAIN PARENS(macro, __VA_ARGS__))
18#define FOR_EACH_AGAIN() FOR_EACH_HELPER
20#define ALL_POSSIBLE_OPCODES \
21 CONSTANT, WITNESS, CONSTANT_WITNESS, ADD, SUBTRACT, MULTIPLY, DIVIDE, ADD_TWO, MADD, MULT_MADD, MSUB_DIV, SQR, \
22 ASSERT_EQUAL, ASSERT_NOT_EQUAL, SQR_ADD, ASSERT_EQUAL, ASSERT_NOT_EQUAL, SQR_ADD, SUBTRACT_WITH_CONSTRAINT, \
23 DIVIDE_WITH_CONSTRAINTS, SLICE, ASSERT_ZERO, ASSERT_NOT_ZERO, COND_NEGATE, ADD_MULTI, ASSERT_VALID, \
24 COND_SELECT, DOUBLE, RANDOMSEED, SELECT_IF_ZERO, SELECT_IF_EQ, REVERSE, GET_BIT, SET_BIT, SET, INVERT, AND, \
25 OR, XOR, MODULO, SHL, SHR, ROL, ROR, NOT, BATCH_MUL, COND_ASSIGN
70 state =
static_cast<uint32_t
>(
71 (
static_cast<uint64_t
>(
state) *
static_cast<uint64_t
>(363364578) +
static_cast<uint64_t
>(537)) %
72 static_cast<uint64_t
>(3758096939));
88template <
typename T>
static inline uint256_t fast_log_distributed_uint256(T& rng)
93 uint16_t* p = (uint16_t*)&temp;
94 uint8_t mask_size =
static_cast<uint8_t
>(rng.next() & 0xff);
95 for (
size_t i = 0; i < 16; i++) {
96 *(p + i) =
static_cast<uint16_t
>(rng.next() & 0xffff);
110 uint64_t parts[4] = { 0, 0, 0, 0 };
112 for (
size_t i = 0; i < (buffer_size + 7) / 8; i++) {
113 size_t to_read = (buffer_size - (i * 8)) < 8 ? buffer_size - (i * 8) : 8;
116 return uint256_t(parts[0], parts[1], parts[2], parts[3]);
120#ifdef FUZZING_SHOW_INFORMATION
127template <
typename... Args>
inline void debug_log(Args&&... args)
134#ifdef FUZZING_SHOW_INFORMATION
139struct FormattedArgs {
152template <
typename Stack>
153inline FormattedArgs format_single_arg(
const Stack& stack,
size_t first_index,
size_t output_index)
155 std::string rhs = stack[first_index].cycle_group.is_constant() ?
"c" :
"w";
156 std::string out = rhs;
158 out +=
std::to_string(output_index >= stack.size() ? stack.size() : output_index);
159 out = (output_index >= stack.size() ?
"auto " :
"") + out;
160 return FormattedArgs{ .lhs =
"", .rhs = rhs, .out = out };
171template <
typename Stack>
172inline FormattedArgs format_two_arg(
const Stack& stack,
size_t first_index,
size_t second_index,
size_t output_index)
174 std::string lhs = stack[first_index].cycle_group.is_constant() ?
"c" :
"w";
175 std::string rhs = stack[second_index].cycle_group.is_constant() ?
"c" :
"w";
177 (stack[first_index].cycle_group.is_constant() && stack[second_index].cycle_group.is_constant()) ?
"c" :
"w";
180 out +=
std::to_string(output_index >= stack.size() ? stack.size() : output_index);
181 out = (output_index >= stack.size() ?
"auto " :
"") + out;
182 return FormattedArgs{ .lhs = lhs, .rhs = rhs, .out = out };
203 std::make_tuple(T::CONSTANT,
216 T::SUBTRACT_WITH_CONSTRAINT,
217 T::DIVIDE_WITH_CONSTRAINTS,
232 std::make_tuple(T::GEN_MUTATION_COUNT_LOG, T::GEN_STRUCTURAL_MUTATION_PROBABILITY)
234 T::GEN_MUTATION_COUNT_LOG <= 7;
243 typename T::ArgSizes;
244 typename T::Instruction;
245 typename T::ExecutionState;
246 typename T::ExecutionHandler;
267template <
typename T,
typename Composer,
typename Context>
280 typename T::InstructionWeights;
281 T::InstructionWeights::_LIMIT;
293template <
typename T,
typename FF>
294inline static FF mutateFieldElement(
FF e, T& rng)
300 bool convert_to_montgomery = (rng.next() & 1);
303#define MONT_CONVERSION_LOCAL \
304 if (convert_to_montgomery) { \
305 value_data = uint256_t(e.to_montgomery_form()); \
307 value_data = uint256_t(e); \
310#define INV_MONT_CONVERSION_LOCAL \
311 if (convert_to_montgomery) { \
312 e = FF(value_data).from_montgomery_form(); \
314 e = FF(value_data); \
319 const size_t choice = rng.next() % 4;
327 }
else if (choice < 3) {
330 if (convert_to_montgomery) {
331 e = e.to_montgomery_form();
333 if (rng.next() & 1) {
334 e +=
FF(rng.next() & 0xff);
336 e -=
FF(rng.next() & 0xff);
338 if (convert_to_montgomery) {
339 e = e.from_montgomery_form();
344 switch (rng.next() % 8) {
373 if (convert_to_montgomery) {
374 e = e.from_montgomery_form();
398 const size_t instructions_count = instructions.size();
399 if (instructions_count <= 2) {
402 const size_t first_element_index = rng.
next() % instructions_count;
403 size_t second_element_index = rng.
next() % instructions_count;
404 if (first_element_index == second_element_index) {
405 second_element_index = (second_element_index + 1) % instructions_count;
407 std::iter_swap(instructions.begin() +
static_cast<int>(first_element_index),
408 instructions.begin() +
static_cast<int>(second_element_index));
423 const size_t instructions_count = instructions.size();
424 if (instructions_count == 0) {
427 if ((rng.
next() & 1) != 0U) {
428 instructions.erase(instructions.begin() + (rng.
next() % instructions_count));
431 const size_t max_deletion_log =
432 std::min(
static_cast<size_t>(64 - __builtin_clzll(
static_cast<uint64_t
>(instructions_count)) - 1),
435 if (max_deletion_log == 0) {
438 const size_t deletion_size = 1 << (rng.
next() % max_deletion_log);
439 const size_t start = rng.
next() % (instructions_count + 1 - deletion_size);
440 instructions.erase(instructions.begin() +
static_cast<int>(start),
441 instructions.begin() +
static_cast<int>(start + deletion_size));
455 const size_t instructions_count = instructions.size();
456 if (instructions_count == 0) {
460 typename T::Instruction chosen_instruction = instructions[rng.
next() % instructions_count];
462 instructions.begin() + (rng.
next() % (instructions_count + 1)), duplication_size, chosen_instruction);
468 (void)havoc_settings;
469 instructions.insert(instructions.begin() +
static_cast<int>(rng.
next() % (instructions.size() + 1)),
470 T::Instruction::template generateRandom<FastRandom>(rng));
485 const size_t choice = rng.
next() % prob_pool;
510 const size_t instructions_count = instructions.size();
511 if (instructions_count == 0) {
514 const size_t chosen = rng.
next() % instructions_count;
515 instructions[chosen] =
516 T::Instruction::template mutateInstruction<FastRandom>(instructions[chosen], rng, havoc_settings);
525 const size_t mutation_count = 1 << T::HavocConfig::MUTATION_COUNT_LOG;
529 for (
size_t i = 0; i < mutation_count; i++) {
530 uint32_t val = rng.
next();
559 const size_t vecA_size = vecA.size();
560 const size_t vecB_size = vecB.size();
562 if (vecA_size == 0) {
565 if (vecB_size == 0) {
570 const size_t final_result_size = rng.
next() % (vecA_size + vecB_size) + 1;
573 size_t* inIndex = &indexA;
574 size_t inSize = vecA_size;
575 auto inIterator = vecA.begin();
576 size_t current_result_size = 0;
577 bool currentlyUsingA =
true;
579 while (current_result_size < final_result_size && (indexA < vecA_size || indexB < vecB_size)) {
581 size_t result_size_left = final_result_size - current_result_size;
583 if (*inIndex < inSize) {
585 size_t inSizeLeft = inSize - *inIndex;
586 size_t maxExtraSize = std::min(result_size_left, inSizeLeft);
587 if (maxExtraSize != 0) {
589 size_t copySize = (rng.
next() % maxExtraSize) + 1;
590 result.insert(result.begin() +
static_cast<long>(current_result_size),
591 inIterator +
static_cast<long>((*inIndex)),
593 inIterator +
static_cast<long>((*inIndex) + copySize));
595 *inIndex += copySize;
596 current_result_size += copySize;
600 inIndex = currentlyUsingA ? &indexB : &indexA;
601 inSize = currentlyUsingA ? vecB_size : vecA_size;
602 inIterator = currentlyUsingA ? vecB.begin() : vecA.begin();
603 currentlyUsingA = !currentlyUsingA;
619 auto* pData =
const_cast<uint8_t*
>(Data);
620 size_t size_left = Size;
621 while (size_left != 0) {
622 uint8_t chosen_operation = *pData;
627#define PARSE_OPCODE(name) \
628 if constexpr (requires { T::ArgSizes::name; }) \
629 if constexpr (T::ArgSizes::name != size_t(-1)) { \
630 if (chosen_operation == T::Instruction::OPCODE::name) { \
631 if (size_left < T::ArgSizes::name) { \
632 return fuzzingInstructions; \
634 fuzzingInstructions.push_back( \
635 T::Parser::template parseInstructionArgs<T::Instruction::OPCODE::name>(pData)); \
636 size_left -= T::ArgSizes::name; \
637 pData += T::ArgSizes::name; \
642#define PARSE_ALL_OPCODES(...) FOR_EACH(PARSE_OPCODE, __VA_ARGS__)
646 return fuzzingInstructions;
660 uint8_t* pData = Data;
661 size_t size_left = MaxSize;
664#define WRITE_OPCODE_IF(name) \
665 if constexpr (requires { T::ArgSizes::name; }) \
666 if constexpr (T::ArgSizes::name != (size_t)-1) { \
667 if (instruction.id == T::Instruction::OPCODE::name) { \
668 if (size_left >= (T::ArgSizes::name + 1)) { \
669 T::Parser::template writeInstruction<T::Instruction::OPCODE::name>(instruction, pData); \
670 size_left -= (T::ArgSizes::name + 1); \
671 pData += (T::ArgSizes::name + 1); \
673 return MaxSize - size_left; \
679#define WRITE_ALL_OPCODES(...) FOR_EACH(WRITE_OPCODE_IF, __VA_ARGS__)
683 return MaxSize - size_left;
692 template <
typename Composer>
698 typename T::ExecutionState state;
699 Composer composer = Composer();
702 size_t total_instruction_weight = 0;
703 (void)total_instruction_weight;
706#define EXECUTE_OPCODE_IF(name) \
707 if constexpr (requires { T::ArgSizes::name; }) \
708 if constexpr (T::ArgSizes::name != size_t(-1)) { \
709 if (instruction.id == T::Instruction::OPCODE::name) { \
710 if constexpr (InstructionWeightsEnabled<T>) { \
711 if (!((total_instruction_weight + T::InstructionWeights::name) > T::InstructionWeights::_LIMIT)) { \
712 total_instruction_weight += T::InstructionWeights::name; \
713 if (T::ExecutionHandler::execute_##name(&composer, state, instruction)) { \
721 if (T::ExecutionHandler::execute_##name(&composer, state, instruction)) { \
727#define EXECUTE_ALL_OPCODES(...) FOR_EACH(EXECUTE_OPCODE_IF, __VA_ARGS__)
731 bool final_value_check =
true;
734 final_value_check = T::postProcess(&composer, state);
736#ifdef FUZZING_SHOW_INFORMATION
737 if (!final_value_check) {
743#ifndef FUZZING_DISABLE_WARNINGS
745 info(
"circuit should fail");
754 if (!final_value_check) {
783template <
template <
typename>
class Fuzzer,
typename Composer>
791template <
template <
typename>
class Fuzzer, uint64_t Composers>
794 RunWithBuilder<Fuzzer, bb::UltraCircuitBuilder>(Data, Size,
VarianceRNG);
#define BB_ASSERT_LTE(left, right,...)
bb::field< bb::Bn254FrParams > FF
FastRandom VarianceRNG(0)
A templated class containing most of the fuzzing logic for a generic Arithmetic class.
static void mutateInstructionVector(std::vector< typename T::Instruction > &instructions, FastRandom &rng)
static size_t writeInstructionsToBuffer(std::vector< typename T::Instruction > &instructions, uint8_t *Data, size_t MaxSize)
Write instructions into the buffer until there are no instructions left or there is no more space.
static void duplicateInstruction(std::vector< typename T::Instruction > &instructions, FastRandom &rng, HavocSettings &havoc_settings)
Mutator duplicating an instruction.
static std::vector< typename T::Instruction > parseDataIntoInstructions(const uint8_t *Data, size_t Size)
Parses a given data buffer into a vector of instructions for testing the arithmetic.
static void swapTwoInstructions(std::vector< typename T::Instruction > &instructions, FastRandom &rng)
Mutator swapping two instructions together.
static size_t MutateInstructionBuffer(uint8_t *Data, size_t Size, size_t MaxSize, FastRandom &rng)
Interpret the data buffer as a series of arithmetic instructions and mutate it accordingly.
static std::vector< typename T::Instruction > crossoverInstructionVector(const std::vector< typename T::Instruction > &vecA, const std::vector< typename T::Instruction > &vecB, FastRandom &rng)
Splice two instruction vectors into one randomly.
static void deleteInstructions(std::vector< typename T::Instruction > &instructions, FastRandom &rng, HavocSettings &havoc_settings)
Mutator, deleting a sequence of instructions.
static void executeInstructions(std::vector< typename T::Instruction > &instructions)
Execute instructions in a loop.
static void insertRandomInstruction(std::vector< typename T::Instruction > &instructions, FastRandom &rng, HavocSettings &havoc_settings)
static void mutateInstructionValue(std::vector< typename T::Instruction > &instructions, FastRandom &rng, HavocSettings &havoc_settings)
Choose a random instruction from the vector and mutate it.
static void mutateInstructionStructure(std::vector< typename T::Instruction > &instructions, FastRandom &rng, HavocSettings &havoc_settings)
Mutator for instruction structure.
Class for quickly deterministically creating new random values. We don't care about distribution much...
FastRandom(uint32_t seed)
void reseed(uint32_t seed)
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
Concept specifying the class used by the fuzzer.
Fuzzer uses only composers with check_circuit function.
Concept for Havoc Configurations.
Concept for forcing ArgumentSizes to be size_t.
This concept is used when we want to limit the number of executions of certain instructions (for exam...
The fuzzer can use a postprocessing function that is specific to the type being fuzzed.
Concept for a simple PRNG which returns a uint32_t when next is called.
const std::vector< MemoryValue > data
StrictMock< MockContext > context
#define INV_MONT_CONVERSION_LOCAL
#define WRITE_ALL_OPCODES(...)
uint256_t read_uint256(const uint8_t *data, size_t buffer_size=32)
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize)
void debug_log(Args &&... args)
Compile-time debug logging helper.
constexpr void RunWithBuilder(const uint8_t *Data, const size_t Size, FastRandom &VarianceRNG)
#define EXECUTE_ALL_OPCODES(...)
#define PARSE_ALL_OPCODES(...)
#define ALL_POSSIBLE_OPCODES
constexpr bool SHOW_FUZZING_INFO
#define MONT_CONVERSION_LOCAL
constexpr void RunWithBuilders(const uint8_t *Data, const size_t Size, FastRandom &VarianceRNG)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
std::string to_string(bb::avm2::ValueTag tag)
size_t GEN_VALUE_MUTATION_PROBABILITY
size_t ST_MUT_MAXIMUM_DELETION_LOG
size_t GEN_MUTATION_COUNT_LOG
size_t ST_MUT_MAXIMUM_DUPLICATION_LOG
size_t VAL_MUT_LLVM_MUTATE_PROBABILITY
size_t ST_MUT_DELETION_PROBABILITY
size_t VAL_MUT_SMALL_ADDITION_PROBABILITY
size_t ST_MUT_DUPLICATION_PROBABILITY
size_t VAL_MUT_SPECIAL_VALUE_PROBABILITY
std::vector< size_t > value_mutation_distribution
size_t GEN_STRUCTURAL_MUTATION_PROBABILITY
size_t VAL_MUT_MONTGOMERY_PROBABILITY
size_t VAL_MUT_NON_MONTGOMERY_PROBABILITY
size_t ST_MUT_SWAP_PROBABILITY
size_t GEN_LLVM_POST_MUTATION_PROB
std::vector< size_t > structural_mutation_distribution
size_t ST_MUT_INSERTION_PROBABILITY
static constexpr field get_root_of_unity(size_t subgroup_size) noexcept
static constexpr field one()
static constexpr uint256_t modulus
constexpr std::pair< bool, field > sqrt() const noexcept
Compute square root of the field element.
static constexpr field zero()