barretenberg
Loading...
Searching...
No Matches
byte_array.fuzzer.hpp
1#include "barretenberg/numeric/random/engine.hpp"
2#include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp"
3#include "barretenberg/stdlib/primitives/safe_uint/safe_uint.hpp"
4#pragma clang diagnostic push
5#pragma clang diagnostic ignored "-Wc99-designator"
6
7#define MAX_ARRAY_SIZE 128
8
9// This is a global variable, so that the execution handling class could alter it and signal to the input tester that
10// the input should fail
11bool circuit_should_fail = false;
12
13#define HAVOC_TESTING
14
15#include "barretenberg/common/fuzzer.hpp"
16FastRandom VarianceRNG(0);
17
18// Enable this definition, when you want to find out the instructions that caused a failure
19// #define SHOW_INFORMATION 1
20
21#define OPERATION_TYPE_SIZE 1
22
23#define ELEMENT_SIZE (sizeof(fr) + 1)
24#define TWO_IN_ONE_OUT 3
25#define THREE_IN_ONE_OUT 4
26#define SLICE_ARGS_SIZE 6
27
32template <typename Builder> class ByteArrayFuzzBase {
33 private:
37
38 template <class From, class To> static To from_to(const From& in, const std::optional<size_t> size = std::nullopt)
39 {
40 return To(in.data(), in.data() + (size ? *size : in.size()));
41 }
42
43 public:
49 public:
50 enum OPCODE { CONSTANT, REVERSE, SLICE, GET_BIT, SET_BIT, ADD, SET, RANDOMSEED, _LAST };
51 struct Element {
52 public:
53 std::array<uint8_t, MAX_ARRAY_SIZE> data;
54 uint16_t size;
55
56 uint16_t real_size(void) const { return std::min(size, static_cast<uint16_t>(MAX_ARRAY_SIZE)); }
57 std::vector<uint8_t> as_vector(void) const
58 {
59 return from_to<decltype(data), std::vector<uint8_t>>(data, real_size());
60 }
61 std::string as_string(void) const { return from_to<decltype(data), std::string>(data, real_size()); }
62 };
63 struct TwoArgs {
64 uint8_t in;
65 uint8_t out;
66 };
67 struct ThreeArgs {
68 uint8_t in1;
69 uint8_t in2;
70 uint8_t out;
71 };
72 struct SliceArgs {
73 uint8_t in;
74 uint8_t out;
75 uint16_t offset;
76 uint16_t length;
77 };
78 struct GetBitArgs {
79 uint8_t in;
80 uint8_t out;
81 uint32_t bit;
82 };
83 struct SetBitArgs {
84 uint8_t in;
85 uint32_t bit;
86 uint8_t value;
87 };
88
90 uint32_t randomseed;
91 Element element;
92 TwoArgs twoArgs;
93 ThreeArgs threeArgs;
94 SliceArgs sliceArgs;
95 GetBitArgs getBitArgs;
96 SetBitArgs setBitArgs;
97 };
98 // The type of instruction
99 OPCODE id;
100 // Instruction arguments
101 ArgumentContents arguments;
109 template <typename T>
110 inline static Instruction generateRandom(T& rng)
111 requires SimpleRng<T>
112 {
113 // Choose which instruction we are going to generate
114 OPCODE instruction_opcode = static_cast<OPCODE>(rng.next() % (OPCODE::_LAST));
115 uint8_t in1, in2, out, value;
116 uint16_t offset, length;
117 uint32_t bit;
118 // Depending on instruction
119 switch (instruction_opcode) {
120 case OPCODE::CONSTANT:
121 // Return instruction
122 {
123 std::array<uint8_t, MAX_ARRAY_SIZE> data;
124 for (size_t i = 0; i < MAX_ARRAY_SIZE; i++) {
125 data[i] = rng.next() & 0xFF;
126 }
127
128 const uint16_t size = rng.next() & 0xFFFF;
129 return { .id = instruction_opcode, .arguments.element = { .data = data, .size = size } };
130 }
131 break;
132 case OPCODE::REVERSE:
133 case OPCODE::SET:
134 in1 = static_cast<uint8_t>(rng.next() & 0xff);
135 out = static_cast<uint8_t>(rng.next() & 0xff);
136 return { .id = instruction_opcode, .arguments.twoArgs = { .in = in1, .out = out } };
137 break;
138 case OPCODE::SLICE:
139 in1 = static_cast<uint8_t>(rng.next() & 0xff);
140 out = static_cast<uint8_t>(rng.next() & 0xff);
141 offset = static_cast<uint16_t>(rng.next() & 0xffff);
142 length = static_cast<uint16_t>(rng.next() & 0xffff);
143 return { .id = instruction_opcode,
144 .arguments.sliceArgs = { .in = in1, .out = out, .offset = offset, .length = length } };
145 case OPCODE::GET_BIT:
146 in1 = static_cast<uint8_t>(rng.next() & 0xff);
147 out = static_cast<uint8_t>(rng.next() & 0xff);
148 bit = static_cast<uint32_t>(rng.next() & 0xffffffff);
149 return { .id = instruction_opcode, .arguments.getBitArgs = { .in = in1, .out = out, .bit = bit } };
150 case OPCODE::ADD:
151 // For two-input-one-output instructions we just randomly pick each argument and generate an instruction
152 // accordingly
153 in1 = static_cast<uint8_t>(rng.next() & 0xff);
154 in2 = static_cast<uint8_t>(rng.next() & 0xff);
155 out = static_cast<uint8_t>(rng.next() & 0xff);
156 return { .id = instruction_opcode, .arguments.threeArgs = { .in1 = in1, .in2 = in2, .out = out } };
157 case OPCODE::SET_BIT:
158 in1 = static_cast<uint8_t>(rng.next() & 0xff);
159 bit = static_cast<uint32_t>(rng.next() & 0xffffffff);
160 value = static_cast<uint8_t>(rng.next() & 0xff);
161 return { .id = instruction_opcode, .arguments.setBitArgs = { .in = in1, .bit = bit, .value = value } };
162 case OPCODE::RANDOMSEED:
163 return { .id = instruction_opcode, .arguments.randomseed = rng.next() };
164 break;
165 default:
166 abort(); // We have missed some instructions, it seems
167 break;
168 }
169 }
170
180 template <typename T>
181 inline static Instruction mutateInstruction(Instruction instruction, T& rng, HavocSettings& havoc_config)
182 requires SimpleRng<T>
183 {
184 (void)rng;
185 (void)havoc_config;
186#define PUT_RANDOM_BYTE_IF_LUCKY(variable) \
187 if (rng.next() & 1) { \
188 variable = rng.next() & 0xff; \
189 }
190#define PUT_RANDOM_TWO_BYTES_IF_LUCKY(variable) \
191 if (rng.next() & 1) { \
192 variable = rng.next() & 0xffff; \
193 }
194#define PUT_RANDOM_FOUR_BYTES_IF_LUCKY(variable) \
195 if (rng.next() & 1) { \
196 variable = rng.next() & 0xffffffff; \
197 }
198 // Depending on instruction type...
199 switch (instruction.id) {
200 case OPCODE::CONSTANT:
201 break;
202 case OPCODE::REVERSE:
203 case OPCODE::SET:
204 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.twoArgs.in)
205 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.twoArgs.out)
206 break;
207 case OPCODE::SLICE:
208 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.sliceArgs.in)
209 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.sliceArgs.out)
210 PUT_RANDOM_TWO_BYTES_IF_LUCKY(instruction.arguments.sliceArgs.offset)
211 PUT_RANDOM_TWO_BYTES_IF_LUCKY(instruction.arguments.sliceArgs.length)
212 break;
213 case OPCODE::GET_BIT:
214 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.getBitArgs.in)
215 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.getBitArgs.out)
216 PUT_RANDOM_FOUR_BYTES_IF_LUCKY(instruction.arguments.getBitArgs.bit)
217 break;
218 case OPCODE::SET_BIT:
219 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.setBitArgs.in)
220 PUT_RANDOM_FOUR_BYTES_IF_LUCKY(instruction.arguments.setBitArgs.bit)
221 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.setBitArgs.value)
222 break;
223 case OPCODE::ADD:
224 // Randomly sample each of the arguments with 50% probability
225 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.in1)
226 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.in2)
227 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.out)
228 break;
229 case OPCODE::RANDOMSEED:
230 instruction.arguments.randomseed = rng.next();
231 break;
232 default:
233 abort(); // New instruction encountered
234 break;
235 }
236 // Return mutated instruction
237 return instruction;
238 }
239 };
240 // We use argsizes to both specify the size of data needed to parse the instruction and to signal that the
241 // instruction is enabled (if it is -1,it's disabled )
242 class ArgSizes {
243 public:
244 static constexpr size_t CONSTANT = MAX_ARRAY_SIZE + sizeof(uint16_t);
245 static constexpr size_t REVERSE = 2;
246 static constexpr size_t SLICE = 6;
247 static constexpr size_t GET_BIT = 6;
248 static constexpr size_t SET_BIT = 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::GET_BIT) {
294 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
295 .arguments.getBitArgs = {
296 .in = *Data, .out = *(Data + 1), .bit = *((uint32_t*)(Data + 2)) } };
297 }
298 if constexpr (opcode == Instruction::OPCODE::SET_BIT) {
299 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
300 .arguments.setBitArgs = {
301 .in = *Data, .bit = *((uint32_t*)(Data + 1)), .value = *(Data + 5) } };
302 }
303 if constexpr (opcode == Instruction::OPCODE::RANDOMSEED) {
304 uint32_t randomseed;
305 memcpy(&randomseed, Data, sizeof(uint32_t));
306 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
307 .arguments.randomseed = randomseed };
308 };
309 }
317 template <typename Instruction::OPCODE instruction_opcode>
318 inline static void writeInstruction(Instruction& instruction, uint8_t* Data)
319 {
320 if constexpr (instruction_opcode == Instruction::OPCODE::CONSTANT) {
321 *Data = instruction.id;
322 memcpy(Data, instruction.arguments.element.data.data(), MAX_ARRAY_SIZE);
323 memcpy(Data + MAX_ARRAY_SIZE, &instruction.arguments.element.size, sizeof(uint16_t));
324 }
325 if constexpr (instruction_opcode == Instruction::OPCODE::REVERSE ||
326 instruction_opcode == Instruction::OPCODE::SET) {
327 *Data = instruction.id;
328 *(Data + 1) = instruction.arguments.twoArgs.in;
329 *(Data + 2) = instruction.arguments.twoArgs.out;
330 }
331 if constexpr (instruction_opcode == Instruction::OPCODE::SLICE) {
332 *Data = instruction.id;
333 *(Data + 1) = instruction.arguments.sliceArgs.in;
334 *(Data + 2) = instruction.arguments.sliceArgs.out;
335 *((uint16_t*)(Data + 3)) = instruction.arguments.sliceArgs.offset;
336 *((uint16_t*)(Data + 5)) = instruction.arguments.sliceArgs.length;
337 }
338 if constexpr (instruction_opcode == Instruction::OPCODE::GET_BIT) {
339 *Data = instruction.id;
340 *(Data + 1) = instruction.arguments.getBitArgs.in;
341 *(Data + 2) = instruction.arguments.getBitArgs.out;
342 *((uint32_t*)(Data + 3)) = instruction.arguments.getBitArgs.bit;
343 }
344 if constexpr (instruction_opcode == Instruction::OPCODE::SET_BIT) {
345 *Data = instruction.id;
346 *(Data + 1) = instruction.arguments.setBitArgs.in;
347 *((uint32_t*)(Data + 2)) = instruction.arguments.setBitArgs.bit;
348 *(Data + 6) = instruction.arguments.setBitArgs.value;
349 }
350 if constexpr (instruction_opcode == Instruction::OPCODE::ADD) {
351 *Data = instruction.id;
352 *(Data + 1) = instruction.arguments.threeArgs.in1;
353 *(Data + 2) = instruction.arguments.threeArgs.in2;
354 *(Data + 3) = instruction.arguments.threeArgs.out;
355 }
356 if constexpr (instruction_opcode == Instruction::OPCODE::RANDOMSEED) {
357
358 *Data = instruction.id;
359 memcpy(Data + 1, &instruction.arguments.randomseed, sizeof(uint32_t));
360 }
361 }
362 };
368 public:
369 std::vector<uint8_t> reference_value;
370
371 byte_array_t byte_array{ nullptr, std::vector<uint8_t>{} };
372
373 static std::vector<uint8_t> get_value(const byte_array_t& byte_array)
374 {
375 /* Based on the PRNG, alternate between retrieving an std::vector
376 * and a string.
377 * These should be functionally equivalent.
378 */
379 if (static_cast<bool>(VarianceRNG.next() % 2)) {
380 return byte_array.get_value();
381 } else {
382 return from_to<std::string, std::vector<uint8_t>>(byte_array.get_string());
383 }
384 }
385 static const std::vector<uint8_t>& bool_to_vector(const bool& b)
386 {
387 static const std::vector<uint8_t> false_{ 0 };
388 static const std::vector<uint8_t> true_{ 1 };
389 return b ? true_ : false_;
390 }
391 std::optional<field_t> to_field_t(std::optional<size_t> max_msb = std::nullopt) const
392 {
393 const auto& ref = this->reference_value;
394
395 if (ref.size() > 32) {
396 /* Cannot construct via field if size is larger than field */
397 return std::nullopt;
398 } else if (ref.size() == 32) {
399 uint64_t u0, u1, u2, u3;
400 memcpy(&u3, ref.data(), 8);
401 memcpy(&u2, ref.data() + 8, 8);
402 memcpy(&u1, ref.data() + 16, 8);
403 memcpy(&u0, ref.data() + 24, 8);
404 const uint256_t u256{ htonll(u0), htonll(u1), htonll(u2), htonll(u3) };
405 if (max_msb != std::nullopt && u256.get_msb() >= max_msb) {
406 return std::nullopt;
407 }
408 if (u256 >= field_t::modulus) {
409 return std::nullopt;
410 }
411 }
412
413 return static_cast<field_t>(this->byte_array);
414 }
415 ExecutionHandler() = default;
416 ExecutionHandler(std::vector<uint8_t>& r, byte_array_t& s)
417 : reference_value(r)
418 , byte_array(s)
419 {}
420 ExecutionHandler(std::vector<uint8_t> r, byte_array_t s)
421 : reference_value(r)
422 , byte_array(s)
423 {}
425 : reference_value(get_value(s))
426 , byte_array(s)
427 {}
428
429 ExecutionHandler reverse() const
430 {
431 auto reversed = this->reference_value;
432 std::reverse(reversed.begin(), reversed.end());
433
434 return ExecutionHandler(reversed, this->byte_array.reverse());
435 }
436 ExecutionHandler slice(const size_t offset, const size_t length) const
437 {
438 if (offset > this->reference_value.size()) {
439 /* Offset is beyond buffer bounds; cannot comply.
440 * Return the whole buffer.
441 */
442 return ExecutionHandler(this->reference_value, this->byte_array.slice(0));
443 } else if (offset + length > this->reference_value.size()) {
444 /* Offset is valid but range is not.
445 * Return data from the offset to the end of the buffer.
446 */
447 return ExecutionHandler(
448 std::vector<uint8_t>(this->reference_value.data() + offset,
449 this->reference_value.data() + this->reference_value.size()),
450 this->byte_array.slice(offset));
451 } else {
452 return ExecutionHandler(std::vector<uint8_t>(this->reference_value.data() + offset,
453 this->reference_value.data() + offset + length),
454 this->byte_array.slice(offset, length));
455 }
456 }
457 ExecutionHandler get_bit(Builder* builder, const size_t bit) const
458 {
459 if (bit >= this->reference_value.size() * 8) {
460 return ExecutionHandler(this->reference_value, this->byte_array);
461 } else {
462 const bool is_set_ref = this->reference_value[
463 /* Byte */ this->reference_value.size() - 1 - (bit / 8)] &
464 /* Bit */ (1 << (bit % 8));
465 const bool is_set_ba = this->byte_array.get_bit(bit).get_value();
466
467 return ExecutionHandler(bool_to_vector(is_set_ref), byte_array_t(builder, bool_to_vector(is_set_ba)));
468 }
469 }
470 /* Modifies the buffer at hand, so does not produce a return value */
471 void set_bit(const size_t bit, const bool value)
472 {
473 if (bit < this->reference_value.size() * 8) {
474 if (value) {
475 this->reference_value[
476 /* Byte */ this->reference_value.size() - 1 - (bit / 8)] |=
477 /* Bit */ (1 << (bit % 8));
478 } else {
479 this->reference_value[
480 /* Byte */ this->reference_value.size() - 1 - (bit / 8)] &=
481 /* Bit */ ~(1 << (bit % 8));
482 }
483 this->byte_array.set_bit(bit, value);
484 }
485 }
486 ExecutionHandler operator+(const ExecutionHandler& other)
487 {
488 if (this->reference_value.size() + other.reference_value.size() > (MAX_ARRAY_SIZE * 3)) {
489 return ExecutionHandler(this->reference_value, this->byte_array.slice(0));
490 } else {
491 const auto other_ref = other.reference_value;
492 this->reference_value.insert(this->reference_value.end(), other_ref.begin(), other_ref.end());
493
494 return ExecutionHandler(std::vector<uint8_t>(this->reference_value),
495 this->byte_array.write(other.byte_array));
496 }
497 }
498 /* Explicit re-instantiation using the various bit_array constructors */
499 ExecutionHandler set(Builder* builder)
500 {
501 const auto& ref = this->reference_value;
502
503 switch (VarianceRNG.next() % 8) {
504 case 0:
505 /* Construct via byte_array */
506 return ExecutionHandler(ref, byte_array_t(this->byte_array));
507 case 1:
508 /* Construct via std::string */
509 return ExecutionHandler(ref, byte_array_t(builder, this->byte_array.get_string()));
510 case 2:
511 /* Construct via std::vector<uint8_t> */
512 return ExecutionHandler(ref, byte_array_t(builder, this->byte_array.get_value()));
513 case 3:
514 /* Construct via bytes_t */
515 return ExecutionHandler(ref, byte_array_t(builder, this->byte_array.bytes()));
516 case 4:
517 /* Construct via bytes_t move constructor */
518 return ExecutionHandler(ref, byte_array_t(builder, std::move(this->byte_array.bytes())));
519 case 5: {
520 const auto field = to_field_t();
521
522 if (field == std::nullopt) {
523 return ExecutionHandler(ref, byte_array_t(this->byte_array));
524 } else {
525 /* Pick a number ref.size()..32 */
526 const size_t num_bytes = ref.size() + (VarianceRNG.next() % (32 - ref.size() + 1));
527 if (num_bytes > 32)
528 abort(); /* Should never happen */
529
530 const size_t excess_bytes = num_bytes - ref.size();
531
532 /* Construct new reference value */
533 std::vector<uint8_t> new_ref(excess_bytes, 0);
534 new_ref.insert(new_ref.end(), ref.begin(), ref.end());
535
536 /* Construct via field_t */
537 return ExecutionHandler(new_ref, byte_array_t(*field, num_bytes));
538 }
539 }
540 case 6: {
541 /* Create a bit_array with gibberish.
542 *
543 * The purpose of this is to ascertain that no gibberish
544 * values are retained in the re-assigned value
545 */
546 const size_t gibberish_size = VarianceRNG.next() % (MAX_ARRAY_SIZE * 2);
547 std::vector<uint8_t> gibberish(gibberish_size);
548 for (size_t i = 0; i < gibberish_size; i++) {
549 gibberish[i] = static_cast<uint8_t>(VarianceRNG.next() % 0xFF);
550 }
551 auto ba = byte_array_t(builder, gibberish);
552
553 /* Construct via assignment */
554 ba = this->byte_array;
555
556 return ExecutionHandler(ref, ba);
557 } break;
558 case 7: {
559 static_assert(suint_t::MAX_BIT_NUM > 0);
560 const auto field = to_field_t(
561 /* One bit must be reserved */
562 suint_t::MAX_BIT_NUM - 1);
563
564 if (field == std::nullopt) {
565 return ExecutionHandler(ref, byte_array_t(this->byte_array));
566 } else {
567 /* Test the suint constructor.
568 *
569 * byte_array -> field -> suint -> byte_array
570 */
571 return ExecutionHandler(ref, byte_array_t(suint_t(*field, suint_t::MAX_BIT_NUM), ref.size()));
572 }
573 } break;
574 default:
575 abort();
576 }
577 }
578
587 static inline size_t execute_CONSTANT(Builder* builder,
588 std::vector<ExecutionHandler>& stack,
589 Instruction& instruction)
590 {
591 (void)builder;
592 if (static_cast<bool>(VarianceRNG.next() % 2)) {
593 stack.push_back(byte_array_t(builder, instruction.arguments.element.as_vector()));
594 } else {
595 stack.push_back(byte_array_t(builder, instruction.arguments.element.as_string()));
596 }
597 return 0;
598 }
607 static inline size_t execute_REVERSE(Builder* builder,
608 std::vector<ExecutionHandler>& stack,
609 Instruction& instruction)
610 {
611 (void)builder;
612 if (stack.size() == 0) {
613 return 1;
614 }
615 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
616 size_t output_index = instruction.arguments.twoArgs.out;
617
618 ExecutionHandler result;
619 result = stack[first_index].reverse();
620 // If the output index is larger than the number of elements in stack, append
621 if (output_index >= stack.size()) {
622 stack.push_back(result);
623 } else {
624 stack[output_index] = result;
625 }
626 return 0;
627 };
637 static inline size_t execute_SLICE(Builder* builder,
638 std::vector<ExecutionHandler>& stack,
639 Instruction& instruction)
640 {
641 (void)builder;
642 if (stack.size() == 0) {
643 return 1;
644 }
645 size_t first_index = instruction.arguments.sliceArgs.in % stack.size();
646 size_t output_index = instruction.arguments.sliceArgs.out;
647 const uint16_t offset = instruction.arguments.sliceArgs.offset;
648 const uint16_t length = instruction.arguments.sliceArgs.length;
649 ExecutionHandler result;
650 result = stack[first_index].slice(offset, length);
651 // If the output index is larger than the number of elements in stack, append
652 if (output_index >= stack.size()) {
653 stack.push_back(result);
654 } else {
655 stack[output_index] = result;
656 }
657 return 0;
658 }
667 static inline size_t execute_GET_BIT(Builder* builder,
668 std::vector<ExecutionHandler>& stack,
669 Instruction& instruction)
670 {
671 (void)builder;
672 if (stack.size() == 0) {
673 return 1;
674 }
675 size_t first_index = instruction.arguments.getBitArgs.in % stack.size();
676 size_t output_index = instruction.arguments.getBitArgs.out;
677 const uint32_t bit = instruction.arguments.getBitArgs.bit;
678 ExecutionHandler result;
679 result = stack[first_index].get_bit(builder, bit);
680 // If the output index is larger than the number of elements in stack, append
681 if (output_index >= stack.size()) {
682 stack.push_back(result);
683 } else {
684 stack[output_index] = result;
685 }
686 return 0;
687 };
696 static inline size_t execute_SET_BIT(Builder* builder,
697 std::vector<ExecutionHandler>& stack,
698 Instruction& instruction)
699 {
700 (void)builder;
701 if (stack.size() == 0) {
702 return 1;
703 }
704 size_t first_index = instruction.arguments.setBitArgs.in % stack.size();
705 const uint32_t bit = instruction.arguments.setBitArgs.bit;
706 const bool value = static_cast<bool>(instruction.arguments.setBitArgs.value % 2);
707 stack[first_index].set_bit(bit, value);
708 return 0;
709 };
718 static inline size_t execute_ADD(Builder* builder,
719 std::vector<ExecutionHandler>& stack,
720 Instruction& instruction)
721 {
722 (void)builder;
723 if (stack.size() == 0) {
724 return 1;
725 }
726 size_t first_index = instruction.arguments.threeArgs.in1 % stack.size();
727 size_t second_index = instruction.arguments.threeArgs.in2 % stack.size();
728 size_t output_index = instruction.arguments.threeArgs.out;
729
730 ExecutionHandler result;
731 result = stack[first_index] + stack[second_index];
732 // If the output index is larger than the number of elements in stack, append
733 if (output_index >= stack.size()) {
734 stack.push_back(result);
735 } else {
736 stack[output_index] = result;
737 }
738 return 0;
739 };
748 static inline size_t execute_SET(Builder* builder,
749 std::vector<ExecutionHandler>& stack,
750 Instruction& instruction)
751 {
752 (void)builder;
753 if (stack.size() == 0) {
754 return 1;
755 }
756 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
757 size_t output_index = instruction.arguments.twoArgs.out;
758 ExecutionHandler result;
759 result = stack[first_index].set(builder);
760 // If the output index is larger than the number of elements in stack, append
761 if (output_index >= stack.size()) {
762 stack.push_back(result);
763 } else {
764 stack[output_index] = result;
765 }
766 return 0;
767 };
776 static inline size_t execute_RANDOMSEED(Builder* builder,
777 std::vector<ExecutionHandler>& stack,
778 Instruction& instruction)
779 {
780 (void)builder;
781 (void)stack;
782
783 VarianceRNG.reseed(instruction.arguments.randomseed);
784 return 0;
785 };
786 };
787
788 typedef std::vector<ExecutionHandler> ExecutionState;
798 inline static bool postProcess(Builder* builder, std::vector<ByteArrayFuzzBase::ExecutionHandler>& stack)
799 {
800 (void)builder;
801 for (size_t i = 0; i < stack.size(); i++) {
802 auto element = stack[i];
803 if (element.byte_array.get_value() != element.reference_value) {
804 return false;
805 }
806 }
807 return true;
808 }
809};
810
811#ifdef HAVOC_TESTING
812
813extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
814{
815 (void)argc;
816 (void)argv;
817 // These are the settings, optimized for the safeuint class (under them, fuzzer reaches maximum expected coverage in
818 // 40 seconds)
819 fuzzer_havoc_settings = HavocSettings{
820 .GEN_LLVM_POST_MUTATION_PROB = 30, // Out of 200
821 .GEN_MUTATION_COUNT_LOG = 5, // Fully checked
822 .GEN_STRUCTURAL_MUTATION_PROBABILITY = 300, // Fully checked
823 .GEN_VALUE_MUTATION_PROBABILITY = 700, // Fully checked
824 .ST_MUT_DELETION_PROBABILITY = 100, // Fully checked
825 .ST_MUT_DUPLICATION_PROBABILITY = 80, // Fully checked
826 .ST_MUT_INSERTION_PROBABILITY = 120, // Fully checked
827 .ST_MUT_MAXIMUM_DELETION_LOG = 6, // Fully checked
828 .ST_MUT_MAXIMUM_DUPLICATION_LOG = 2, // Fully checked
829 .ST_MUT_SWAP_PROBABILITY = 50, // Fully checked
830 .VAL_MUT_LLVM_MUTATE_PROBABILITY = 250, // Fully checked
831 .VAL_MUT_MONTGOMERY_PROBABILITY = 130, // Fully checked
832 .VAL_MUT_NON_MONTGOMERY_PROBABILITY = 50, // Fully checked
833 .VAL_MUT_SMALL_ADDITION_PROBABILITY = 110, // Fully checked
834 .VAL_MUT_SPECIAL_VALUE_PROBABILITY = 130 // Fully checked
835
836 };
841 /*
842 std::random_device rd;
843 std::uniform_int_distribution<uint64_t> dist(0, ~(uint64_t)(0));
844 srandom(static_cast<unsigned int>(dist(rd)));
845
846 fuzzer_havoc_settings =
847 HavocSettings{ .GEN_MUTATION_COUNT_LOG = static_cast<size_t>((random() % 8) + 1),
848 .GEN_STRUCTURAL_MUTATION_PROBABILITY = static_cast<size_t>(random() % 100),
849 .GEN_VALUE_MUTATION_PROBABILITY = static_cast<size_t>(random() % 100),
850 .ST_MUT_DELETION_PROBABILITY = static_cast<size_t>(random() % 100),
851 .ST_MUT_DUPLICATION_PROBABILITY = static_cast<size_t>(random() % 100),
852 .ST_MUT_INSERTION_PROBABILITY = static_cast<size_t>((random() % 99) + 1),
853 .ST_MUT_MAXIMUM_DELETION_LOG = static_cast<size_t>((random() % 8) + 1),
854 .ST_MUT_MAXIMUM_DUPLICATION_LOG = static_cast<size_t>((random() % 8) + 1),
855 .ST_MUT_SWAP_PROBABILITY = static_cast<size_t>(random() % 100),
856 .VAL_MUT_LLVM_MUTATE_PROBABILITY = static_cast<size_t>(random() % 100),
857 .VAL_MUT_MONTGOMERY_PROBABILITY = static_cast<size_t>(random() % 100),
858 .VAL_MUT_NON_MONTGOMERY_PROBABILITY = static_cast<size_t>(random() % 100),
859 .VAL_MUT_SMALL_ADDITION_PROBABILITY = static_cast<size_t>(random() % 100),
860 .VAL_MUT_SPECIAL_VALUE_PROBABILITY = static_cast<size_t>(random() % 100)
861
862 };
863 while (fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY == 0 &&
864 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY == 0) {
865 fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY = static_cast<size_t>(random() % 8);
866 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY = static_cast<size_t>(random() % 8);
867 }
868 */
869
870 // fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB = static_cast<size_t>(((random() % (20 - 1)) + 1) * 10);
875 /*
876 std::cerr << "CUSTOM MUTATOR SETTINGS:" << std::endl
877 << "################################################################" << std::endl
878 << "GEN_LLVM_POST_MUTATION_PROB: " << fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB << std::endl
879 << "GEN_MUTATION_COUNT_LOG: " << fuzzer_havoc_settings.GEN_MUTATION_COUNT_LOG << std::endl
880 << "GEN_STRUCTURAL_MUTATION_PROBABILITY: " << fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY
881 << std::endl
882 << "GEN_VALUE_MUTATION_PROBABILITY: " << fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY << std::endl
883 << "ST_MUT_DELETION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_DELETION_PROBABILITY << std::endl
884 << "ST_MUT_DUPLICATION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_DUPLICATION_PROBABILITY << std::endl
885 << "ST_MUT_INSERTION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_INSERTION_PROBABILITY << std::endl
886 << "ST_MUT_MAXIMUM_DELETION_LOG: " << fuzzer_havoc_settings.ST_MUT_MAXIMUM_DELETION_LOG << std::endl
887 << "ST_MUT_MAXIMUM_DUPLICATION_LOG: " << fuzzer_havoc_settings.ST_MUT_MAXIMUM_DUPLICATION_LOG << std::endl
888 << "ST_MUT_SWAP_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_SWAP_PROBABILITY << std::endl
889 << "VAL_MUT_LLVM_MUTATE_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_LLVM_MUTATE_PROBABILITY
890 << std::endl
891 << "VAL_MUT_MONTGOMERY_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_MONTGOMERY_PROBABILITY << std::endl
892 << "VAL_MUT_NON_MONTGOMERY_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_NON_MONTGOMERY_PROBABILITY
893 << std::endl
894 << "VAL_MUT_SMALL_ADDITION_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_SMALL_ADDITION_PROBABILITY
895 << std::endl
896 << "VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY: "
897 << fuzzer_havoc_settings.VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY << std::endl
898 << "VAL_MUT_SPECIAL_VALUE_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_SPECIAL_VALUE_PROBABILITY
899 << std::endl;
900 */
901 std::vector<size_t> structural_mutation_distribution;
902 std::vector<size_t> value_mutation_distribution;
903 size_t temp = 0;
904 temp += fuzzer_havoc_settings.ST_MUT_DELETION_PROBABILITY;
905 structural_mutation_distribution.push_back(temp);
906 temp += fuzzer_havoc_settings.ST_MUT_DUPLICATION_PROBABILITY;
907 structural_mutation_distribution.push_back(temp);
908 temp += fuzzer_havoc_settings.ST_MUT_INSERTION_PROBABILITY;
909 structural_mutation_distribution.push_back(temp);
910 temp += fuzzer_havoc_settings.ST_MUT_SWAP_PROBABILITY;
911 structural_mutation_distribution.push_back(temp);
912 fuzzer_havoc_settings.structural_mutation_distribution = structural_mutation_distribution;
913
914 temp = 0;
915 temp += fuzzer_havoc_settings.VAL_MUT_LLVM_MUTATE_PROBABILITY;
916 value_mutation_distribution.push_back(temp);
917 temp += fuzzer_havoc_settings.VAL_MUT_SMALL_ADDITION_PROBABILITY;
918 value_mutation_distribution.push_back(temp);
919
920 temp += fuzzer_havoc_settings.VAL_MUT_SPECIAL_VALUE_PROBABILITY;
921 value_mutation_distribution.push_back(temp);
922 fuzzer_havoc_settings.value_mutation_distribution = value_mutation_distribution;
923 return 0;
924}
925#endif
926#ifndef DISABLE_CUSTOM_MUTATORS
931extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* Data, size_t Size, size_t MaxSize, unsigned int Seed)
932{
934 auto fast_random = FastRandom(Seed);
935 auto size_occupied = ArithmeticFuzzHelper<FuzzerClass>::MutateInstructionBuffer(Data, Size, MaxSize, fast_random);
936 if ((fast_random.next() % 200) < fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB) {
937 size_occupied = LLVMFuzzerMutate(Data, size_occupied, MaxSize);
938 }
939 return size_occupied;
940}
941
946extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t* Data1,
947 size_t Size1,
948 const uint8_t* Data2,
949 size_t Size2,
950 uint8_t* Out,
951 size_t MaxOutSize,
952 unsigned int Seed)
953{
955 auto fast_random = FastRandom(Seed);
958 auto vecC = ArithmeticFuzzHelper<FuzzerClass>::crossoverInstructionVector(vecA, vecB, fast_random);
960}
961
962#endif
963
968extern "C" size_t LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size)
969{
970 RunWithBuilders<ByteArrayFuzzBase, FuzzerCircuitTypes>(Data, Size, VarianceRNG);
971 return 0;
972}
973
974#pragma clang diagnostic pop
static size_t writeInstructionsToBuffer(std::vector< typename T::Instruction > &instructions, uint8_t *Data, size_t MaxSize)
Write instructions into the buffer until there are no instructions left or there is no more space.
Definition: fuzzer.hpp:561
static std::vector< typename T::Instruction > parseDataIntoInstructions(const uint8_t *Data, size_t Size)
Parses a given data buffer into a vector of instructions for testing the arithmetic.
Definition: fuzzer.hpp:520
static size_t MutateInstructionBuffer(uint8_t *Data, size_t Size, size_t MaxSize, FastRandom &rng)
Interpret the data buffer as a series of arithmetic instructions and mutate it accordingly.
Definition: fuzzer.hpp:670
static std::vector< typename T::Instruction > crossoverInstructionVector(const std::vector< typename T::Instruction > &vecA, const std::vector< typename T::Instruction > &vecB, FastRandom &rng)
Splice two instruction vectors into one randomly.
Definition: fuzzer.hpp:458
Definition: byte_array.fuzzer.hpp:242
This class implements the execution of safeuint with an oracle to detect discrepancies.
Definition: byte_array.fuzzer.hpp:367
static size_t execute_CONSTANT(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the constant instruction (push constant safeuint to the stack)
Definition: byte_array.fuzzer.hpp:587
static size_t execute_SLICE(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the slice instruction.
Definition: byte_array.fuzzer.hpp:637
static size_t execute_SET(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the SET instruction.
Definition: byte_array.fuzzer.hpp:748
static size_t execute_ADD(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the ADD (append) instruction.
Definition: byte_array.fuzzer.hpp:718
static size_t execute_RANDOMSEED(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the RANDOMSEED instruction.
Definition: byte_array.fuzzer.hpp:776
static size_t execute_GET_BIT(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the GET_BIT instruction.
Definition: byte_array.fuzzer.hpp:667
static size_t execute_SET_BIT(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the SET_BIT instruction.
Definition: byte_array.fuzzer.hpp:696
static size_t execute_REVERSE(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the REVERSE instruction.
Definition: byte_array.fuzzer.hpp:607
A class representing a single fuzzing instruction.
Definition: byte_array.fuzzer.hpp:48
static Instruction generateRandom(T &rng)
Generate a random instruction.
Definition: byte_array.fuzzer.hpp:110
static Instruction mutateInstruction(Instruction instruction, T &rng, HavocSettings &havoc_config)
Mutate a single instruction.
Definition: byte_array.fuzzer.hpp:181
Parser class handles the parsing and writing the instructions back to data buffer.
Definition: byte_array.fuzzer.hpp:257
static void writeInstruction(Instruction &instruction, uint8_t *Data)
Write a single instruction to buffer.
Definition: byte_array.fuzzer.hpp:318
static Instruction parseInstructionArgs(uint8_t *Data)
Parse a single instruction from data.
Definition: byte_array.fuzzer.hpp:266
The class parametrizing ByteArray fuzzing instructions, execution, etc.
Definition: byte_array.fuzzer.hpp:32
static bool postProcess(Builder *builder, std::vector< ByteArrayFuzzBase::ExecutionHandler > &stack)
Check that the resulting values are equal to expected.
Definition: byte_array.fuzzer.hpp:798
Class for quickly deterministically creating new random values. We don't care about distribution much...
Definition: fuzzer.hpp:64
Definition: uint256.hpp:25
Definition: standard_circuit_builder.hpp:12
Definition: byte_array.hpp:9
bool_t< Builder > get_bit(size_t index) const
Extract a bit from the byte array.
Definition: byte_array.cpp:298
void set_bit(size_t index, bool_t< Builder > const &value)
Set a bit in the byte array.
Definition: byte_array.cpp:320
byte_array reverse() const
Reverse the bytes in the byte array.
Definition: byte_array.cpp:264
Definition: field.hpp:10
Definition: safe_uint.hpp:17
Concept for a simple PRNG which returns a uint32_t when next is called.
Definition: fuzzer.hpp:91
Definition: byte_array.fuzzer.hpp:51
Definition: byte_array.fuzzer.hpp:78
Definition: byte_array.fuzzer.hpp:83
Definition: byte_array.fuzzer.hpp:72
Definition: byte_array.fuzzer.hpp:67
Definition: byte_array.fuzzer.hpp:63
Definition: fuzzer.hpp:27
Definition: byte_array.fuzzer.hpp:89