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: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
10#pragma clang diagnostic push
11#pragma clang diagnostic ignored "-Wc99-designator"
12
13#define MAX_ARRAY_SIZE 128
14
15// This is a global variable, so that the execution handling class could alter it and signal to the input tester that
16// the input should fail
18
19#define HAVOC_TESTING
20
23
24// Enable this definition, when you want to find out the instructions that caused a failure
25// #define FUZZING_SHOW_INFORMATION 1
26#ifdef FUZZING_SHOW_INFORMATION
27#define PREP_SINGLE_ARG(stack, first_index, output_index) \
28 std::string rhs = "c"; \
29 std::string out = rhs; \
30 rhs += std::to_string(first_index); \
31 out += std::to_string(output_index >= stack.size() ? stack.size() : output_index); \
32 out = (output_index >= stack.size() ? "auto " : "") + out;
33
34#define PREP_TWO_ARG(stack, first_index, second_index, output_index) \
35 std::string lhs = "c"; \
36 std::string rhs = "c"; \
37 std::string out = "c"; \
38 lhs += std::to_string(first_index); \
39 rhs += std::to_string(second_index); \
40 out += std::to_string(output_index >= stack.size() ? stack.size() : output_index); \
41 out = (output_index >= stack.size() ? "auto " : "") + out;
42#endif
43
44#define OPERATION_TYPE_SIZE 1
45
46#define ELEMENT_SIZE (sizeof(fr) + 1)
47#define TWO_IN_ONE_OUT 3
48#define THREE_IN_ONE_OUT 4
49#define SLICE_ARGS_SIZE 6
50
55template <typename Builder> class ByteArrayFuzzBase {
56 private:
60
61 template <class From, class To> static To from_to(const From& in, const std::optional<size_t> size = std::nullopt)
62 {
63 return To(in.data(), in.data() + (size ? *size : in.size()));
64 }
65
66 public:
72 public:
74 struct Element {
75 public:
76 std::array<uint8_t, MAX_ARRAY_SIZE> data;
77 uint16_t size;
78
79 uint16_t real_size(void) const { return std::min(size, static_cast<uint16_t>(MAX_ARRAY_SIZE)); }
80 std::vector<uint8_t> as_vector(void) const
81 {
82 return from_to<decltype(data), std::vector<uint8_t>>(data, real_size());
83 }
84 std::string as_string(void) const { return from_to<decltype(data), std::string>(data, real_size()); }
85 };
86 struct TwoArgs {
87 uint8_t in;
88 uint8_t out;
89 };
90 struct ThreeArgs {
91 uint8_t in1;
92 uint8_t in2;
93 uint8_t out;
94 };
95 struct SliceArgs {
96 uint8_t in;
97 uint8_t out;
98 uint16_t offset;
99 uint16_t length;
100 };
101 struct GetBitArgs {
102 uint8_t in;
103 uint8_t out;
104 uint32_t bit;
105 };
106 struct SetBitArgs {
107 uint8_t in;
108 uint32_t bit;
109 uint8_t value;
110 };
111
121 // The type of instruction
123 // Instruction arguments
132 template <typename T>
133 inline static Instruction generateRandom(T& rng)
134 requires SimpleRng<T>
135 {
136 // Choose which instruction we are going to generate
137 OPCODE instruction_opcode = static_cast<OPCODE>(rng.next() % (OPCODE::_LAST));
138 uint8_t in1, in2, out;
139 uint16_t offset, length;
140 // Depending on instruction
141 switch (instruction_opcode) {
142 case OPCODE::CONSTANT:
143 // Return instruction
144 {
145 std::array<uint8_t, MAX_ARRAY_SIZE> data;
146 for (size_t i = 0; i < MAX_ARRAY_SIZE; i++) {
147 data[i] = rng.next() & 0xFF;
148 }
149
150 const uint16_t size = rng.next() & 0xFFFF;
151 return { .id = instruction_opcode, .arguments.element = { .data = data, .size = size } };
152 }
153 break;
154 case OPCODE::REVERSE:
155 case OPCODE::SET:
156 in1 = static_cast<uint8_t>(rng.next() & 0xff);
157 out = static_cast<uint8_t>(rng.next() & 0xff);
158 return { .id = instruction_opcode, .arguments.twoArgs = { .in = in1, .out = out } };
159 break;
160 case OPCODE::SLICE:
161 in1 = static_cast<uint8_t>(rng.next() & 0xff);
162 out = static_cast<uint8_t>(rng.next() & 0xff);
163 offset = static_cast<uint16_t>(rng.next() & 0xffff);
164 length = static_cast<uint16_t>(rng.next() & 0xffff);
165 return { .id = instruction_opcode,
166 .arguments.sliceArgs = { .in = in1, .out = out, .offset = offset, .length = length } };
167 case OPCODE::ADD:
168 // For two-input-one-output instructions we just randomly pick each argument and generate an instruction
169 // accordingly
170 in1 = static_cast<uint8_t>(rng.next() & 0xff);
171 in2 = static_cast<uint8_t>(rng.next() & 0xff);
172 out = static_cast<uint8_t>(rng.next() & 0xff);
173 return { .id = instruction_opcode, .arguments.threeArgs = { .in1 = in1, .in2 = in2, .out = out } };
175 return { .id = instruction_opcode, .arguments.randomseed = rng.next() };
176 break;
177 default:
178 abort(); // We have missed some instructions, it seems
179 break;
180 }
181 }
182
192 template <typename T>
194 requires SimpleRng<T>
195 {
196 (void)rng;
197 (void)havoc_config;
198#define PUT_RANDOM_BYTE_IF_LUCKY(variable) \
199 if (rng.next() & 1) { \
200 variable = rng.next() & 0xff; \
201 }
202#define PUT_RANDOM_TWO_BYTES_IF_LUCKY(variable) \
203 if (rng.next() & 1) { \
204 variable = rng.next() & 0xffff; \
205 }
206#define PUT_RANDOM_FOUR_BYTES_IF_LUCKY(variable) \
207 if (rng.next() & 1) { \
208 variable = rng.next() & 0xffffffff; \
209 }
210 // Depending on instruction type...
211 switch (instruction.id) {
212 case OPCODE::CONSTANT:
213 break;
214 case OPCODE::REVERSE:
215 case OPCODE::SET:
216 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.twoArgs.in)
217 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.twoArgs.out)
218 break;
219 case OPCODE::SLICE:
220 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.sliceArgs.in)
221 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.sliceArgs.out)
222 PUT_RANDOM_TWO_BYTES_IF_LUCKY(instruction.arguments.sliceArgs.offset)
223 PUT_RANDOM_TWO_BYTES_IF_LUCKY(instruction.arguments.sliceArgs.length)
224 break;
225 case OPCODE::ADD:
226 // Randomly sample each of the arguments with 50% probability
227 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.in1)
228 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.in2)
229 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.out)
230 break;
232 instruction.arguments.randomseed = rng.next();
233 break;
234 default:
235 abort(); // New instruction encountered
236 break;
237 }
238 // Return mutated instruction
239 return instruction;
240 }
241 };
242 // We use argsizes to both specify the size of data needed to parse the instruction and to signal that the
243 // instruction is enabled (if it is -1,it's disabled )
244 class ArgSizes {
245 public:
246 static constexpr size_t CONSTANT = MAX_ARRAY_SIZE + sizeof(uint16_t);
247 static constexpr size_t REVERSE = 2;
248 static constexpr size_t SLICE = 6;
249 static constexpr size_t ADD = 3;
250 static constexpr size_t SET = 2;
251 static constexpr size_t RANDOMSEED = sizeof(uint32_t);
252 };
257 class Parser {
258 public:
266 template <typename Instruction::OPCODE opcode> inline static Instruction parseInstructionArgs(uint8_t* Data)
267 {
268 if constexpr (opcode == Instruction::OPCODE::CONSTANT) {
269 std::array<uint8_t, MAX_ARRAY_SIZE> data;
270 std::copy_n(Data, data.size(), data.begin());
271
272 uint16_t size;
273 memcpy(&size, Data + MAX_ARRAY_SIZE, sizeof(uint16_t));
274
275 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
276 .arguments.element = { .data = data, .size = size } };
277 }
278 if constexpr (opcode == Instruction::OPCODE::REVERSE || opcode == Instruction::OPCODE::SET) {
279 return { .id = static_cast<typename Instruction::OPCODE>(opcode),
280 .arguments.twoArgs = { .in = *Data, .out = *(Data + 1) } };
281 }
282 if constexpr (opcode == Instruction::OPCODE::SLICE) {
283 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
284 .arguments.sliceArgs = { .in = *Data,
285 .out = *(Data + 1),
286 .offset = *((uint16_t*)(Data + 2)),
287 .length = *((uint16_t*)(Data + 4)) } };
288 }
289 if constexpr (opcode == Instruction::OPCODE::ADD) {
290 return { .id = static_cast<typename Instruction::OPCODE>(opcode),
291 .arguments.threeArgs = { .in1 = *Data, .in2 = *(Data + 1), .out = *(Data + 2) } };
292 }
293 if constexpr (opcode == Instruction::OPCODE::RANDOMSEED) {
294 uint32_t randomseed;
295 memcpy(&randomseed, Data, sizeof(uint32_t));
296 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
297 .arguments.randomseed = randomseed };
298 };
299 }
307 template <typename Instruction::OPCODE instruction_opcode>
308 inline static void writeInstruction(Instruction& instruction, uint8_t* Data)
309 {
310 if constexpr (instruction_opcode == Instruction::OPCODE::CONSTANT) {
311 *Data = instruction.id;
312 memcpy(Data, instruction.arguments.element.data.data(), MAX_ARRAY_SIZE);
313 memcpy(Data + MAX_ARRAY_SIZE, &instruction.arguments.element.size, sizeof(uint16_t));
314 }
315 if constexpr (instruction_opcode == Instruction::OPCODE::REVERSE ||
316 instruction_opcode == Instruction::OPCODE::SET) {
317 *Data = instruction.id;
318 *(Data + 1) = instruction.arguments.twoArgs.in;
319 *(Data + 2) = instruction.arguments.twoArgs.out;
320 }
321 if constexpr (instruction_opcode == Instruction::OPCODE::SLICE) {
322 *Data = instruction.id;
323 *(Data + 1) = instruction.arguments.sliceArgs.in;
324 *(Data + 2) = instruction.arguments.sliceArgs.out;
325 *((uint16_t*)(Data + 3)) = instruction.arguments.sliceArgs.offset;
326 *((uint16_t*)(Data + 5)) = instruction.arguments.sliceArgs.length;
327 }
328 if constexpr (instruction_opcode == Instruction::OPCODE::ADD) {
329 *Data = instruction.id;
330 *(Data + 1) = instruction.arguments.threeArgs.in1;
331 *(Data + 2) = instruction.arguments.threeArgs.in2;
332 *(Data + 3) = instruction.arguments.threeArgs.out;
333 }
334 if constexpr (instruction_opcode == Instruction::OPCODE::RANDOMSEED) {
335
336 *Data = instruction.id;
337 memcpy(Data + 1, &instruction.arguments.randomseed, sizeof(uint32_t));
338 }
339 }
340 };
346 public:
347 std::vector<uint8_t> reference_value;
348
349 byte_array_t byte_array{ nullptr, std::vector<uint8_t>{} };
350
351 static std::vector<uint8_t> get_value(const byte_array_t& byte_array) { return byte_array.get_value(); }
352 static const std::vector<uint8_t>& bool_to_vector(const bool& b)
353 {
354 static const std::vector<uint8_t> false_{ 0 };
355 static const std::vector<uint8_t> true_{ 1 };
356 return b ? true_ : false_;
357 }
359 {
360 const auto& ref = this->reference_value;
361
362 if (ref.size() > 32) {
363 /* Cannot construct via field if size is larger than field */
364 return std::nullopt;
365 } else if (ref.size() == 32) {
366 uint64_t u0, u1, u2, u3;
367 memcpy(&u3, ref.data(), 8);
368 memcpy(&u2, ref.data() + 8, 8);
369 memcpy(&u1, ref.data() + 16, 8);
370 memcpy(&u0, ref.data() + 24, 8);
371 const uint256_t u256{ htonll(u0), htonll(u1), htonll(u2), htonll(u3) };
372 if (max_msb != std::nullopt && u256.get_msb() >= max_msb) {
373 return std::nullopt;
374 }
375 if (u256 >= field_t::modulus) {
376 return std::nullopt;
377 }
378 }
379
380 return static_cast<field_t>(this->byte_array);
381 }
382 ExecutionHandler() = default;
383 ExecutionHandler(std::vector<uint8_t>& r, byte_array_t& s)
384 : reference_value(r)
385 , byte_array(s)
386 {}
387 ExecutionHandler(std::vector<uint8_t> r, byte_array_t s)
388 : reference_value(r)
389 , byte_array(s)
390 {}
392 : reference_value(get_value(s))
393 , byte_array(s)
394 {}
395
397 {
398 auto reversed = this->reference_value;
399 std::reverse(reversed.begin(), reversed.end());
400
401 return ExecutionHandler(reversed, this->byte_array.reverse());
402 }
403 ExecutionHandler slice(const size_t offset, const size_t length) const
404 {
405 if (offset > this->reference_value.size()) {
406 /* Offset is beyond buffer bounds; cannot comply.
407 * Return the whole buffer.
408 */
409 if (this->byte_array.size() == 0) {
410 return ExecutionHandler(this->reference_value, byte_array_t(this->byte_array));
411 }
412 return ExecutionHandler(this->reference_value, this->byte_array.slice(0));
413 } else if (offset + length > this->reference_value.size()) {
414 /* Offset is valid but range is not.
415 * Return data from the offset to the end of the buffer.
416 */
417 if (this->byte_array.size() == 0 && offset == 0) {
418 return ExecutionHandler(this->reference_value, byte_array_t(this->byte_array));
419 }
420 return ExecutionHandler(
421 std::vector<uint8_t>(this->reference_value.data() + offset,
422 this->reference_value.data() + this->reference_value.size()),
423 this->byte_array.slice(offset));
424 } else {
425 if (this->byte_array.size() == 0 && offset == 0 && length == 0) {
426 return ExecutionHandler(this->reference_value, byte_array_t(this->byte_array));
427 }
428 return ExecutionHandler(std::vector<uint8_t>(this->reference_value.data() + offset,
429 this->reference_value.data() + offset + length),
430 this->byte_array.slice(offset, length));
431 }
432 }
433
435 {
436 if (this->reference_value.size() + other.reference_value.size() > (MAX_ARRAY_SIZE * 3)) {
437 if (this->byte_array.size() == 0) {
438 return ExecutionHandler(this->reference_value, byte_array_t(this->byte_array));
439 }
440 return ExecutionHandler(this->reference_value, this->byte_array.slice(0));
441 } else {
442 const auto other_ref = other.reference_value;
443 this->reference_value.insert(this->reference_value.end(), other_ref.begin(), other_ref.end());
444
445 return ExecutionHandler(std::vector<uint8_t>(this->reference_value),
446 this->byte_array.write(other.byte_array));
447 }
448 }
449 /* Explicit re-instantiation using the various byte_array constructors */
451 {
452 const auto& ref = this->reference_value;
453
454 switch (VarianceRNG.next() % 5) {
455 case 0:
456#ifdef SHOW_INFORMATION
457 std::cout << "byte_array_t(e);" << std::cout;
458#endif
459 /* Construct via byte_array */
460 return ExecutionHandler(ref, byte_array_t(this->byte_array));
461 case 1:
462#ifdef SHOW_INFORMATION
463 std::cout << "e.get_value();" << std::cout;
464#endif
465 /* Construct via std::vector<uint8_t> */
467 // case 2 and 3: Removed - tested private bytes_t constructors (redundant with cases 0-1)
468 case 2: {
469 const auto field = to_field_t();
470
471 if (field == std::nullopt) {
472#ifdef SHOW_INFORMATION
473 std::cout << "byte_array_t(e);" << std::cout;
474#endif
475 return ExecutionHandler(ref, byte_array_t(this->byte_array));
476 } else {
477#ifdef SHOW_INFORMATION
478 std::cout << "tmp_f = " << e << ".to_field_t();" << std::cout;
479#endif
480 /* Pick a number ref.size()..32 */
481 const size_t num_bytes = ref.size() + (VarianceRNG.next() % (32 - ref.size() + 1));
482 if (num_bytes > 32)
483 abort(); /* Should never happen */
484
485 const size_t excess_bytes = num_bytes - ref.size();
486
487 /* Construct new reference value */
488 std::vector<uint8_t> new_ref(excess_bytes, 0);
489 new_ref.insert(new_ref.end(), ref.begin(), ref.end());
490
491 /* Construct via field_t */
492#ifdef SHOW_INFORMATION
493 std::cout << " = byte_array_t(*tmp_f, " << num_bytes << ");" << std::cout;
494#endif
495 return ExecutionHandler(new_ref, byte_array_t(*field, num_bytes));
496 }
497 }
498 case 3: {
499 /* Create a byte_array with gibberish.
500 *
501 * The purpose of this is to ascertain that no gibberish
502 * values are retained in the re-assigned value
503 */
504 const size_t gibberish_size = VarianceRNG.next() % (MAX_ARRAY_SIZE * 2);
505 std::vector<uint8_t> gibberish(gibberish_size);
506 for (size_t i = 0; i < gibberish_size; i++) {
507 gibberish[i] = static_cast<uint8_t>(VarianceRNG.next() % 0xFF);
508 }
509 auto ba = byte_array_t(builder, gibberish);
510
511 /* Construct via assignment */
512 ba = this->byte_array;
513
514 return ExecutionHandler(ref, ba);
515 } break;
516 case 4: {
517 static_assert(suint_t::MAX_BIT_NUM > 0);
518 const auto field = to_field_t(
519 /* One bit must be reserved */
520 suint_t::MAX_BIT_NUM - 1);
521
522 if (field == std::nullopt) {
523 return ExecutionHandler(ref, byte_array_t(this->byte_array));
524 } else {
525 /* Test the suint constructor.
526 *
527 * byte_array -> field -> suint -> byte_array
528 */
529 return ExecutionHandler(ref, byte_array_t(suint_t(*field, suint_t::MAX_BIT_NUM), ref.size()));
530 }
531 } break;
532 default:
533 abort();
534 }
535 }
536
545 static inline size_t execute_CONSTANT(Builder* builder,
548 {
549 (void)builder;
550#ifdef FUZZING_SHOW_INFORMATION
551 std::cout << "std::array<uint8_t, 128> tmp_a = {";
552 for (size_t i = 0; i < instruction.arguments.element.size; i++) {
553 printf("0x%02X, ", instruction.arguments.element.data[i]);
554 }
555 std::cout << "};" << std::endl;
556 std::cout << "auto c" << stack.size() << " = ";
557#endif
558 if (static_cast<bool>(VarianceRNG.next() % 2)) {
559 stack.push_back(byte_array_t(builder, instruction.arguments.element.as_vector()));
560#ifdef FUZZING_SHOW_INFORMATION
561 std::cout << "byte_array_t(&builder, as_vector(tmp_a, " << instruction.arguments.element.size << "));"
562 << std::endl;
563#endif
564 } else {
565 stack.push_back(byte_array_t(builder, instruction.arguments.element.as_string()));
566#ifdef FUZZING_SHOW_INFORMATION
567 std::cout << "byte_array_t(&builder, as_string(tmp_a, " << instruction.arguments.element.size << "));"
568 << std::endl;
569#endif
570 }
571 return 0;
572 }
581 static inline size_t execute_REVERSE(Builder* builder,
584 {
585 (void)builder;
586 if (stack.size() == 0) {
587 return 1;
588 }
589 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
590 size_t output_index = instruction.arguments.twoArgs.out;
591
592#ifdef FUZZING_SHOW_INFORMATION
593 PREP_SINGLE_ARG(stack, first_index, output_index)
594 std::cout << out << " = " << rhs << ".reverse();" << std::endl;
595#endif
596 ExecutionHandler result;
597 result = stack[first_index].reverse();
598 // If the output index is larger than the number of elements in stack, append
599 if (output_index >= stack.size()) {
600 stack.push_back(result);
601 } else {
602 stack[output_index] = result;
603 }
604 return 0;
605 };
615 static inline size_t execute_SLICE(Builder* builder,
618 {
619 (void)builder;
620 if (stack.size() == 0) {
621 return 1;
622 }
623 size_t first_index = instruction.arguments.sliceArgs.in % stack.size();
624 size_t output_index = instruction.arguments.sliceArgs.out;
625 const uint16_t offset = instruction.arguments.sliceArgs.offset;
626 const uint16_t length = instruction.arguments.sliceArgs.length;
627
628#ifdef FUZZING_SHOW_INFORMATION
629 PREP_SINGLE_ARG(stack, first_index, output_index)
630 std::cout << out << " = " << rhs;
631 if (offset > stack[first_index].reference_value.size()) {
632 std::cout << ".slice(0);" << std::endl;
633 } else if (offset + length > stack[first_index].reference_value.size()) {
634 std::cout << ".slice(" << offset << ");" << std::endl;
635 } else {
636 std::cout << ".slice(" << offset << ", " << length << ");" << std::endl;
637 }
638#endif
639
640 ExecutionHandler result;
641 result = stack[first_index].slice(offset, length);
642 // If the output index is larger than the number of elements in stack, append
643 if (output_index >= stack.size()) {
644 stack.push_back(result);
645 } else {
646 stack[output_index] = result;
647 }
648 return 0;
649 }
658 static inline size_t execute_ADD(Builder* builder,
661 {
662 (void)builder;
663 if (stack.size() == 0) {
664 return 1;
665 }
666 size_t first_index = instruction.arguments.threeArgs.in1 % stack.size();
667 size_t second_index = instruction.arguments.threeArgs.in2 % stack.size();
668 size_t output_index = instruction.arguments.threeArgs.out;
669
670#ifdef FUZZING_SHOW_INFORMATION
671 PREP_TWO_ARG(stack, first_index, second_index, output_index)
672 if (stack[first_index].reference_value.size() + stack[second_index].reference_value.size() >
673 (MAX_ARRAY_SIZE * 3)) {
674 std::cout << out << " = " << lhs << ".slice(0);" << std::endl;
675 } else {
676 std::cout << out << " = " << lhs << "write(" << rhs << ");" << std::endl;
677 }
678#endif
679 ExecutionHandler result;
680 result = stack[first_index] + stack[second_index];
681 // If the output index is larger than the number of elements in stack, append
682 if (output_index >= stack.size()) {
683 stack.push_back(result);
684 } else {
685 stack[output_index] = result;
686 }
687 return 0;
688 };
697 static inline size_t execute_SET(Builder* builder,
700 {
701 (void)builder;
702 if (stack.size() == 0) {
703 return 1;
704 }
705 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
706 size_t output_index = instruction.arguments.twoArgs.out;
707
708#ifdef FUZZING_SHOW_INFORMATION
709 PREP_SINGLE_ARG(stack, first_index, output_index)
710 std::cout << "e = " << rhs;
711 std::cout << out << " = ";
712#endif
713
714 ExecutionHandler result;
715 result = stack[first_index].set(builder);
716 // If the output index is larger than the number of elements in stack, append
717 if (output_index >= stack.size()) {
718 stack.push_back(result);
719 } else {
720 stack[output_index] = result;
721 }
722 return 0;
723 };
732 static inline size_t execute_RANDOMSEED(Builder* builder,
735 {
736 (void)builder;
737 (void)stack;
738
739 VarianceRNG.reseed(instruction.arguments.randomseed);
740 return 0;
741 };
742 };
743
755 {
756 (void)builder;
757 for (size_t i = 0; i < stack.size(); i++) {
758 auto element = stack[i];
759 if (element.byte_array.get_value() != element.reference_value) {
760 return false;
761 }
762 }
763 return true;
764 }
765};
766
767#ifdef HAVOC_TESTING
768
769extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
770{
771 (void)argc;
772 (void)argv;
773 // These are the settings, optimized for the safeuint class (under them, fuzzer reaches maximum expected coverage in
774 // 40 seconds)
775 fuzzer_havoc_settings = HavocSettings{
776 .GEN_LLVM_POST_MUTATION_PROB = 30, // Out of 200
777 .GEN_MUTATION_COUNT_LOG = 5, // Fully checked
778 .GEN_STRUCTURAL_MUTATION_PROBABILITY = 300, // Fully checked
779 .GEN_VALUE_MUTATION_PROBABILITY = 700, // Fully checked
780 .ST_MUT_DELETION_PROBABILITY = 100, // Fully checked
781 .ST_MUT_DUPLICATION_PROBABILITY = 80, // Fully checked
782 .ST_MUT_INSERTION_PROBABILITY = 120, // Fully checked
783 .ST_MUT_MAXIMUM_DELETION_LOG = 6, // Fully checked
784 .ST_MUT_MAXIMUM_DUPLICATION_LOG = 2, // Fully checked
785 .ST_MUT_SWAP_PROBABILITY = 50, // Fully checked
786 .VAL_MUT_LLVM_MUTATE_PROBABILITY = 250, // Fully checked
787 .VAL_MUT_MONTGOMERY_PROBABILITY = 130, // Fully checked
788 .VAL_MUT_NON_MONTGOMERY_PROBABILITY = 50, // Fully checked
789 .VAL_MUT_SMALL_ADDITION_PROBABILITY = 110, // Fully checked
790 .VAL_MUT_SPECIAL_VALUE_PROBABILITY = 130 // Fully checked
791
792 };
797 /*
798 std::random_device rd;
799 std::uniform_int_distribution<uint64_t> dist(0, ~(uint64_t)(0));
800 srandom(static_cast<unsigned int>(dist(rd)));
801
802 fuzzer_havoc_settings =
803 HavocSettings{ .GEN_MUTATION_COUNT_LOG = static_cast<size_t>((random() % 8) + 1),
804 .GEN_STRUCTURAL_MUTATION_PROBABILITY = static_cast<size_t>(random() % 100),
805 .GEN_VALUE_MUTATION_PROBABILITY = static_cast<size_t>(random() % 100),
806 .ST_MUT_DELETION_PROBABILITY = static_cast<size_t>(random() % 100),
807 .ST_MUT_DUPLICATION_PROBABILITY = static_cast<size_t>(random() % 100),
808 .ST_MUT_INSERTION_PROBABILITY = static_cast<size_t>((random() % 99) + 1),
809 .ST_MUT_MAXIMUM_DELETION_LOG = static_cast<size_t>((random() % 8) + 1),
810 .ST_MUT_MAXIMUM_DUPLICATION_LOG = static_cast<size_t>((random() % 8) + 1),
811 .ST_MUT_SWAP_PROBABILITY = static_cast<size_t>(random() % 100),
812 .VAL_MUT_LLVM_MUTATE_PROBABILITY = static_cast<size_t>(random() % 100),
813 .VAL_MUT_MONTGOMERY_PROBABILITY = static_cast<size_t>(random() % 100),
814 .VAL_MUT_NON_MONTGOMERY_PROBABILITY = static_cast<size_t>(random() % 100),
815 .VAL_MUT_SMALL_ADDITION_PROBABILITY = static_cast<size_t>(random() % 100),
816 .VAL_MUT_SPECIAL_VALUE_PROBABILITY = static_cast<size_t>(random() % 100)
817
818 };
819 while (fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY == 0 &&
820 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY == 0) {
821 fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY = static_cast<size_t>(random() % 8);
822 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY = static_cast<size_t>(random() % 8);
823 }
824 */
825
826 // fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB = static_cast<size_t>(((random() % (20 - 1)) + 1) * 10);
831 /*
832 std::cerr << "CUSTOM MUTATOR SETTINGS:" << std::endl
833 << "################################################################" << std::endl
834 << "GEN_LLVM_POST_MUTATION_PROB: " << fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB << std::endl
835 << "GEN_MUTATION_COUNT_LOG: " << fuzzer_havoc_settings.GEN_MUTATION_COUNT_LOG << std::endl
836 << "GEN_STRUCTURAL_MUTATION_PROBABILITY: " << fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY
837 << std::endl
838 << "GEN_VALUE_MUTATION_PROBABILITY: " << fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY << std::endl
839 << "ST_MUT_DELETION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_DELETION_PROBABILITY << std::endl
840 << "ST_MUT_DUPLICATION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_DUPLICATION_PROBABILITY << std::endl
841 << "ST_MUT_INSERTION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_INSERTION_PROBABILITY << std::endl
842 << "ST_MUT_MAXIMUM_DELETION_LOG: " << fuzzer_havoc_settings.ST_MUT_MAXIMUM_DELETION_LOG << std::endl
843 << "ST_MUT_MAXIMUM_DUPLICATION_LOG: " << fuzzer_havoc_settings.ST_MUT_MAXIMUM_DUPLICATION_LOG << std::endl
844 << "ST_MUT_SWAP_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_SWAP_PROBABILITY << std::endl
845 << "VAL_MUT_LLVM_MUTATE_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_LLVM_MUTATE_PROBABILITY
846 << std::endl
847 << "VAL_MUT_MONTGOMERY_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_MONTGOMERY_PROBABILITY << std::endl
848 << "VAL_MUT_NON_MONTGOMERY_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_NON_MONTGOMERY_PROBABILITY
849 << std::endl
850 << "VAL_MUT_SMALL_ADDITION_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_SMALL_ADDITION_PROBABILITY
851 << std::endl
852 << "VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY: "
853 << fuzzer_havoc_settings.VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY << std::endl
854 << "VAL_MUT_SPECIAL_VALUE_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_SPECIAL_VALUE_PROBABILITY
855 << std::endl;
856 */
857 std::vector<size_t> structural_mutation_distribution;
858 std::vector<size_t> value_mutation_distribution;
859 size_t temp = 0;
860 temp += fuzzer_havoc_settings.ST_MUT_DELETION_PROBABILITY;
861 structural_mutation_distribution.push_back(temp);
862 temp += fuzzer_havoc_settings.ST_MUT_DUPLICATION_PROBABILITY;
863 structural_mutation_distribution.push_back(temp);
864 temp += fuzzer_havoc_settings.ST_MUT_INSERTION_PROBABILITY;
865 structural_mutation_distribution.push_back(temp);
866 temp += fuzzer_havoc_settings.ST_MUT_SWAP_PROBABILITY;
867 structural_mutation_distribution.push_back(temp);
868 fuzzer_havoc_settings.structural_mutation_distribution = structural_mutation_distribution;
869
870 temp = 0;
871 temp += fuzzer_havoc_settings.VAL_MUT_LLVM_MUTATE_PROBABILITY;
872 value_mutation_distribution.push_back(temp);
873 temp += fuzzer_havoc_settings.VAL_MUT_SMALL_ADDITION_PROBABILITY;
874 value_mutation_distribution.push_back(temp);
875
876 temp += fuzzer_havoc_settings.VAL_MUT_SPECIAL_VALUE_PROBABILITY;
877 value_mutation_distribution.push_back(temp);
878 fuzzer_havoc_settings.value_mutation_distribution = value_mutation_distribution;
879 return 0;
880}
881#endif
882
887extern "C" size_t LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size)
888{
889 RunWithBuilders<ByteArrayFuzzBase, FuzzerCircuitTypes>(Data, Size, VarianceRNG);
890 return 0;
891}
892
893#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.
bb::stdlib::safe_uint_t< Builder > suint_t
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:225
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:36
Instruction instruction
stdlib::byte_array< Builder > byte_array
Instruction
Enumeration of VM instructions that can be executed.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
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