barretenberg
Loading...
Searching...
No Matches
bit_array.fuzzer.hpp
1#include "barretenberg/numeric/random/engine.hpp"
2#include "barretenberg/stdlib/primitives/bit_array/bit_array.hpp"
3#pragma clang diagnostic push
4#pragma clang diagnostic ignored "-Wc99-designator"
5
6#define MAX_ARRAY_SIZE 128
7
8// This is a global variable, so that the execution handling class could alter it and signal to the input tester that
9// the input should fail
10bool circuit_should_fail = false;
11
12#define HAVOC_TESTING
13
14#include "barretenberg/common/fuzzer.hpp"
15FastRandom VarianceRNG(0);
16
17// Enable this definition, when you want to find out the instructions that caused a failure
18// #define SHOW_INFORMATION 1
19
20#define OPERATION_TYPE_SIZE 1
21
22#define ELEMENT_SIZE (sizeof(fr) + 1)
23#define TWO_IN_ONE_OUT 3
24#define THREE_IN_ONE_OUT 4
25#define SLICE_ARGS_SIZE 6
26
31template <typename Builder> class BitArrayFuzzBase {
32 private:
35 template <size_t NumBytes, size_t NumWords>
36 static std::vector<uint8_t> to_vector(std::array<proof_system::plonk::stdlib::uint32<Builder>, NumWords>& a32)
37 {
38 /* Convert array of uint32_t to vector of uint8_t */
39 std::vector<uint8_t> v(NumBytes);
40 for (size_t i = 0; i < a32.size(); i++) {
41 const uint32_t u32 = htonl(static_cast<uint32_t>(a32[i].get_value()));
42 memcpy(v.data() + (i * 4), &u32, 4);
43 }
44
45 return v;
46 }
47
48 template <size_t NumWords>
49 static std::vector<uint8_t> vector_via_populate_uint32_array(bit_array_t& bit_array, const size_t offset = 0)
50 {
51 /* NumWords can never be 0 because this case has explicit specialization */
52 static_assert(NumWords != 0);
53
54 constexpr size_t NumBits = NumWords * 32;
55 constexpr size_t NumBytes = NumWords * 4;
56
57 if (offset > bit_array.size() || (bit_array.size() - offset) % 32 != 0) {
58 /* If the offset is out-of-bounds, or the output would not
59 * be a multiple of 32 bits, return the whole buffer.
60 */
61 return static_cast<byte_array_t>(bit_array).get_value();
62 } else if (bit_array.size() - offset == NumBits) {
63 std::array<proof_system::plonk::stdlib::uint32<Builder>, NumWords> a32;
64 bit_array.template populate_uint32_array<NumWords>(offset, a32);
65 return to_vector<NumBytes, NumWords>(a32);
66 } else {
67 /* Template recursion: iterate from NumWords..0 */
68 return vector_via_populate_uint32_array<NumWords - 1>(bit_array, offset);
69 }
70 }
71
72 template <> static std::vector<uint8_t> vector_via_populate_uint32_array<0>(bit_array_t&, const size_t)
73 {
74 return {};
75 }
76
77 template <size_t NumBytes>
78 static std::vector<uint8_t> bit_array_to_a32(bit_array_t& bit_array, const bool cast_or_populate)
79 {
80 /* NumBytes can never be 0 because this case has explicit specialization */
81 static_assert(NumBytes != 0);
82
83 /* Must be a multiple of uint32_t for this to work */
84 static_assert(NumBytes % 4 == 0);
85
86 constexpr size_t NumWords = NumBytes / 4;
87 constexpr size_t NumBits = NumBytes * 8;
88
89 if (bit_array.size() % 32 != 0) {
90 /* Bit array is not a multiple of 32 bits; cannot convert to array of uint32_t.
91 * Return vector instead.
92 */
93 return static_cast<byte_array_t>(bit_array).get_value();
94 } else if (bit_array.size() == NumBits) {
95 std::array<proof_system::plonk::stdlib::uint32<Builder>, NumWords> a32;
96
97 /* Switch between two different methods to retrieve the uint32 array */
98 if (cast_or_populate) {
99 a32 = static_cast<decltype(a32)>(bit_array);
100 } else {
101 bit_array.template populate_uint32_array<NumWords>(0, a32);
102 }
103
104 return to_vector<NumBytes, NumWords>(a32);
105 } else {
106 /* Template recursion: iterate from NumBytes..0 */
107 return bit_array_to_a32<NumBytes - 4>(bit_array, cast_or_populate);
108 }
109 }
110
111 template <> std::vector<uint8_t> bit_array_to_a32<0>(bit_array_t&, const bool) { return {}; }
112
113 template <class From, class To> static To from_to(const From& in, const std::optional<size_t> size = std::nullopt)
114 {
115 return To(in.data(), in.data() + (size ? *size : in.size()));
116 }
117
118 public:
124 public:
125 enum OPCODE { CONSTANT, GET_BIT, SET_BIT, SLICE, SET, RANDOMSEED, _LAST };
126 struct Element {
127 public:
128 std::array<uint8_t, MAX_ARRAY_SIZE> data;
129 uint16_t size;
130
131 uint16_t real_size(void) const { return std::min(size, static_cast<uint16_t>(MAX_ARRAY_SIZE)); }
132 std::string as_string(void) const { return from_to<decltype(data), std::string>(data, real_size()); }
133 };
134 struct GetBitArgs {
135 uint8_t in;
136 uint8_t out;
137 uint32_t bit;
138 };
139 struct SetBitArgs {
140 uint8_t in;
141 uint32_t bit;
142 uint8_t value;
143 };
144 struct SliceArgs {
145 uint8_t in;
146 uint8_t out;
147 uint16_t offset;
148 };
149 struct TwoArgs {
150 uint8_t in;
151 uint8_t out;
152 };
153
155 uint32_t randomseed;
156 Element element;
157 GetBitArgs getBitArgs;
158 SetBitArgs setBitArgs;
159 SliceArgs sliceArgs;
160 TwoArgs twoArgs;
161 };
162 // The type of instruction
163 OPCODE id;
164 // Instruction arguments
165 ArgumentContents arguments;
173 template <typename T>
174 inline static Instruction generateRandom(T& rng)
175 requires SimpleRng<T>
176 {
177 // Choose which instruction we are going to generate
178 OPCODE instruction_opcode = static_cast<OPCODE>(rng.next() % (OPCODE::_LAST));
179 uint8_t in1, out, value;
180 uint32_t bit;
181 uint16_t offset;
182 // Depending on instruction
183 switch (instruction_opcode) {
184 case OPCODE::CONSTANT:
185 // Return instruction
186 {
187 std::array<uint8_t, MAX_ARRAY_SIZE> data;
188 for (size_t i = 0; i < MAX_ARRAY_SIZE; i++) {
189 data[i] = rng.next() & 0xFF;
190 }
191
192 const uint16_t size = rng.next() & 0xFFFF;
193 return { .id = instruction_opcode, .arguments.element = { .data = data, .size = size } };
194 }
195 break;
196 case OPCODE::GET_BIT:
197 in1 = static_cast<uint8_t>(rng.next() & 0xff);
198 out = static_cast<uint8_t>(rng.next() & 0xff);
199 bit = static_cast<uint32_t>(rng.next() & 0xffffffff);
200 return { .id = instruction_opcode, .arguments.getBitArgs = { .in = in1, .out = out, .bit = bit } };
201 case OPCODE::SET_BIT:
202 in1 = static_cast<uint8_t>(rng.next() & 0xff);
203 bit = static_cast<uint32_t>(rng.next() & 0xffffffff);
204 value = static_cast<uint8_t>(rng.next() & 0xff);
205 return { .id = instruction_opcode, .arguments.setBitArgs = { .in = in1, .bit = bit, .value = value } };
206 case OPCODE::SLICE:
207 in1 = static_cast<uint8_t>(rng.next() & 0xff);
208 out = static_cast<uint8_t>(rng.next() & 0xff);
209 offset = static_cast<uint16_t>(rng.next() & 0xffff);
210 return { .id = instruction_opcode, .arguments.sliceArgs = { .in = in1, .out = out, .offset = offset } };
211 case OPCODE::SET:
212 in1 = static_cast<uint8_t>(rng.next() & 0xff);
213 out = static_cast<uint8_t>(rng.next() & 0xff);
214 return { .id = instruction_opcode, .arguments.twoArgs = { .in = in1, .out = out } };
215 case OPCODE::RANDOMSEED:
216 return { .id = instruction_opcode, .arguments.randomseed = rng.next() };
217 break;
218 default:
219 abort(); // We have missed some instructions, it seems
220 break;
221 }
222 }
223
233 template <typename T>
234 inline static Instruction mutateInstruction(Instruction instruction, T& rng, HavocSettings& havoc_config)
235 requires SimpleRng<T>
236 {
237 (void)rng;
238 (void)havoc_config;
239#define PUT_RANDOM_BYTE_IF_LUCKY(variable) \
240 if (rng.next() & 1) { \
241 variable = rng.next() & 0xff; \
242 }
243#define PUT_RANDOM_TWO_BYTES_IF_LUCKY(variable) \
244 if (rng.next() & 1) { \
245 variable = rng.next() & 0xffff; \
246 }
247#define PUT_RANDOM_FOUR_BYTES_IF_LUCKY(variable) \
248 if (rng.next() & 1) { \
249 variable = rng.next() & 0xffffffff; \
250 }
251 // Depending on instruction type...
252 switch (instruction.id) {
253 case OPCODE::CONSTANT:
254 break;
255 case OPCODE::GET_BIT:
256 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.getBitArgs.in)
257 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.getBitArgs.out)
258 PUT_RANDOM_FOUR_BYTES_IF_LUCKY(instruction.arguments.getBitArgs.bit)
259 break;
260 case OPCODE::SET_BIT:
261 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.setBitArgs.in)
262 PUT_RANDOM_FOUR_BYTES_IF_LUCKY(instruction.arguments.setBitArgs.bit)
263 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.setBitArgs.value)
264 break;
265 case OPCODE::SLICE:
266 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.sliceArgs.in)
267 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.sliceArgs.out)
268 PUT_RANDOM_TWO_BYTES_IF_LUCKY(instruction.arguments.sliceArgs.offset)
269 break;
270 case OPCODE::SET:
271 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.twoArgs.in)
272 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.twoArgs.out)
273 break;
274 case OPCODE::RANDOMSEED:
275 instruction.arguments.randomseed = rng.next();
276 break;
277 default:
278 abort(); // New instruction encountered
279 break;
280 }
281 // Return mutated instruction
282 return instruction;
283 }
284 };
285 // We use argsizes to both specify the size of data needed to parse the instruction and to signal that the
286 // instruction is enabled (if it is -1,it's disabled )
287 class ArgSizes {
288 public:
289 static constexpr size_t CONSTANT = MAX_ARRAY_SIZE + sizeof(uint16_t);
290 static constexpr size_t GET_BIT = 6;
291 static constexpr size_t SET_BIT = 6;
292 static constexpr size_t SLICE = 4;
293 static constexpr size_t SET = 2;
294 static constexpr size_t RANDOMSEED = sizeof(uint32_t);
295 };
300 class Parser {
301 public:
309 template <typename Instruction::OPCODE opcode> inline static Instruction parseInstructionArgs(uint8_t* Data)
310 {
311 if constexpr (opcode == Instruction::OPCODE::CONSTANT) {
312 std::array<uint8_t, MAX_ARRAY_SIZE> data;
313 std::copy_n(Data, data.size(), data.begin());
314
315 uint16_t size;
316 memcpy(&size, Data + MAX_ARRAY_SIZE, sizeof(uint16_t));
317
318 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
319 .arguments.element = { .data = data, .size = size } };
320 }
321 if constexpr (opcode == Instruction::OPCODE::GET_BIT) {
322 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
323 .arguments.getBitArgs = {
324 .in = *Data, .out = *(Data + 1), .bit = *((uint32_t*)(Data + 2)) } };
325 }
326 if constexpr (opcode == Instruction::OPCODE::SET_BIT) {
327 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
328 .arguments.setBitArgs = {
329 .in = *Data, .bit = *((uint32_t*)(Data + 1)), .value = *(Data + 5) } };
330 }
331 if constexpr (opcode == Instruction::OPCODE::SLICE) {
332 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
333 .arguments.sliceArgs = {
334 .in = *Data, .out = *(Data + 1), .offset = *((uint16_t*)(Data + 2)) } };
335 }
336 if constexpr (opcode == Instruction::OPCODE::SET) {
337 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
338 .arguments.twoArgs = { .in = *Data, .out = *(Data + 1) } };
339 }
340 if constexpr (opcode == Instruction::OPCODE::RANDOMSEED) {
341 uint32_t randomseed;
342 memcpy(&randomseed, Data, sizeof(uint32_t));
343 return Instruction{ .id = static_cast<typename Instruction::OPCODE>(opcode),
344 .arguments.randomseed = randomseed };
345 };
346 }
354 template <typename Instruction::OPCODE instruction_opcode>
355 inline static void writeInstruction(Instruction& instruction, uint8_t* Data)
356 {
357 if constexpr (instruction_opcode == Instruction::OPCODE::CONSTANT) {
358 *Data = instruction.id;
359 memcpy(Data + 1, instruction.arguments.element.data.data(), MAX_ARRAY_SIZE);
360 memcpy(Data + 1 + MAX_ARRAY_SIZE, &instruction.arguments.element.size, sizeof(uint16_t));
361 }
362 if constexpr (instruction_opcode == Instruction::OPCODE::GET_BIT) {
363 *Data = instruction.id;
364 *(Data + 1) = instruction.arguments.getBitArgs.in;
365 *(Data + 2) = instruction.arguments.getBitArgs.out;
366 *((uint32_t*)(Data + 3)) = instruction.arguments.getBitArgs.bit;
367 }
368 if constexpr (instruction_opcode == Instruction::OPCODE::SET_BIT) {
369 *Data = instruction.id;
370 *(Data + 1) = instruction.arguments.setBitArgs.in;
371 *((uint32_t*)(Data + 2)) = instruction.arguments.setBitArgs.bit;
372 *(Data + 6) = instruction.arguments.setBitArgs.value;
373 }
374 if constexpr (instruction_opcode == Instruction::OPCODE::SLICE) {
375 *Data = instruction.id;
376 *(Data + 1) = instruction.arguments.sliceArgs.in;
377 *(Data + 2) = instruction.arguments.sliceArgs.out;
378 *((uint16_t*)(Data + 3)) = instruction.arguments.sliceArgs.offset;
379 }
380 if constexpr (instruction_opcode == Instruction::OPCODE::SET) {
381 *Data = instruction.id;
382 *(Data + 1) = instruction.arguments.twoArgs.in;
383 *(Data + 2) = instruction.arguments.twoArgs.out;
384 }
385 if constexpr (instruction_opcode == Instruction::OPCODE::RANDOMSEED) {
386
387 *Data = instruction.id;
388 memcpy(Data + 1, &instruction.arguments.randomseed, sizeof(uint32_t));
389 }
390 }
391 };
397 public:
398 std::vector<uint8_t> reference_value;
399
400 bit_array_t bit_array{ nullptr, std::vector<uint8_t>{} };
401
402 static bool get_bit(const std::vector<uint8_t>& v, size_t bit, const bool rev = true)
403 {
404 const size_t pos = rev ? v.size() - 1 - (bit / 8) : (bit / 8);
405 bit = rev ? bit % 8 : 7 - (bit % 8);
406 return static_cast<bool>(v[
407 /* Byte */ pos] &
408 /* Bit */ (1 << bit));
409 }
410 static void set_bit(std::vector<uint8_t>& v, size_t bit, const bool value, const bool rev = true)
411 {
412 const size_t pos = rev ? v.size() - 1 - (bit / 8) : (bit / 8);
413 bit = rev ? bit % 8 : 7 - (bit % 8);
414 if (value) {
415 v[
416 /* Byte */ pos] |=
417 /* Bit */ (1 << bit);
418 } else {
419 v[
420 /* Byte */ pos] &=
421 /* Bit */ ~(1 << bit);
422 }
423 }
424
425 static std::vector<uint8_t> get_value(bit_array_t& bit_array)
426 {
427 const uint8_t which = VarianceRNG.next() % 4;
428
429 switch (which) {
430 case 0:
431 return from_to<std::string, std::vector<uint8_t>>(bit_array.get_witness_as_string());
432 case 1: {
433 const auto bits = bit_array.get_bits();
434 std::vector<uint8_t> ret((bits.size() + 7) / 8, 0);
435 for (size_t i = 0; i < bits.size(); i++) {
436 set_bit(ret, i, bits[i].get_value());
437 }
438 return ret;
439 }
440 case 2:
441 return static_cast<byte_array_t>(bit_array).get_value();
442 case 3:
443 return bit_array_to_a32<MAX_ARRAY_SIZE>(bit_array, static_cast<bool>(VarianceRNG.next() % 2));
444 static_assert(MAX_ARRAY_SIZE % 32 == 0);
445 if (bit_array.size() == MAX_ARRAY_SIZE / 32) {
446 std::array<uint32_t, MAX_ARRAY_SIZE> a32;
447 const auto a32_ =
448 static_cast<std::array<proof_system::plonk::stdlib::uint32<Builder>, MAX_ARRAY_SIZE>>(
449 bit_array);
450 for (size_t i = 0; i < a32_.size(); i++) {
451 a32[i] = static_cast<uint32_t>(a32_[i].get_value());
452 }
453 return from_to<std::array<uint32_t, MAX_ARRAY_SIZE>, std::vector<uint8_t>>(a32);
454 } else {
455 return static_cast<byte_array_t>(bit_array).get_value();
456 }
457 default:
458 abort();
459 }
460 }
461 static std::vector<uint8_t> v32_to_v8(const std::vector<uint32_t>& v)
462 {
463 return from_to<std::vector<uint32_t>, std::vector<uint8_t>>(v);
464 }
465 static const std::vector<uint8_t>& bool_to_vector(const bool& b)
466 {
467 static const std::vector<uint8_t> false_{ 0 };
468 static const std::vector<uint8_t> true_{ 1 };
469 return b ? true_ : false_;
470 }
471 ExecutionHandler() = default;
472 ExecutionHandler(std::vector<uint8_t>& r, bit_array_t& s)
473 : reference_value(r)
474 , bit_array(s)
475 {}
476 ExecutionHandler(std::vector<uint8_t> r, bit_array_t s)
477 : reference_value(r)
478 , bit_array(s)
479 {}
481 : reference_value(get_value(s))
482 , bit_array(s)
483 {}
484
485 ExecutionHandler get_bit(Builder* builder, const size_t bit) const
486 {
487 if (bit >= this->reference_value.size() * 8) {
488 return ExecutionHandler(this->reference_value, this->bit_array);
489 } else {
490 const bool is_set_ref = get_bit(this->reference_value, bit);
491 const bool is_set_ba = this->bit_array[bit].get_value();
492
493 return ExecutionHandler(bool_to_vector(is_set_ref), bit_array_t(builder, bool_to_vector(is_set_ba)));
494 }
495 }
496 /* Modifies the buffer at hand, so does not produce a return value */
497 void set_bit(const size_t bit, const bool value)
498 {
499 if (bit < this->reference_value.size() * 8) {
500 set_bit(this->reference_value, bit, value);
501 this->bit_array[bit] = value;
502 }
503 }
504 /* output = input[offset:] (where offset denotes bits) */
505 ExecutionHandler slice(Builder* builder, const size_t offset)
506 {
507 static_assert(MAX_ARRAY_SIZE % 4 == 0);
508 const auto v_ba = vector_via_populate_uint32_array<MAX_ARRAY_SIZE / 4>(this->bit_array, offset);
509
510 std::vector<uint8_t> v_ref;
511
512 const auto& ref = this->reference_value;
513 const size_t ref_num_bits = ref.size() * 8;
514 const size_t out_num_bits = ref_num_bits - offset;
515 const size_t out_num_bytes = out_num_bits / 8;
516 if (offset > ref_num_bits || out_num_bits % 32 != 0) {
517 v_ref = ref;
518 } else {
519 v_ref.resize(out_num_bytes);
520 for (size_t i = 0; i < out_num_bits; i++) {
521 set_bit(v_ref, i, get_bit(ref, i + offset, false), false);
522 }
523 }
524
525 return ExecutionHandler(v_ref, bit_array_t(builder, v_ba));
526 }
527
528 /* Explicit re-instantiation using the various bit_array constructors */
529 ExecutionHandler set(Builder* builder)
530 {
531 const uint8_t which = VarianceRNG.next() % 6;
532
533 const auto& ref = this->reference_value;
534
535 switch (which) {
536 case 0:
537 /* Construct via bit_array */
538 return ExecutionHandler(ref, bit_array_t(this->bit_array));
539 case 1:
540 /* Construct via std::string */
541 return ExecutionHandler(ref, bit_array_t(builder, this->bit_array.get_witness_as_string()));
542 case 2:
543 /* Construct via std::vector<uint8_t> */
544 return ExecutionHandler(ref,
545 bit_array_t(builder, static_cast<byte_array_t>(this->bit_array).get_value()));
546 case 3:
547 /* Construct via byte_array */
548 return ExecutionHandler(ref, bit_array_t(static_cast<byte_array_t>(this->bit_array)));
549 case 4:
550 if (this->bit_array.size() % 32 != 0) {
551 return ExecutionHandler(ref, bit_array_t(this->bit_array));
552 } else {
553 const auto v = this->bit_array.to_uint32_vector();
554
555 if (v.size() == 1 && static_cast<bool>(VarianceRNG.next() % 2)) {
556 /* Construct via uint32<Builder> */
557 return ExecutionHandler(ref, bit_array_t(v[0]));
558 } else {
559 /* Construct via std::vector<uint32<Builder>> */
560 return ExecutionHandler(ref, bit_array_t(v));
561 }
562 }
563 case 5: {
564 /* Create a bit_array with gibberish.
565 *
566 * The purpose of this is to ascertain that no gibberish
567 * values are retained in the re-assigned value
568 */
569 const size_t gibberish_size = VarianceRNG.next() % (MAX_ARRAY_SIZE * 2);
570 std::vector<uint8_t> gibberish(gibberish_size);
571 for (size_t i = 0; i < gibberish_size; i++) {
572 gibberish[i] = static_cast<uint8_t>(VarianceRNG.next() % 0xFF);
573 }
574 auto ba = bit_array_t(builder, gibberish);
575
576 /* Construct via assignment */
577 ba = this->bit_array;
578
579 return ExecutionHandler(ref, ba);
580 } break;
581 default:
582 abort();
583 }
584 }
585
594 static inline size_t execute_CONSTANT(Builder* builder,
595 std::vector<ExecutionHandler>& stack,
596 Instruction& instruction)
597 {
598 (void)builder;
599 stack.push_back(bit_array_t(builder, instruction.arguments.element.as_string()));
600 return 0;
601 }
610 static inline size_t execute_GET_BIT(Builder* builder,
611 std::vector<ExecutionHandler>& stack,
612 Instruction& instruction)
613 {
614 (void)builder;
615 if (stack.size() == 0) {
616 return 1;
617 }
618 size_t first_index = instruction.arguments.getBitArgs.in % stack.size();
619 size_t output_index = instruction.arguments.getBitArgs.out;
620 const uint32_t bit = instruction.arguments.getBitArgs.bit;
621 ExecutionHandler result;
622 result = stack[first_index].get_bit(builder, bit);
623 // If the output index is larger than the number of elements in stack, append
624 if (output_index >= stack.size()) {
625 stack.push_back(result);
626 } else {
627 stack[output_index] = result;
628 }
629 return 0;
630 };
639 static inline size_t execute_SET_BIT(Builder* builder,
640 std::vector<ExecutionHandler>& stack,
641 Instruction& instruction)
642 {
643 (void)builder;
644 if (stack.size() == 0) {
645 return 1;
646 }
647 size_t first_index = instruction.arguments.setBitArgs.in % stack.size();
648 const uint32_t bit = instruction.arguments.setBitArgs.bit;
649 const bool value = static_cast<bool>(instruction.arguments.setBitArgs.value % 2);
650 stack[first_index].set_bit(bit, value);
651 return 0;
652 };
661 static inline size_t execute_SLICE(Builder* builder,
662 std::vector<ExecutionHandler>& stack,
663 Instruction& instruction)
664 {
665 (void)builder;
666 if (stack.size() == 0) {
667 return 1;
668 }
669 size_t first_index = instruction.arguments.sliceArgs.in % stack.size();
670 size_t output_index = instruction.arguments.sliceArgs.out;
671 const uint16_t offset = instruction.arguments.sliceArgs.offset;
672 ExecutionHandler result;
673 result = stack[first_index].slice(builder, offset);
674 // If the output index is larger than the number of elements in stack, append
675 if (output_index >= stack.size()) {
676 stack.push_back(result);
677 } else {
678 stack[output_index] = result;
679 }
680 return 0;
681 };
690 static inline size_t execute_SET(Builder* builder,
691 std::vector<ExecutionHandler>& stack,
692 Instruction& instruction)
693 {
694 (void)builder;
695 if (stack.size() == 0) {
696 return 1;
697 }
698 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
699 size_t output_index = instruction.arguments.twoArgs.out;
700 ExecutionHandler result;
701 result = stack[first_index].set(builder);
702 // If the output index is larger than the number of elements in stack, append
703 if (output_index >= stack.size()) {
704 stack.push_back(result);
705 } else {
706 stack[output_index] = result;
707 }
708 return 0;
709 };
718 static inline size_t execute_RANDOMSEED(Builder* builder,
719 std::vector<ExecutionHandler>& stack,
720 Instruction& instruction)
721 {
722 (void)builder;
723 (void)stack;
724
725 VarianceRNG.reseed(instruction.arguments.randomseed);
726 return 0;
727 };
728 };
729
730 typedef std::vector<ExecutionHandler> ExecutionState;
740 inline static bool postProcess(Builder* builder, std::vector<BitArrayFuzzBase::ExecutionHandler>& stack)
741 {
742 (void)builder;
743 for (size_t i = 0; i < stack.size(); i++) {
744 auto element = stack[i];
745 const auto other = from_to<std::string, std::vector<uint8_t>>(element.bit_array.get_witness_as_string());
746 if (other != element.reference_value) {
747 printf("Other (as bytes):\n");
748 for (size_t i = 0; i < other.size(); i++) {
749 printf("%02X ", other[i]);
750 }
751 printf("\n");
752 printf("Reference value (as bytes):\n");
753 for (size_t i = 0; i < element.reference_value.size(); i++) {
754 printf("%02X ", element.reference_value[i]);
755 }
756 printf("\n");
757 return false;
758 }
759 }
760 return true;
761 }
762};
763
764#ifdef HAVOC_TESTING
765
766extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
767{
768 (void)argc;
769 (void)argv;
770 // These are the settings, optimized for the safeuint class (under them, fuzzer reaches maximum expected coverage in
771 // 40 seconds)
772 fuzzer_havoc_settings = HavocSettings{
773 .GEN_LLVM_POST_MUTATION_PROB = 30, // Out of 200
774 .GEN_MUTATION_COUNT_LOG = 5, // Fully checked
775 .GEN_STRUCTURAL_MUTATION_PROBABILITY = 300, // Fully checked
776 .GEN_VALUE_MUTATION_PROBABILITY = 700, // Fully checked
777 .ST_MUT_DELETION_PROBABILITY = 100, // Fully checked
778 .ST_MUT_DUPLICATION_PROBABILITY = 80, // Fully checked
779 .ST_MUT_INSERTION_PROBABILITY = 120, // Fully checked
780 .ST_MUT_MAXIMUM_DELETION_LOG = 6, // Fully checked
781 .ST_MUT_MAXIMUM_DUPLICATION_LOG = 2, // Fully checked
782 .ST_MUT_SWAP_PROBABILITY = 50, // Fully checked
783 .VAL_MUT_LLVM_MUTATE_PROBABILITY = 250, // Fully checked
784 .VAL_MUT_MONTGOMERY_PROBABILITY = 130, // Fully checked
785 .VAL_MUT_NON_MONTGOMERY_PROBABILITY = 50, // Fully checked
786 .VAL_MUT_SMALL_ADDITION_PROBABILITY = 110, // Fully checked
787 .VAL_MUT_SPECIAL_VALUE_PROBABILITY = 130 // Fully checked
788
789 };
794 /*
795 std::random_device rd;
796 std::uniform_int_distribution<uint64_t> dist(0, ~(uint64_t)(0));
797 srandom(static_cast<unsigned int>(dist(rd)));
798
799 fuzzer_havoc_settings =
800 HavocSettings{ .GEN_MUTATION_COUNT_LOG = static_cast<size_t>((random() % 8) + 1),
801 .GEN_STRUCTURAL_MUTATION_PROBABILITY = static_cast<size_t>(random() % 100),
802 .GEN_VALUE_MUTATION_PROBABILITY = static_cast<size_t>(random() % 100),
803 .ST_MUT_DELETION_PROBABILITY = static_cast<size_t>(random() % 100),
804 .ST_MUT_DUPLICATION_PROBABILITY = static_cast<size_t>(random() % 100),
805 .ST_MUT_INSERTION_PROBABILITY = static_cast<size_t>((random() % 99) + 1),
806 .ST_MUT_MAXIMUM_DELETION_LOG = static_cast<size_t>((random() % 8) + 1),
807 .ST_MUT_MAXIMUM_DUPLICATION_LOG = static_cast<size_t>((random() % 8) + 1),
808 .ST_MUT_SWAP_PROBABILITY = static_cast<size_t>(random() % 100),
809 .VAL_MUT_LLVM_MUTATE_PROBABILITY = static_cast<size_t>(random() % 100),
810 .VAL_MUT_MONTGOMERY_PROBABILITY = static_cast<size_t>(random() % 100),
811 .VAL_MUT_NON_MONTGOMERY_PROBABILITY = static_cast<size_t>(random() % 100),
812 .VAL_MUT_SMALL_ADDITION_PROBABILITY = static_cast<size_t>(random() % 100),
813 .VAL_MUT_SPECIAL_VALUE_PROBABILITY = static_cast<size_t>(random() % 100)
814
815 };
816 while (fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY == 0 &&
817 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY == 0) {
818 fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY = static_cast<size_t>(random() % 8);
819 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY = static_cast<size_t>(random() % 8);
820 }
821 */
822
823 // fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB = static_cast<size_t>(((random() % (20 - 1)) + 1) * 10);
828 /*
829 std::cerr << "CUSTOM MUTATOR SETTINGS:" << std::endl
830 << "################################################################" << std::endl
831 << "GEN_LLVM_POST_MUTATION_PROB: " << fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB << std::endl
832 << "GEN_MUTATION_COUNT_LOG: " << fuzzer_havoc_settings.GEN_MUTATION_COUNT_LOG << std::endl
833 << "GEN_STRUCTURAL_MUTATION_PROBABILITY: " << fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY
834 << std::endl
835 << "GEN_VALUE_MUTATION_PROBABILITY: " << fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY << std::endl
836 << "ST_MUT_DELETION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_DELETION_PROBABILITY << std::endl
837 << "ST_MUT_DUPLICATION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_DUPLICATION_PROBABILITY << std::endl
838 << "ST_MUT_INSERTION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_INSERTION_PROBABILITY << std::endl
839 << "ST_MUT_MAXIMUM_DELETION_LOG: " << fuzzer_havoc_settings.ST_MUT_MAXIMUM_DELETION_LOG << std::endl
840 << "ST_MUT_MAXIMUM_DUPLICATION_LOG: " << fuzzer_havoc_settings.ST_MUT_MAXIMUM_DUPLICATION_LOG << std::endl
841 << "ST_MUT_SWAP_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_SWAP_PROBABILITY << std::endl
842 << "VAL_MUT_LLVM_MUTATE_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_LLVM_MUTATE_PROBABILITY
843 << std::endl
844 << "VAL_MUT_MONTGOMERY_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_MONTGOMERY_PROBABILITY << std::endl
845 << "VAL_MUT_NON_MONTGOMERY_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_NON_MONTGOMERY_PROBABILITY
846 << std::endl
847 << "VAL_MUT_SMALL_ADDITION_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_SMALL_ADDITION_PROBABILITY
848 << std::endl
849 << "VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY: "
850 << fuzzer_havoc_settings.VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY << std::endl
851 << "VAL_MUT_SPECIAL_VALUE_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_SPECIAL_VALUE_PROBABILITY
852 << std::endl;
853 */
854 std::vector<size_t> structural_mutation_distribution;
855 std::vector<size_t> value_mutation_distribution;
856 size_t temp = 0;
857 temp += fuzzer_havoc_settings.ST_MUT_DELETION_PROBABILITY;
858 structural_mutation_distribution.push_back(temp);
859 temp += fuzzer_havoc_settings.ST_MUT_DUPLICATION_PROBABILITY;
860 structural_mutation_distribution.push_back(temp);
861 temp += fuzzer_havoc_settings.ST_MUT_INSERTION_PROBABILITY;
862 structural_mutation_distribution.push_back(temp);
863 temp += fuzzer_havoc_settings.ST_MUT_SWAP_PROBABILITY;
864 structural_mutation_distribution.push_back(temp);
865 fuzzer_havoc_settings.structural_mutation_distribution = structural_mutation_distribution;
866
867 temp = 0;
868 temp += fuzzer_havoc_settings.VAL_MUT_LLVM_MUTATE_PROBABILITY;
869 value_mutation_distribution.push_back(temp);
870 temp += fuzzer_havoc_settings.VAL_MUT_SMALL_ADDITION_PROBABILITY;
871 value_mutation_distribution.push_back(temp);
872
873 temp += fuzzer_havoc_settings.VAL_MUT_SPECIAL_VALUE_PROBABILITY;
874 value_mutation_distribution.push_back(temp);
875 fuzzer_havoc_settings.value_mutation_distribution = value_mutation_distribution;
876 return 0;
877}
878#endif
879#ifndef DISABLE_CUSTOM_MUTATORS
884extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* Data, size_t Size, size_t MaxSize, unsigned int Seed)
885{
887 auto fast_random = FastRandom(Seed);
888 auto size_occupied = ArithmeticFuzzHelper<FuzzerClass>::MutateInstructionBuffer(Data, Size, MaxSize, fast_random);
889 if ((fast_random.next() % 200) < fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB) {
890 size_occupied = LLVMFuzzerMutate(Data, size_occupied, MaxSize);
891 }
892 return size_occupied;
893}
894
899extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t* Data1,
900 size_t Size1,
901 const uint8_t* Data2,
902 size_t Size2,
903 uint8_t* Out,
904 size_t MaxOutSize,
905 unsigned int Seed)
906{
908 auto fast_random = FastRandom(Seed);
911 auto vecC = ArithmeticFuzzHelper<FuzzerClass>::crossoverInstructionVector(vecA, vecB, fast_random);
913}
914
915#endif
916
921extern "C" size_t LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size)
922{
923 RunWithBuilders<BitArrayFuzzBase, FuzzerCircuitTypes>(Data, Size, VarianceRNG);
924 return 0;
925}
926
927#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: bit_array.fuzzer.hpp:287
This class implements the execution of safeuint with an oracle to detect discrepancies.
Definition: bit_array.fuzzer.hpp:396
static size_t execute_SET(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the SET instruction.
Definition: bit_array.fuzzer.hpp:690
static size_t execute_GET_BIT(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the GET_BIT instruction.
Definition: bit_array.fuzzer.hpp:610
static size_t execute_RANDOMSEED(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the RANDOMSEED instruction.
Definition: bit_array.fuzzer.hpp:718
static size_t execute_SET_BIT(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the SET_BIT instruction.
Definition: bit_array.fuzzer.hpp:639
static size_t execute_SLICE(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the SLICE instruction.
Definition: bit_array.fuzzer.hpp:661
static size_t execute_CONSTANT(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the constant instruction (push constant safeuint to the stack)
Definition: bit_array.fuzzer.hpp:594
A class representing a single fuzzing instruction.
Definition: bit_array.fuzzer.hpp:123
static Instruction generateRandom(T &rng)
Generate a random instruction.
Definition: bit_array.fuzzer.hpp:174
static Instruction mutateInstruction(Instruction instruction, T &rng, HavocSettings &havoc_config)
Mutate a single instruction.
Definition: bit_array.fuzzer.hpp:234
Parser class handles the parsing and writing the instructions back to data buffer.
Definition: bit_array.fuzzer.hpp:300
static void writeInstruction(Instruction &instruction, uint8_t *Data)
Write a single instruction to buffer.
Definition: bit_array.fuzzer.hpp:355
static Instruction parseInstructionArgs(uint8_t *Data)
Parse a single instruction from data.
Definition: bit_array.fuzzer.hpp:309
The class parametrizing ByteArray fuzzing instructions, execution, etc.
Definition: bit_array.fuzzer.hpp:31
static bool postProcess(Builder *builder, std::vector< BitArrayFuzzBase::ExecutionHandler > &stack)
Check that the resulting values are equal to expected.
Definition: bit_array.fuzzer.hpp:740
Class for quickly deterministically creating new random values. We don't care about distribution much...
Definition: fuzzer.hpp:64
Definition: standard_circuit_builder.hpp:12
Definition: bit_array.hpp:9
Definition: byte_array.hpp:9
Concept for a simple PRNG which returns a uint32_t when next is called.
Definition: fuzzer.hpp:91
Definition: bit_array.fuzzer.hpp:126
Definition: bit_array.fuzzer.hpp:134
Definition: bit_array.fuzzer.hpp:139
Definition: bit_array.fuzzer.hpp:144
Definition: bit_array.fuzzer.hpp:149
Definition: fuzzer.hpp:27
Definition: bit_array.fuzzer.hpp:154