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)
352 {
353 /* Based on the PRNG, alternate between retrieving an std::vector
354 * and a string.
355 * These should be functionally equivalent.
356 */
357 if (static_cast<bool>(VarianceRNG.next() % 2)) {
358 return byte_array.get_value();
359 } else {
360 return from_to<std::string, std::vector<uint8_t>>(byte_array.get_string());
361 }
362 }
363 static const std::vector<uint8_t>& bool_to_vector(const bool& b)
364 {
365 static const std::vector<uint8_t> false_{ 0 };
366 static const std::vector<uint8_t> true_{ 1 };
367 return b ? true_ : false_;
368 }
370 {
371 const auto& ref = this->reference_value;
372
373 if (ref.size() > 32) {
374 /* Cannot construct via field if size is larger than field */
375 return std::nullopt;
376 } else if (ref.size() == 32) {
377 uint64_t u0, u1, u2, u3;
378 memcpy(&u3, ref.data(), 8);
379 memcpy(&u2, ref.data() + 8, 8);
380 memcpy(&u1, ref.data() + 16, 8);
381 memcpy(&u0, ref.data() + 24, 8);
382 const uint256_t u256{ htonll(u0), htonll(u1), htonll(u2), htonll(u3) };
383 if (max_msb != std::nullopt && u256.get_msb() >= max_msb) {
384 return std::nullopt;
385 }
386 if (u256 >= field_t::modulus) {
387 return std::nullopt;
388 }
389 }
390
391 return static_cast<field_t>(this->byte_array);
392 }
393 ExecutionHandler() = default;
394 ExecutionHandler(std::vector<uint8_t>& r, byte_array_t& s)
395 : reference_value(r)
396 , byte_array(s)
397 {}
398 ExecutionHandler(std::vector<uint8_t> r, byte_array_t s)
399 : reference_value(r)
400 , byte_array(s)
401 {}
403 : reference_value(get_value(s))
404 , byte_array(s)
405 {}
406
408 {
409 auto reversed = this->reference_value;
410 std::reverse(reversed.begin(), reversed.end());
411
412 return ExecutionHandler(reversed, this->byte_array.reverse());
413 }
414 ExecutionHandler slice(const size_t offset, const size_t length) const
415 {
416 if (offset > this->reference_value.size()) {
417 /* Offset is beyond buffer bounds; cannot comply.
418 * Return the whole buffer.
419 */
420 if (this->byte_array.size() == 0) {
421 return ExecutionHandler(this->reference_value, byte_array_t(this->byte_array));
422 }
423 return ExecutionHandler(this->reference_value, this->byte_array.slice(0));
424 } else if (offset + length > this->reference_value.size()) {
425 /* Offset is valid but range is not.
426 * Return data from the offset to the end of the buffer.
427 */
428 if (this->byte_array.size() == 0 && offset == 0) {
429 return ExecutionHandler(this->reference_value, byte_array_t(this->byte_array));
430 }
431 return ExecutionHandler(
432 std::vector<uint8_t>(this->reference_value.data() + offset,
433 this->reference_value.data() + this->reference_value.size()),
434 this->byte_array.slice(offset));
435 } else {
436 if (this->byte_array.size() == 0 && offset == 0 && length == 0) {
437 return ExecutionHandler(this->reference_value, byte_array_t(this->byte_array));
438 }
439 return ExecutionHandler(std::vector<uint8_t>(this->reference_value.data() + offset,
440 this->reference_value.data() + offset + length),
441 this->byte_array.slice(offset, length));
442 }
443 }
444
446 {
447 if (this->reference_value.size() + other.reference_value.size() > (MAX_ARRAY_SIZE * 3)) {
448 if (this->byte_array.size() == 0) {
449 return ExecutionHandler(this->reference_value, byte_array_t(this->byte_array));
450 }
451 return ExecutionHandler(this->reference_value, this->byte_array.slice(0));
452 } else {
453 const auto other_ref = other.reference_value;
454 this->reference_value.insert(this->reference_value.end(), other_ref.begin(), other_ref.end());
455
456 return ExecutionHandler(std::vector<uint8_t>(this->reference_value),
457 this->byte_array.write(other.byte_array));
458 }
459 }
460 /* Explicit re-instantiation using the various byte_array constructors */
462 {
463 const auto& ref = this->reference_value;
464
465 switch (VarianceRNG.next() % 8) {
466 case 0:
467#ifdef SHOW_INFORMATION
468 std::cout << "byte_array_t(e);" << std::cout;
469#endif
470 /* Construct via byte_array */
471 return ExecutionHandler(ref, byte_array_t(this->byte_array));
472 case 1:
473#ifdef SHOW_INFORMATION
474 std::cout << "e.get_string();" << std::cout;
475#endif
476 /* Construct via std::string */
478 case 2:
479#ifdef SHOW_INFORMATION
480 std::cout << "e.get_value();" << std::cout;
481#endif
482 /* Construct via std::vector<uint8_t> */
484 case 3:
485#ifdef SHOW_INFORMATION
486 std::cout << "e.bytes();" << std::cout;
487#endif
488 /* Construct via bytes_t */
490 case 4:
491#ifdef SHOW_INFORMATION
492 std::cout << "std::move(e.bytes());" << std::cout;
493#endif
494 /* Construct via bytes_t move constructor */
496 case 5: {
497 const auto field = to_field_t();
498
499 if (field == std::nullopt) {
500#ifdef SHOW_INFORMATION
501 std::cout << "byte_array_t(e);" << std::cout;
502#endif
503 return ExecutionHandler(ref, byte_array_t(this->byte_array));
504 } else {
505#ifdef SHOW_INFORMATION
506 std::cout << "tmp_f = " << e << ".to_field_t();" << std::cout;
507#endif
508 /* Pick a number ref.size()..32 */
509 const size_t num_bytes = ref.size() + (VarianceRNG.next() % (32 - ref.size() + 1));
510 if (num_bytes > 32)
511 abort(); /* Should never happen */
512
513 const size_t excess_bytes = num_bytes - ref.size();
514
515 /* Construct new reference value */
516 std::vector<uint8_t> new_ref(excess_bytes, 0);
517 new_ref.insert(new_ref.end(), ref.begin(), ref.end());
518
519 /* Construct via field_t */
520#ifdef SHOW_INFORMATION
521 std::cout << " = byte_array_t(*tmp_f, " << num_bytes << ");" << std::cout;
522#endif
523 return ExecutionHandler(new_ref, byte_array_t(*field, num_bytes));
524 }
525 }
526 case 6: {
527 /* Create a byte_array with gibberish.
528 *
529 * The purpose of this is to ascertain that no gibberish
530 * values are retained in the re-assigned value
531 */
532 const size_t gibberish_size = VarianceRNG.next() % (MAX_ARRAY_SIZE * 2);
533 std::vector<uint8_t> gibberish(gibberish_size);
534 for (size_t i = 0; i < gibberish_size; i++) {
535 gibberish[i] = static_cast<uint8_t>(VarianceRNG.next() % 0xFF);
536 }
537 auto ba = byte_array_t(builder, gibberish);
538
539 /* Construct via assignment */
540 ba = this->byte_array;
541
542 return ExecutionHandler(ref, ba);
543 } break;
544 case 7: {
545 static_assert(suint_t::MAX_BIT_NUM > 0);
546 const auto field = to_field_t(
547 /* One bit must be reserved */
548 suint_t::MAX_BIT_NUM - 1);
549
550 if (field == std::nullopt) {
551 return ExecutionHandler(ref, byte_array_t(this->byte_array));
552 } else {
553 /* Test the suint constructor.
554 *
555 * byte_array -> field -> suint -> byte_array
556 */
557 return ExecutionHandler(ref, byte_array_t(suint_t(*field, suint_t::MAX_BIT_NUM), ref.size()));
558 }
559 } break;
560 default:
561 abort();
562 }
563 }
564
573 static inline size_t execute_CONSTANT(Builder* builder,
576 {
577 (void)builder;
578#ifdef FUZZING_SHOW_INFORMATION
579 std::cout << "std::array<uint8_t, 128> tmp_a = {";
580 for (size_t i = 0; i < instruction.arguments.element.size; i++) {
581 printf("0x%02X, ", instruction.arguments.element.data[i]);
582 }
583 std::cout << "};" << std::endl;
584 std::cout << "auto c" << stack.size() << " = ";
585#endif
586 if (static_cast<bool>(VarianceRNG.next() % 2)) {
587 stack.push_back(byte_array_t(builder, instruction.arguments.element.as_vector()));
588#ifdef FUZZING_SHOW_INFORMATION
589 std::cout << "byte_array_t(&builder, as_vector(tmp_a, " << instruction.arguments.element.size << "));"
590 << std::endl;
591#endif
592 } else {
593 stack.push_back(byte_array_t(builder, instruction.arguments.element.as_string()));
594#ifdef FUZZING_SHOW_INFORMATION
595 std::cout << "byte_array_t(&builder, as_string(tmp_a, " << instruction.arguments.element.size << "));"
596 << std::endl;
597#endif
598 }
599 return 0;
600 }
609 static inline size_t execute_REVERSE(Builder* builder,
612 {
613 (void)builder;
614 if (stack.size() == 0) {
615 return 1;
616 }
617 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
618 size_t output_index = instruction.arguments.twoArgs.out;
619
620#ifdef FUZZING_SHOW_INFORMATION
621 PREP_SINGLE_ARG(stack, first_index, output_index)
622 std::cout << out << " = " << rhs << ".reverse();" << std::endl;
623#endif
624 ExecutionHandler result;
625 result = stack[first_index].reverse();
626 // If the output index is larger than the number of elements in stack, append
627 if (output_index >= stack.size()) {
628 stack.push_back(result);
629 } else {
630 stack[output_index] = result;
631 }
632 return 0;
633 };
643 static inline size_t execute_SLICE(Builder* builder,
646 {
647 (void)builder;
648 if (stack.size() == 0) {
649 return 1;
650 }
651 size_t first_index = instruction.arguments.sliceArgs.in % stack.size();
652 size_t output_index = instruction.arguments.sliceArgs.out;
653 const uint16_t offset = instruction.arguments.sliceArgs.offset;
654 const uint16_t length = instruction.arguments.sliceArgs.length;
655
656#ifdef FUZZING_SHOW_INFORMATION
657 PREP_SINGLE_ARG(stack, first_index, output_index)
658 std::cout << out << " = " << rhs;
659 if (offset > stack[first_index].reference_value.size()) {
660 std::cout << ".slice(0);" << std::endl;
661 } else if (offset + length > stack[first_index].reference_value.size()) {
662 std::cout << ".slice(" << offset << ");" << std::endl;
663 } else {
664 std::cout << ".slice(" << offset << ", " << length << ");" << std::endl;
665 }
666#endif
667
668 ExecutionHandler result;
669 result = stack[first_index].slice(offset, length);
670 // If the output index is larger than the number of elements in stack, append
671 if (output_index >= stack.size()) {
672 stack.push_back(result);
673 } else {
674 stack[output_index] = result;
675 }
676 return 0;
677 }
686 static inline size_t execute_ADD(Builder* builder,
689 {
690 (void)builder;
691 if (stack.size() == 0) {
692 return 1;
693 }
694 size_t first_index = instruction.arguments.threeArgs.in1 % stack.size();
695 size_t second_index = instruction.arguments.threeArgs.in2 % stack.size();
696 size_t output_index = instruction.arguments.threeArgs.out;
697
698#ifdef FUZZING_SHOW_INFORMATION
699 PREP_TWO_ARG(stack, first_index, second_index, output_index)
700 if (stack[first_index].reference_value.size() + stack[second_index].reference_value.size() >
701 (MAX_ARRAY_SIZE * 3)) {
702 std::cout << out << " = " << lhs << ".slice(0);" << std::endl;
703 } else {
704 std::cout << out << " = " << lhs << "write(" << rhs << ");" << std::endl;
705 }
706#endif
707 ExecutionHandler result;
708 result = stack[first_index] + stack[second_index];
709 // If the output index is larger than the number of elements in stack, append
710 if (output_index >= stack.size()) {
711 stack.push_back(result);
712 } else {
713 stack[output_index] = result;
714 }
715 return 0;
716 };
725 static inline size_t execute_SET(Builder* builder,
728 {
729 (void)builder;
730 if (stack.size() == 0) {
731 return 1;
732 }
733 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
734 size_t output_index = instruction.arguments.twoArgs.out;
735
736#ifdef FUZZING_SHOW_INFORMATION
737 PREP_SINGLE_ARG(stack, first_index, output_index)
738 std::cout << "e = " << rhs;
739 std::cout << out << " = ";
740#endif
741
742 ExecutionHandler result;
743 result = stack[first_index].set(builder);
744 // If the output index is larger than the number of elements in stack, append
745 if (output_index >= stack.size()) {
746 stack.push_back(result);
747 } else {
748 stack[output_index] = result;
749 }
750 return 0;
751 };
760 static inline size_t execute_RANDOMSEED(Builder* builder,
763 {
764 (void)builder;
765 (void)stack;
766
767 VarianceRNG.reseed(instruction.arguments.randomseed);
768 return 0;
769 };
770 };
771
783 {
784 (void)builder;
785 for (size_t i = 0; i < stack.size(); i++) {
786 auto element = stack[i];
787 if (element.byte_array.get_value() != element.reference_value) {
788 return false;
789 }
790 }
791 return true;
792 }
793};
794
795#ifdef HAVOC_TESTING
796
797extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
798{
799 (void)argc;
800 (void)argv;
801 // These are the settings, optimized for the safeuint class (under them, fuzzer reaches maximum expected coverage in
802 // 40 seconds)
803 fuzzer_havoc_settings = HavocSettings{
804 .GEN_LLVM_POST_MUTATION_PROB = 30, // Out of 200
805 .GEN_MUTATION_COUNT_LOG = 5, // Fully checked
806 .GEN_STRUCTURAL_MUTATION_PROBABILITY = 300, // Fully checked
807 .GEN_VALUE_MUTATION_PROBABILITY = 700, // Fully checked
808 .ST_MUT_DELETION_PROBABILITY = 100, // Fully checked
809 .ST_MUT_DUPLICATION_PROBABILITY = 80, // Fully checked
810 .ST_MUT_INSERTION_PROBABILITY = 120, // Fully checked
811 .ST_MUT_MAXIMUM_DELETION_LOG = 6, // Fully checked
812 .ST_MUT_MAXIMUM_DUPLICATION_LOG = 2, // Fully checked
813 .ST_MUT_SWAP_PROBABILITY = 50, // Fully checked
814 .VAL_MUT_LLVM_MUTATE_PROBABILITY = 250, // Fully checked
815 .VAL_MUT_MONTGOMERY_PROBABILITY = 130, // Fully checked
816 .VAL_MUT_NON_MONTGOMERY_PROBABILITY = 50, // Fully checked
817 .VAL_MUT_SMALL_ADDITION_PROBABILITY = 110, // Fully checked
818 .VAL_MUT_SPECIAL_VALUE_PROBABILITY = 130 // Fully checked
819
820 };
825 /*
826 std::random_device rd;
827 std::uniform_int_distribution<uint64_t> dist(0, ~(uint64_t)(0));
828 srandom(static_cast<unsigned int>(dist(rd)));
829
830 fuzzer_havoc_settings =
831 HavocSettings{ .GEN_MUTATION_COUNT_LOG = static_cast<size_t>((random() % 8) + 1),
832 .GEN_STRUCTURAL_MUTATION_PROBABILITY = static_cast<size_t>(random() % 100),
833 .GEN_VALUE_MUTATION_PROBABILITY = static_cast<size_t>(random() % 100),
834 .ST_MUT_DELETION_PROBABILITY = static_cast<size_t>(random() % 100),
835 .ST_MUT_DUPLICATION_PROBABILITY = static_cast<size_t>(random() % 100),
836 .ST_MUT_INSERTION_PROBABILITY = static_cast<size_t>((random() % 99) + 1),
837 .ST_MUT_MAXIMUM_DELETION_LOG = static_cast<size_t>((random() % 8) + 1),
838 .ST_MUT_MAXIMUM_DUPLICATION_LOG = static_cast<size_t>((random() % 8) + 1),
839 .ST_MUT_SWAP_PROBABILITY = static_cast<size_t>(random() % 100),
840 .VAL_MUT_LLVM_MUTATE_PROBABILITY = static_cast<size_t>(random() % 100),
841 .VAL_MUT_MONTGOMERY_PROBABILITY = static_cast<size_t>(random() % 100),
842 .VAL_MUT_NON_MONTGOMERY_PROBABILITY = static_cast<size_t>(random() % 100),
843 .VAL_MUT_SMALL_ADDITION_PROBABILITY = static_cast<size_t>(random() % 100),
844 .VAL_MUT_SPECIAL_VALUE_PROBABILITY = static_cast<size_t>(random() % 100)
845
846 };
847 while (fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY == 0 &&
848 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY == 0) {
849 fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY = static_cast<size_t>(random() % 8);
850 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY = static_cast<size_t>(random() % 8);
851 }
852 */
853
854 // fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB = static_cast<size_t>(((random() % (20 - 1)) + 1) * 10);
859 /*
860 std::cerr << "CUSTOM MUTATOR SETTINGS:" << std::endl
861 << "################################################################" << std::endl
862 << "GEN_LLVM_POST_MUTATION_PROB: " << fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB << std::endl
863 << "GEN_MUTATION_COUNT_LOG: " << fuzzer_havoc_settings.GEN_MUTATION_COUNT_LOG << std::endl
864 << "GEN_STRUCTURAL_MUTATION_PROBABILITY: " << fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY
865 << std::endl
866 << "GEN_VALUE_MUTATION_PROBABILITY: " << fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY << std::endl
867 << "ST_MUT_DELETION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_DELETION_PROBABILITY << std::endl
868 << "ST_MUT_DUPLICATION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_DUPLICATION_PROBABILITY << std::endl
869 << "ST_MUT_INSERTION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_INSERTION_PROBABILITY << std::endl
870 << "ST_MUT_MAXIMUM_DELETION_LOG: " << fuzzer_havoc_settings.ST_MUT_MAXIMUM_DELETION_LOG << std::endl
871 << "ST_MUT_MAXIMUM_DUPLICATION_LOG: " << fuzzer_havoc_settings.ST_MUT_MAXIMUM_DUPLICATION_LOG << std::endl
872 << "ST_MUT_SWAP_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_SWAP_PROBABILITY << std::endl
873 << "VAL_MUT_LLVM_MUTATE_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_LLVM_MUTATE_PROBABILITY
874 << std::endl
875 << "VAL_MUT_MONTGOMERY_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_MONTGOMERY_PROBABILITY << std::endl
876 << "VAL_MUT_NON_MONTGOMERY_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_NON_MONTGOMERY_PROBABILITY
877 << std::endl
878 << "VAL_MUT_SMALL_ADDITION_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_SMALL_ADDITION_PROBABILITY
879 << std::endl
880 << "VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY: "
881 << fuzzer_havoc_settings.VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY << std::endl
882 << "VAL_MUT_SPECIAL_VALUE_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_SPECIAL_VALUE_PROBABILITY
883 << std::endl;
884 */
885 std::vector<size_t> structural_mutation_distribution;
886 std::vector<size_t> value_mutation_distribution;
887 size_t temp = 0;
888 temp += fuzzer_havoc_settings.ST_MUT_DELETION_PROBABILITY;
889 structural_mutation_distribution.push_back(temp);
890 temp += fuzzer_havoc_settings.ST_MUT_DUPLICATION_PROBABILITY;
891 structural_mutation_distribution.push_back(temp);
892 temp += fuzzer_havoc_settings.ST_MUT_INSERTION_PROBABILITY;
893 structural_mutation_distribution.push_back(temp);
894 temp += fuzzer_havoc_settings.ST_MUT_SWAP_PROBABILITY;
895 structural_mutation_distribution.push_back(temp);
896 fuzzer_havoc_settings.structural_mutation_distribution = structural_mutation_distribution;
897
898 temp = 0;
899 temp += fuzzer_havoc_settings.VAL_MUT_LLVM_MUTATE_PROBABILITY;
900 value_mutation_distribution.push_back(temp);
901 temp += fuzzer_havoc_settings.VAL_MUT_SMALL_ADDITION_PROBABILITY;
902 value_mutation_distribution.push_back(temp);
903
904 temp += fuzzer_havoc_settings.VAL_MUT_SPECIAL_VALUE_PROBABILITY;
905 value_mutation_distribution.push_back(temp);
906 fuzzer_havoc_settings.value_mutation_distribution = value_mutation_distribution;
907 return 0;
908}
909#endif
910
915extern "C" size_t LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size)
916{
917 RunWithBuilders<ByteArrayFuzzBase, FuzzerCircuitTypes>(Data, Size, VarianceRNG);
918 return 0;
919}
920
921#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.
bytes_t const & bytes() const
size_t size() const
std::string get_string() const
Given a byte_array, compute a vector containing the values of its entries and convert it to a string.
static constexpr uint256_t modulus
Definition field.hpp:212
Concept for a simple PRNG which returns a uint32_t when next is called.
Definition fuzzer.hpp:125
AluTraceBuilder builder
Definition alu.test.cpp:123
const std::vector< FF > 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