mutators.cc (3758B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "mutators.h" 6 7 #include <cassert> 8 #include <cstring> 9 #include <random> 10 #include <tuple> 11 12 static std::tuple<uint8_t *, size_t> ParseItem(uint8_t *data, 13 size_t maxLength) { 14 // Short form. Bit 8 has value "0" and bits 7-1 give the length. 15 if ((data[1] & 0x80) == 0) { 16 size_t length = std::min(static_cast<size_t>(data[1]), maxLength - 2); 17 return std::make_tuple(&data[2], length); 18 } 19 20 // Constructed, indefinite length. Read until {0x00, 0x00}. 21 if (data[1] == 0x80) { 22 void *offset = memmem(&data[2], maxLength - 2, "\0", 2); 23 size_t length = offset ? (static_cast<uint8_t *>(offset) - &data[2]) + 2 24 : maxLength - 2; 25 return std::make_tuple(&data[2], length); 26 } 27 28 // Long form. Two to 127 octets. Bit 8 of first octet has value "1" 29 // and bits 7-1 give the number of additional length octets. 30 size_t octets = std::min(static_cast<size_t>(data[1] & 0x7f), maxLength - 2); 31 32 // Handle lengths bigger than 32 bits. 33 if (octets > 4) { 34 // Ignore any further children, assign remaining length. 35 return std::make_tuple(&data[2] + octets, maxLength - 2 - octets); 36 } 37 38 // Parse the length. 39 size_t length = 0; 40 for (size_t j = 0; j < octets; j++) { 41 length = (length << 8) | data[2 + j]; 42 } 43 44 length = std::min(length, maxLength - 2 - octets); 45 return std::make_tuple(&data[2] + octets, length); 46 } 47 48 static std::vector<uint8_t *> ParseItems(uint8_t *data, size_t size) { 49 std::vector<uint8_t *> items; 50 std::vector<size_t> lengths; 51 52 // The first item is always the whole corpus. 53 items.push_back(data); 54 lengths.push_back(size); 55 56 // Can't use iterators here because the `items` vector is modified inside the 57 // loop. That's safe as long as we always check `items.size()` before every 58 // iteration, and only call `.push_back()` to append new items we found. 59 // Items are accessed through `items.at()`, we hold no references. 60 for (size_t i = 0; i < items.size(); i++) { 61 uint8_t *item = items.at(i); 62 size_t remaining = lengths.at(i); 63 64 // Empty or primitive items have no children. 65 if (remaining == 0 || (0x20 & item[0]) == 0) { 66 continue; 67 } 68 69 while (remaining > 2) { 70 uint8_t *content; 71 size_t length; 72 73 std::tie(content, length) = ParseItem(item, remaining); 74 75 if (length > 0) { 76 // Record the item. 77 items.push_back(content); 78 79 // Record the length for further parsing. 80 lengths.push_back(length); 81 } 82 83 // Reduce number of bytes left in current item. 84 remaining -= length + (content - item); 85 86 // Skip the item we just parsed. 87 item = content + length; 88 } 89 } 90 91 return items; 92 } 93 94 namespace ASN1Mutators { 95 96 size_t FlipConstructed(uint8_t *data, size_t size, size_t maxSize, 97 unsigned int seed) { 98 auto items = ParseItems(data, size); 99 100 std::mt19937 rng(seed); 101 std::uniform_int_distribution<size_t> dist(0, items.size() - 1); 102 uint8_t *item = items.at(dist(rng)); 103 104 // Flip "constructed" type bit. 105 item[0] ^= 0x20; 106 107 return size; 108 } 109 110 size_t ChangeType(uint8_t *data, size_t size, size_t maxSize, 111 unsigned int seed) { 112 auto items = ParseItems(data, size); 113 114 std::mt19937 rng(seed); 115 std::uniform_int_distribution<size_t> dist(0, items.size() - 1); 116 uint8_t *item = items.at(dist(rng)); 117 118 // Change type to a random int [0, 30]. 119 static std::uniform_int_distribution<size_t> tdist(0, 30); 120 item[0] = tdist(rng); 121 122 return size; 123 } 124 125 } // namespace ASN1Mutators