Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
byte_array.fuzzer.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
9#pragma clang diagnostic push
10#pragma clang diagnostic ignored "-Wc99-designator"
11
12#define MAX_ARRAY_SIZE 128
13
14// This is a global variable, so that the execution handling class could alter it and signal to the input tester that
15// the input should fail
17
18#define HAVOC_TESTING
19
22
23// Enable this definition, when you want to find out the instructions that caused a failure
24// #define FUZZING_SHOW_INFORMATION 1
25#ifdef FUZZING_SHOW_INFORMATION
26#define PREP_SINGLE_ARG(stack, first_index, output_index) \
27 std::string rhs = "c"; \
28 std::string out = rhs; \
29 rhs += std::to_string(first_index); \
30 out += std::to_string(output_index >= stack.size() ? stack.size() : output_index); \
31 out = (output_index >= stack.size() ? "auto " : "") + out;
32
33#define PREP_TWO_ARG(stack, first_index, second_index, output_index) \
34 std::string lhs = "c"; \
35 std::string rhs = "c"; \
36 std::string out = "c"; \
37 lhs += std::to_string(first_index); \
38 rhs += std::to_string(second_index); \
39 out += std::to_string(output_index >= stack.size() ? stack.size() : output_index); \
40 out = (output_index >= stack.size() ? "auto " : "") + out;
41#endif
42
43#define OPERATION_TYPE_SIZE 1
44
45#define ELEMENT_SIZE (sizeof(fr) + 1)
46#define TWO_IN_ONE_OUT 3
47#define THREE_IN_ONE_OUT 4
48#define SLICE_ARGS_SIZE 6
49
54template <typename Builder> class ByteArrayFuzzBase {
55 private:
58 template <class From, class To> static To from_to(const From& in, const std::optional<size_t> size = std::nullopt)
59 {
60 return To(in.data(), in.data() + (size ? *size : in.size()));
61 }
62
63 public:
69 public:
71 struct Element {
72 public:
73 std::array<uint8_t, MAX_ARRAY_SIZE> data;
74 uint16_t size;
75
76 uint16_t real_size(void) const { return std::min(size, static_cast<uint16_t>(MAX_ARRAY_SIZE)); }
77 std::vector<uint8_t> as_vector(void) const
78 {
79 return from_to<decltype(data), std::vector<uint8_t>>(data, real_size());
80 }
81 std::string as_string(void) const { return from_to<decltype(data), std::string>(data, real_size()); }
82 };
83 struct TwoArgs {
84 uint8_t in;
85 uint8_t out;
86 };
87 struct ThreeArgs {
88 uint8_t in1;
89 uint8_t in2;
90 uint8_t out;
91 };
92 struct SliceArgs {
93 uint8_t in;
94 uint8_t out;
95 uint16_t offset;
96 uint16_t length;
97 };
98 struct GetBitArgs {
99 uint8_t in;
100 uint8_t out;
101 uint32_t bit;
102 };
103 struct SetBitArgs {
104 uint8_t in;
105 uint32_t bit;
106 uint8_t value;
107 };
108
118 // The type of instruction
120 // Instruction arguments
129 template <typename T>
130 inline static Instruction generateRandom(T& rng)
131 requires SimpleRng<T>
132 {
133 // Choose which instruction we are going to generate
134 OPCODE instruction_opcode = static_cast<OPCODE>(rng.next() % (OPCODE::_LAST));
135 uint8_t in1, in2, out;
136 uint16_t offset, length;
137 // Depending on instruction
138 switch (instruction_opcode) {
139 case OPCODE::CONSTANT:
140 // Return instruction
141 {
142 std::array<uint8_t, MAX_ARRAY_SIZE> data;
143 for (size_t i = 0; i < MAX_ARRAY_SIZE; i++) {
144 data[i] = rng.next() & 0xFF;
145 }
146
147 const uint16_t size = rng.next() & 0xFFFF;
148 return { .id = instruction_opcode, .arguments.element = { .data = data, .size = size } };
149 }
150 break;
151 case OPCODE::REVERSE:
152 case OPCODE::SET:
153 in1 = static_cast<uint8_t>(rng.next() & 0xff);
154 out = static_cast<uint8_t>(rng.next() & 0xff);
155 return { .id = instruction_opcode, .arguments.twoArgs = { .in = in1, .out = out } };
156 break;
157 case OPCODE::SLICE:
158 in1 = static_cast<uint8_t>(rng.next() & 0xff);
159 out = static_cast<uint8_t>(rng.next() & 0xff);
160 offset = static_cast<uint16_t>(rng.next() & 0xffff);
161 length = static_cast<uint16_t>(rng.next() & 0xffff);
162 return { .id = instruction_opcode,
163 .arguments.sliceArgs = { .in = in1, .out = out, .offset = offset, .length = length } };
164 case OPCODE::ADD:
165 // For two-input-one-output instructions we just randomly pick each argument and generate an instruction
166 // accordingly
167 in1 = static_cast<uint8_t>(rng.next() & 0xff);
168 in2 = static_cast<uint8_t>(rng.next() & 0xff);
169 out = static_cast<uint8_t>(rng.next() & 0xff);
170 return { .id = instruction_opcode, .arguments.threeArgs = { .in1 = in1, .in2 = in2, .out = out } };
172 return { .id = instruction_opcode, .arguments.randomseed = rng.next() };
173 break;
174 default:
175 abort(); // We have missed some instructions, it seems
176 break;
177 }
178 }
179
189 template <typename T>
191 requires SimpleRng<T>
192 {
193 (void)rng;
194 (void)havoc_config;
195#define PUT_RANDOM_BYTE_IF_LUCKY(variable) \
196 if (rng.next() & 1) { \
197 variable = rng.next() & 0xff; \
198 }
199#define PUT_RANDOM_TWO_BYTES_IF_LUCKY(variable) \
200 if (rng.next() & 1) { \
201 variable = rng.next() & 0xffff; \
202 }
203#define PUT_RANDOM_FOUR_BYTES_IF_LUCKY(variable) \
204 if (rng.next() & 1) { \
205 variable = rng.next() & 0xffffffff; \
206 }
207 // Depending on instruction type...
208 switch (instruction.id) {
209 case OPCODE::CONSTANT:
210 break;
211 case OPCODE::REVERSE:
212 case OPCODE::SET:
213 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.twoArgs.in)
214 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.twoArgs.out)
215 break;
216 case OPCODE::SLICE:
217 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.sliceArgs.in)
218 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.sliceArgs.out)
219 PUT_RANDOM_TWO_BYTES_IF_LUCKY(instruction.arguments.sliceArgs.offset)
220 PUT_RANDOM_TWO_BYTES_IF_LUCKY(instruction.arguments.sliceArgs.length)
221 break;
222 case OPCODE::ADD:
223 // Randomly sample each of the arguments with 50% probability
224 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.in1)
225 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.in2)
226 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.out)
227 break;
229 instruction.arguments.randomseed = rng.next();
230 break;
231 default:
232 abort(); // New instruction encountered
233 break;
234 }
235 // Return mutated instruction
236 return instruction;
237 }
238 };
239 // We use argsizes to both specify the size of data needed to parse the instruction and to signal that the
240 // instruction is enabled (if it is -1,it's disabled )
241 class ArgSizes {
242 public:
243 static constexpr size_t CONSTANT = MAX_ARRAY_SIZE + sizeof(uint16_t);
244 static constexpr size_t REVERSE = 2;
245 static constexpr size_t SLICE = 6;
246 static constexpr size_t ADD = 3;
247 static constexpr size_t SET = 2;
248 static constexpr size_t RANDOMSEED = sizeof(uint32_t);
249 };
254 class Parser {
255 public:
263 template <typename Instruction::OPCODE opcode> inline static Instruction parseInstructionArgs(uint8_t* Data)
264 {
265 if constexpr (opcode == Instruction::OPCODE::CONSTANT) {
266 std::array<uint8_t, MAX_ARRAY_SIZE> data;
267 std::copy_n(Data, data.size(), data.begin());
268
269 uint16_t size;
270 memcpy(&size, Data + MAX_ARRAY_SIZE, sizeof(uint16_t));
271
272 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
273 .arguments.element = { .data = data, .size = size } };
274 }
275 if constexpr (opcode == Instruction::OPCODE::REVERSE || opcode == Instruction::OPCODE::SET) {
276 return { .id = static_cast<typename Instruction::OPCODE>(opcode),
277 .arguments.twoArgs = { .in = *Data, .out = *(Data + 1) } };
278 }
279 if constexpr (opcode == Instruction::OPCODE::SLICE) {
280 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
281 .arguments.sliceArgs = { .in = *Data,
282 .out = *(Data + 1),
283 .offset = *((uint16_t*)(Data + 2)),
284 .length = *((uint16_t*)(Data + 4)) } };
285 }
286 if constexpr (opcode == Instruction::OPCODE::ADD) {
287 return { .id = static_cast<typename Instruction::OPCODE>(opcode),
288 .arguments.threeArgs = { .in1 = *Data, .in2 = *(Data + 1), .out = *(Data + 2) } };
289 }
290 if constexpr (opcode == Instruction::OPCODE::RANDOMSEED) {
291 uint32_t randomseed;
292 memcpy(&randomseed, Data, sizeof(uint32_t));
293 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
294 .arguments.randomseed = randomseed };
295 };
296 }
304 template <typename Instruction::OPCODE instruction_opcode>
305 inline static void writeInstruction(Instruction& instruction, uint8_t* Data)
306 {
307 if constexpr (instruction_opcode == Instruction::OPCODE::CONSTANT) {
308 *Data = instruction.id;
309 memcpy(Data, instruction.arguments.element.data.data(), MAX_ARRAY_SIZE);
310 memcpy(Data + MAX_ARRAY_SIZE, &instruction.arguments.element.size, sizeof(uint16_t));
311 }
312 if constexpr (instruction_opcode == Instruction::OPCODE::REVERSE ||
313 instruction_opcode == Instruction::OPCODE::SET) {
314 *Data = instruction.id;
315 *(Data + 1) = instruction.arguments.twoArgs.in;
316 *(Data + 2) = instruction.arguments.twoArgs.out;
317 }
318 if constexpr (instruction_opcode == Instruction::OPCODE::SLICE) {
319 *Data = instruction.id;
320 *(Data + 1) = instruction.arguments.sliceArgs.in;
321 *(Data + 2) = instruction.arguments.sliceArgs.out;
322 *((uint16_t*)(Data + 3)) = instruction.arguments.sliceArgs.offset;
323 *((uint16_t*)(Data + 5)) = instruction.arguments.sliceArgs.length;
324 }
325 if constexpr (instruction_opcode == Instruction::OPCODE::ADD) {
326 *Data = instruction.id;
327 *(Data + 1) = instruction.arguments.threeArgs.in1;
328 *(Data + 2) = instruction.arguments.threeArgs.in2;
329 *(Data + 3) = instruction.arguments.threeArgs.out;
330 }
331 if constexpr (instruction_opcode == Instruction::OPCODE::RANDOMSEED) {
332
333 *Data = instruction.id;
334 memcpy(Data + 1, &instruction.arguments.randomseed, sizeof(uint32_t));
335 }
336 }
337 };
343 public:
344 std::vector<uint8_t> reference_value;
345
346 byte_array_t byte_array{ nullptr, std::vector<uint8_t>{} };
347
348 static std::vector<uint8_t> get_value(const byte_array_t& byte_array) { return byte_array.get_value(); }
349 static const std::vector<uint8_t>& bool_to_vector(const bool& b)
350 {
351 static const std::vector<uint8_t> false_{ 0 };
352 static const std::vector<uint8_t> true_{ 1 };
353 return b ? true_ : false_;
354 }
356 {
357 const auto& ref = this->reference_value;
358
359 if (ref.size() > 32) {
360 /* Cannot construct via field if size is larger than field */
361 return std::nullopt;
362 } else if (ref.size() == 32) {
363 uint64_t u0, u1, u2, u3;
364 memcpy(&u3, ref.data(), 8);
365 memcpy(&u2, ref.data() + 8, 8);
366 memcpy(&u1, ref.data() + 16, 8);
367 memcpy(&u0, ref.data() + 24, 8);
368 const uint256_t u256{ htonll(u0), htonll(u1), htonll(u2), htonll(u3) };
369 if (max_msb != std::nullopt && u256.get_msb() >= max_msb) {
370 return std::nullopt;
371 }
372 if (u256 >= field_t::modulus) {
373 return std::nullopt;
374 }
375 }
376
377 return static_cast<field_t>(this->byte_array);
378 }
379 ExecutionHandler() = default;
380 ExecutionHandler(std::vector<uint8_t>& r, byte_array_t& s)
381 : reference_value(r)
382 , byte_array(s)
383 {}
384 ExecutionHandler(std::vector<uint8_t> r, byte_array_t s)
385 : reference_value(r)
386 , byte_array(s)
387 {}
389 : reference_value(get_value(s))
390 , byte_array(s)
391 {}
392
394 {
395 auto reversed = this->reference_value;
396 std::reverse(reversed.begin(), reversed.end());
397
398 return ExecutionHandler(reversed, this->byte_array.reverse());
399 }
400 ExecutionHandler slice(const size_t offset, const size_t length) const
401 {
402 if (offset > this->reference_value.size()) {
403 /* Offset is beyond buffer bounds; cannot comply.
404 * Return the whole buffer.
405 */
406 if (this->byte_array.size() == 0) {
407 return ExecutionHandler(this->reference_value, byte_array_t(this->byte_array));
408 }
409 return ExecutionHandler(this->reference_value, this->byte_array.slice(0));
410 } else if (offset + length > this->reference_value.size()) {
411 /* Offset is valid but range is not.
412 * Return data from the offset to the end of the buffer.
413 */
414 if (this->byte_array.size() == 0 && offset == 0) {
415 return ExecutionHandler(this->reference_value, byte_array_t(this->byte_array));
416 }
417 return ExecutionHandler(
418 std::vector<uint8_t>(this->reference_value.data() + offset,
419 this->reference_value.data() + this->reference_value.size()),
420 this->byte_array.slice(offset));
421 } else {
422 if (this->byte_array.size() == 0 && offset == 0 && length == 0) {
423 return ExecutionHandler(this->reference_value, byte_array_t(this->byte_array));
424 }
425 return ExecutionHandler(std::vector<uint8_t>(this->reference_value.data() + offset,
426 this->reference_value.data() + offset + length),
427 this->byte_array.slice(offset, length));
428 }
429 }
430
432 {
433 if (this->reference_value.size() + other.reference_value.size() > (MAX_ARRAY_SIZE * 3)) {
434 if (this->byte_array.size() == 0) {
435 return ExecutionHandler(this->reference_value, byte_array_t(this->byte_array));
436 }
437 return ExecutionHandler(this->reference_value, this->byte_array.slice(0));
438 } else {
439 const auto other_ref = other.reference_value;
440 this->reference_value.insert(this->reference_value.end(), other_ref.begin(), other_ref.end());
441
442 return ExecutionHandler(std::vector<uint8_t>(this->reference_value),
443 this->byte_array.write(other.byte_array));
444 }
445 }
446 /* Explicit re-instantiation using the various byte_array constructors */
448 {
449 const auto& ref = this->reference_value;
450
451 switch (VarianceRNG.next() % 4) {
452 case 0:
453#ifdef SHOW_INFORMATION
454 std::cout << "byte_array_t(e);" << std::cout;
455#endif
456 /* Construct via byte_array */
457 return ExecutionHandler(ref, byte_array_t(this->byte_array));
458 case 1:
459#ifdef SHOW_INFORMATION
460 std::cout << "e.get_value();" << std::cout;
461#endif
462 /* Construct via std::vector<uint8_t> */
464 // case 2 and 3: Removed - tested private bytes_t constructors (redundant with cases 0-1)
465 case 2: {
466 const auto field = to_field_t();
467
468 if (field == std::nullopt) {
469#ifdef SHOW_INFORMATION
470 std::cout << "byte_array_t(e);" << std::cout;
471#endif
472 return ExecutionHandler(ref, byte_array_t(this->byte_array));
473 } else {
474#ifdef SHOW_INFORMATION
475 std::cout << "tmp_f = " << e << ".to_field_t();" << std::cout;
476#endif
477 /* Pick a number ref.size()..32 */
478 const size_t num_bytes = ref.size() + (VarianceRNG.next() % (32 - ref.size() + 1));
479 if (num_bytes > 32)
480 abort(); /* Should never happen */
481
482 const size_t excess_bytes = num_bytes - ref.size();
483
484 /* Construct new reference value */
485 std::vector<uint8_t> new_ref(excess_bytes, 0);
486 new_ref.insert(new_ref.end(), ref.begin(), ref.end());
487
488 /* Construct via field_t */
489#ifdef SHOW_INFORMATION
490 std::cout << " = byte_array_t(*tmp_f, " << num_bytes << ");" << std::cout;
491#endif
492 return ExecutionHandler(new_ref, byte_array_t(*field, num_bytes));
493 }
494 }
495 case 3: {
496 /* Create a byte_array with gibberish.
497 *
498 * The purpose of this is to ascertain that no gibberish
499 * values are retained in the re-assigned value
500 */
501 const size_t gibberish_size = VarianceRNG.next() % (MAX_ARRAY_SIZE * 2);
502 std::vector<uint8_t> gibberish(gibberish_size);
503 for (size_t i = 0; i < gibberish_size; i++) {
504 gibberish[i] = static_cast<uint8_t>(VarianceRNG.next() % 0xFF);
505 }
506 auto ba = byte_array_t(builder, gibberish);
507
508 /* Construct via assignment */
509 ba = this->byte_array;
510
511 return ExecutionHandler(ref, ba);
512 } break;
513 default:
514 abort();
515 }
516 }
517
526 static inline size_t execute_CONSTANT(Builder* builder,
529 {
530 (void)builder;
531#ifdef FUZZING_SHOW_INFORMATION
532 std::cout << "std::array<uint8_t, 128> tmp_a = {";
533 for (size_t i = 0; i < instruction.arguments.element.size; i++) {
534 printf("0x%02X, ", instruction.arguments.element.data[i]);
535 }
536 std::cout << "};" << std::endl;
537 std::cout << "auto c" << stack.size() << " = ";
538#endif
539 if (static_cast<bool>(VarianceRNG.next() % 2)) {
540 stack.push_back(byte_array_t(builder, instruction.arguments.element.as_vector()));
541#ifdef FUZZING_SHOW_INFORMATION
542 std::cout << "byte_array_t(&builder, as_vector(tmp_a, " << instruction.arguments.element.size << "));"
543 << std::endl;
544#endif
545 } else {
546 stack.push_back(byte_array_t(builder, instruction.arguments.element.as_string()));
547#ifdef FUZZING_SHOW_INFORMATION
548 std::cout << "byte_array_t(&builder, as_string(tmp_a, " << instruction.arguments.element.size << "));"
549 << std::endl;
550#endif
551 }
552 return 0;
553 }
562 static inline size_t execute_REVERSE(Builder* builder,
565 {
566 (void)builder;
567 if (stack.size() == 0) {
568 return 1;
569 }
570 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
571 size_t output_index = instruction.arguments.twoArgs.out;
572
573#ifdef FUZZING_SHOW_INFORMATION
574 PREP_SINGLE_ARG(stack, first_index, output_index)
575 std::cout << out << " = " << rhs << ".reverse();" << std::endl;
576#endif
577 ExecutionHandler result;
578 result = stack[first_index].reverse();
579 // If the output index is larger than the number of elements in stack, append
580 if (output_index >= stack.size()) {
581 stack.push_back(result);
582 } else {
583 stack[output_index] = result;
584 }
585 return 0;
586 };
596 static inline size_t execute_SLICE(Builder* builder,
599 {
600 (void)builder;
601 if (stack.size() == 0) {
602 return 1;
603 }
604 size_t first_index = instruction.arguments.sliceArgs.in % stack.size();
605 size_t output_index = instruction.arguments.sliceArgs.out;
606 const uint16_t offset = instruction.arguments.sliceArgs.offset;
607 const uint16_t length = instruction.arguments.sliceArgs.length;
608
609#ifdef FUZZING_SHOW_INFORMATION
610 PREP_SINGLE_ARG(stack, first_index, output_index)
611 std::cout << out << " = " << rhs;
612 if (offset > stack[first_index].reference_value.size()) {
613 std::cout << ".slice(0);" << std::endl;
614 } else if (offset + length > stack[first_index].reference_value.size()) {
615 std::cout << ".slice(" << offset << ");" << std::endl;
616 } else {
617 std::cout << ".slice(" << offset << ", " << length << ");" << std::endl;
618 }
619#endif
620
621 ExecutionHandler result;
622 result = stack[first_index].slice(offset, length);
623 // If the output index is larger than the number of elements in stack, append
624 if (output_index >= stack.size()) {
625 stack.push_back(result);
626 } else {
627 stack[output_index] = result;
628 }
629 return 0;
630 }
639 static inline size_t execute_ADD(Builder* builder,
642 {
643 (void)builder;
644 if (stack.size() == 0) {
645 return 1;
646 }
647 size_t first_index = instruction.arguments.threeArgs.in1 % stack.size();
648 size_t second_index = instruction.arguments.threeArgs.in2 % stack.size();
649 size_t output_index = instruction.arguments.threeArgs.out;
650
651#ifdef FUZZING_SHOW_INFORMATION
652 PREP_TWO_ARG(stack, first_index, second_index, output_index)
653 if (stack[first_index].reference_value.size() + stack[second_index].reference_value.size() >
654 (MAX_ARRAY_SIZE * 3)) {
655 std::cout << out << " = " << lhs << ".slice(0);" << std::endl;
656 } else {
657 std::cout << out << " = " << lhs << "write(" << rhs << ");" << std::endl;
658 }
659#endif
660 ExecutionHandler result;
661 result = stack[first_index] + stack[second_index];
662 // If the output index is larger than the number of elements in stack, append
663 if (output_index >= stack.size()) {
664 stack.push_back(result);
665 } else {
666 stack[output_index] = result;
667 }
668 return 0;
669 };
678 static inline size_t execute_SET(Builder* builder,
681 {
682 (void)builder;
683 if (stack.size() == 0) {
684 return 1;
685 }
686 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
687 size_t output_index = instruction.arguments.twoArgs.out;
688
689#ifdef FUZZING_SHOW_INFORMATION
690 PREP_SINGLE_ARG(stack, first_index, output_index)
691 std::cout << "e = " << rhs;
692 std::cout << out << " = ";
693#endif
694
695 ExecutionHandler result;
696 result = stack[first_index].set(builder);
697 // If the output index is larger than the number of elements in stack, append
698 if (output_index >= stack.size()) {
699 stack.push_back(result);
700 } else {
701 stack[output_index] = result;
702 }
703 return 0;
704 };
713 static inline size_t execute_RANDOMSEED(Builder* builder,
716 {
717 (void)builder;
718 (void)stack;
719
720 VarianceRNG.reseed(instruction.arguments.randomseed);
721 return 0;
722 };
723 };
724
736 {
737 (void)builder;
738 for (size_t i = 0; i < stack.size(); i++) {
739 auto element = stack[i];
740 if (element.byte_array.get_value() != element.reference_value) {
741 return false;
742 }
743 }
744 return true;
745 }
746};
747
748#ifdef HAVOC_TESTING
749
750extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
751{
752 (void)argc;
753 (void)argv;
754 // These are the settings, optimized for the safeuint class (under them, fuzzer reaches maximum expected coverage in
755 // 40 seconds)
756 fuzzer_havoc_settings = HavocSettings{
757 .GEN_LLVM_POST_MUTATION_PROB = 30, // Out of 200
758 .GEN_MUTATION_COUNT_LOG = 5, // Fully checked
759 .GEN_STRUCTURAL_MUTATION_PROBABILITY = 300, // Fully checked
760 .GEN_VALUE_MUTATION_PROBABILITY = 700, // Fully checked
761 .ST_MUT_DELETION_PROBABILITY = 100, // Fully checked
762 .ST_MUT_DUPLICATION_PROBABILITY = 80, // Fully checked
763 .ST_MUT_INSERTION_PROBABILITY = 120, // Fully checked
764 .ST_MUT_MAXIMUM_DELETION_LOG = 6, // Fully checked
765 .ST_MUT_MAXIMUM_DUPLICATION_LOG = 2, // Fully checked
766 .ST_MUT_SWAP_PROBABILITY = 50, // Fully checked
767 .VAL_MUT_LLVM_MUTATE_PROBABILITY = 250, // Fully checked
768 .VAL_MUT_MONTGOMERY_PROBABILITY = 130, // Fully checked
769 .VAL_MUT_NON_MONTGOMERY_PROBABILITY = 50, // Fully checked
770 .VAL_MUT_SMALL_ADDITION_PROBABILITY = 110, // Fully checked
771 .VAL_MUT_SPECIAL_VALUE_PROBABILITY = 130 // Fully checked
772
773 };
778 /*
779 std::random_device rd;
780 std::uniform_int_distribution<uint64_t> dist(0, ~(uint64_t)(0));
781 srandom(static_cast<unsigned int>(dist(rd)));
782
783 fuzzer_havoc_settings =
784 HavocSettings{ .GEN_MUTATION_COUNT_LOG = static_cast<size_t>((random() % 8) + 1),
785 .GEN_STRUCTURAL_MUTATION_PROBABILITY = static_cast<size_t>(random() % 100),
786 .GEN_VALUE_MUTATION_PROBABILITY = static_cast<size_t>(random() % 100),
787 .ST_MUT_DELETION_PROBABILITY = static_cast<size_t>(random() % 100),
788 .ST_MUT_DUPLICATION_PROBABILITY = static_cast<size_t>(random() % 100),
789 .ST_MUT_INSERTION_PROBABILITY = static_cast<size_t>((random() % 99) + 1),
790 .ST_MUT_MAXIMUM_DELETION_LOG = static_cast<size_t>((random() % 8) + 1),
791 .ST_MUT_MAXIMUM_DUPLICATION_LOG = static_cast<size_t>((random() % 8) + 1),
792 .ST_MUT_SWAP_PROBABILITY = static_cast<size_t>(random() % 100),
793 .VAL_MUT_LLVM_MUTATE_PROBABILITY = static_cast<size_t>(random() % 100),
794 .VAL_MUT_MONTGOMERY_PROBABILITY = static_cast<size_t>(random() % 100),
795 .VAL_MUT_NON_MONTGOMERY_PROBABILITY = static_cast<size_t>(random() % 100),
796 .VAL_MUT_SMALL_ADDITION_PROBABILITY = static_cast<size_t>(random() % 100),
797 .VAL_MUT_SPECIAL_VALUE_PROBABILITY = static_cast<size_t>(random() % 100)
798
799 };
800 while (fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY == 0 &&
801 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY == 0) {
802 fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY = static_cast<size_t>(random() % 8);
803 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY = static_cast<size_t>(random() % 8);
804 }
805 */
806
807 // fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB = static_cast<size_t>(((random() % (20 - 1)) + 1) * 10);
812 /*
813 std::cerr << "CUSTOM MUTATOR SETTINGS:" << std::endl
814 << "################################################################" << std::endl
815 << "GEN_LLVM_POST_MUTATION_PROB: " << fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB << std::endl
816 << "GEN_MUTATION_COUNT_LOG: " << fuzzer_havoc_settings.GEN_MUTATION_COUNT_LOG << std::endl
817 << "GEN_STRUCTURAL_MUTATION_PROBABILITY: " << fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY
818 << std::endl
819 << "GEN_VALUE_MUTATION_PROBABILITY: " << fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY << std::endl
820 << "ST_MUT_DELETION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_DELETION_PROBABILITY << std::endl
821 << "ST_MUT_DUPLICATION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_DUPLICATION_PROBABILITY << std::endl
822 << "ST_MUT_INSERTION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_INSERTION_PROBABILITY << std::endl
823 << "ST_MUT_MAXIMUM_DELETION_LOG: " << fuzzer_havoc_settings.ST_MUT_MAXIMUM_DELETION_LOG << std::endl
824 << "ST_MUT_MAXIMUM_DUPLICATION_LOG: " << fuzzer_havoc_settings.ST_MUT_MAXIMUM_DUPLICATION_LOG << std::endl
825 << "ST_MUT_SWAP_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_SWAP_PROBABILITY << std::endl
826 << "VAL_MUT_LLVM_MUTATE_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_LLVM_MUTATE_PROBABILITY
827 << std::endl
828 << "VAL_MUT_MONTGOMERY_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_MONTGOMERY_PROBABILITY << std::endl
829 << "VAL_MUT_NON_MONTGOMERY_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_NON_MONTGOMERY_PROBABILITY
830 << std::endl
831 << "VAL_MUT_SMALL_ADDITION_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_SMALL_ADDITION_PROBABILITY
832 << std::endl
833 << "VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY: "
834 << fuzzer_havoc_settings.VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY << std::endl
835 << "VAL_MUT_SPECIAL_VALUE_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_SPECIAL_VALUE_PROBABILITY
836 << std::endl;
837 */
838 std::vector<size_t> structural_mutation_distribution;
839 std::vector<size_t> value_mutation_distribution;
840 size_t temp = 0;
841 temp += fuzzer_havoc_settings.ST_MUT_DELETION_PROBABILITY;
842 structural_mutation_distribution.push_back(temp);
843 temp += fuzzer_havoc_settings.ST_MUT_DUPLICATION_PROBABILITY;
844 structural_mutation_distribution.push_back(temp);
845 temp += fuzzer_havoc_settings.ST_MUT_INSERTION_PROBABILITY;
846 structural_mutation_distribution.push_back(temp);
847 temp += fuzzer_havoc_settings.ST_MUT_SWAP_PROBABILITY;
848 structural_mutation_distribution.push_back(temp);
849 fuzzer_havoc_settings.structural_mutation_distribution = structural_mutation_distribution;
850
851 temp = 0;
852 temp += fuzzer_havoc_settings.VAL_MUT_LLVM_MUTATE_PROBABILITY;
853 value_mutation_distribution.push_back(temp);
854 temp += fuzzer_havoc_settings.VAL_MUT_SMALL_ADDITION_PROBABILITY;
855 value_mutation_distribution.push_back(temp);
856
857 temp += fuzzer_havoc_settings.VAL_MUT_SPECIAL_VALUE_PROBABILITY;
858 value_mutation_distribution.push_back(temp);
859 fuzzer_havoc_settings.value_mutation_distribution = value_mutation_distribution;
860 return 0;
861}
862#endif
863
868extern "C" size_t LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size)
869{
870 RunWithBuilders<ByteArrayFuzzBase, FuzzerCircuitTypes>(Data, Size, VarianceRNG);
871 return 0;
872}
873
874#pragma clang diagnostic pop
FastRandom VarianceRNG(0)
#define MAX_ARRAY_SIZE
FastRandom VarianceRNG(0)
bool circuit_should_fail
#define PUT_RANDOM_TWO_BYTES_IF_LUCKY(variable)
int LLVMFuzzerInitialize(int *argc, char ***argv)
size_t LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
Fuzzer entry function.
#define PUT_RANDOM_BYTE_IF_LUCKY(variable)
static constexpr size_t CONSTANT
static constexpr size_t ADD
static constexpr size_t SLICE
static constexpr size_t RANDOMSEED
static constexpr size_t REVERSE
static constexpr size_t SET
This class implements the execution of safeuint with an oracle to detect discrepancies.
ExecutionHandler set(Builder *builder)
static std::vector< uint8_t > get_value(const byte_array_t &byte_array)
static size_t execute_CONSTANT(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the constant instruction (push constant safeuint to the stack)
static const std::vector< uint8_t > & bool_to_vector(const bool &b)
static size_t execute_SLICE(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the slice instruction.
ExecutionHandler slice(const size_t offset, const size_t length) const
ExecutionHandler operator+(const ExecutionHandler &other)
static size_t execute_SET(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the SET instruction.
ExecutionHandler(std::vector< uint8_t > &r, byte_array_t &s)
static size_t execute_ADD(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the ADD (append) instruction.
static size_t execute_RANDOMSEED(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the RANDOMSEED instruction.
std::optional< field_t > to_field_t(std::optional< size_t > max_msb=std::nullopt) const
static size_t execute_REVERSE(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the REVERSE instruction.
ExecutionHandler(std::vector< uint8_t > r, byte_array_t s)
A class representing a single fuzzing instruction.
static Instruction generateRandom(T &rng)
Generate a random instruction.
static Instruction mutateInstruction(Instruction instruction, T &rng, HavocSettings &havoc_config)
Mutate a single instruction.
Parser class handles the parsing and writing the instructions back to data buffer.
static void writeInstruction(Instruction &instruction, uint8_t *Data)
Write a single instruction to buffer.
static Instruction parseInstructionArgs(uint8_t *Data)
Parse a single instruction from data.
The class parametrizing ByteArray fuzzing instructions, execution, etc.
bb::stdlib::field_t< Builder > field_t
bb::stdlib::byte_array< Builder > byte_array_t
static To from_to(const From &in, const std::optional< size_t > size=std::nullopt)
std::vector< ExecutionHandler > ExecutionState
static bool postProcess(Builder *builder, std::vector< ByteArrayFuzzBase::ExecutionHandler > &stack)
Check that the resulting values are equal to expected.
Class for quickly deterministically creating new random values. We don't care about distribution much...
Definition fuzzer.hpp:63
void reseed(uint32_t seed)
Definition fuzzer.hpp:75
uint32_t next()
Definition fuzzer.hpp:68
Represents a dynamic array of bytes in-circuit.
byte_array slice(size_t offset) const
Slice bytes from the byte array starting at offset. Does not add any constraints.
byte_array reverse() const
Reverse the bytes in the byte array.
byte_array & write(byte_array const &other)
Appends the contents of another byte_array (other) to the end of this one.
std::vector< uint8_t > get_value() const
A helper converting a byte_array into the vector of its uint8_t values.
size_t size() const
static constexpr uint256_t modulus
Definition field.hpp:237
Concept for a simple PRNG which returns a uint32_t when next is called.
Definition fuzzer.hpp:192
AluTraceBuilder builder
Definition alu.test.cpp:124
const std::vector< MemoryValue > data
FF b
uint8_t const size_t length
Definition data_store.hpp:9
ssize_t offset
Definition engine.cpp:52
Instruction instruction
Instruction
Enumeration of VM instructions that can be executed.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
stdlib::byte_array< Builder > byte_array
std::array< uint8_t, MAX_ARRAY_SIZE > data
std::vector< uint8_t > as_vector(void) const
size_t GEN_LLVM_POST_MUTATION_PROB
Definition fuzzer.hpp:28