| 24 | /** Perform a simulation fuzz test on BitSet type S. */ |
| 25 | template<typename S> |
| 26 | void TestType(FuzzBufferType buffer) |
| 27 | { |
| 28 | /** This fuzz test's design is based on the assumption that the actual bits stored in the |
| 29 | * bitsets and their simulations do not matter for the purpose of detecting edge cases, thus |
| 30 | * these are taken from a deterministically-seeded RNG instead. To provide some level of |
| 31 | * variation however, pick the seed based on the buffer size and size of the chosen bitset. */ |
| 32 | InsecureRandomContext rng(buffer.size() + 0x10000 * S::Size()); |
| 33 | |
| 34 | using Sim = std::bitset<S::Size()>; |
| 35 | // Up to 4 real BitSets (initially 2). |
| 36 | std::vector<S> real(2); |
| 37 | // Up to 4 std::bitsets with the same corresponding contents. |
| 38 | std::vector<Sim> sim(2); |
| 39 | |
| 40 | /* Compare sim[idx] with real[idx], using all inspector operations. */ |
| 41 | auto compare_fn = [&](unsigned idx) { |
| 42 | /* iterators and operator[] */ |
| 43 | auto it = real[idx].begin(); |
| 44 | unsigned first = S::Size(); |
| 45 | unsigned last = S::Size(); |
| 46 | for (unsigned i = 0; i < S::Size(); ++i) { |
| 47 | bool match = (it != real[idx].end()) && *it == i; |
| 48 | assert(sim[idx][i] == real[idx][i]); |
| 49 | assert(match == real[idx][i]); |
| 50 | assert((it == real[idx].end()) != (it != real[idx].end())); |
| 51 | if (match) { |
| 52 | ++it; |
| 53 | if (first == S::Size()) first = i; |
| 54 | last = i; |
| 55 | } |
| 56 | } |
| 57 | assert(it == real[idx].end()); |
| 58 | assert(!(it != real[idx].end())); |
| 59 | /* Any / None */ |
| 60 | assert(sim[idx].any() == real[idx].Any()); |
| 61 | assert(sim[idx].none() == real[idx].None()); |
| 62 | /* First / Last */ |
| 63 | if (sim[idx].any()) { |
| 64 | assert(first == real[idx].First()); |
| 65 | assert(last == real[idx].Last()); |
| 66 | } |
| 67 | /* Count */ |
| 68 | assert(sim[idx].count() == real[idx].Count()); |
| 69 | }; |
| 70 | |
| 71 | LIMITED_WHILE(buffer.size() > 0, 1000) { |
| 72 | // Read one byte to determine which operation to execute on the BitSets. |
| 73 | int command = ReadByte(buffer) % 64; |
| 74 | // Read another byte that determines which bitsets will be involved. |
| 75 | unsigned args = ReadByte(buffer); |
| 76 | unsigned dest = ((args & 7) * sim.size()) >> 3; |
| 77 | unsigned src = (((args >> 3) & 7) * sim.size()) >> 3; |
| 78 | unsigned aux = (((args >> 6) & 3) * sim.size()) >> 2; |
| 79 | // Args are in range for non-empty sim, or sim is completely empty and will be grown |
| 80 | assert((sim.empty() && dest == 0 && src == 0 && aux == 0) || |
| 81 | (!sim.empty() && dest < sim.size() && src < sim.size() && aux < sim.size())); |
| 82 | |
| 83 | // Pick one operation based on value of command. Not all operations are always applicable. |