mutators.cc (8010B)
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 <algorithm> 8 #include <cassert> 9 #include <cstddef> 10 #include <cstdint> 11 #include <random> 12 #include <vector> 13 14 #include "tls_parser.h" 15 16 // Helper class to simplify TLS record manipulation. 17 class Record { 18 public: 19 static std::unique_ptr<Record> Create(const uint8_t *data, size_t size, 20 size_t remaining) { 21 return std::unique_ptr<Record>(new Record(data, size, remaining)); 22 } 23 24 void insert_before(const std::unique_ptr<Record> &other) { 25 assert(data_ && size_ > 0); 26 27 // Copy data in case other == this. 28 std::vector<uint8_t> buf(size_); 29 memcpy(buf.data(), data_, size_); 30 31 uint8_t *dest = const_cast<uint8_t *>(other->data()); 32 // Make room for the record we want to insert. 33 memmove(dest + size_, other->data(), other->size() + other->remaining()); 34 // Insert the record. 35 memcpy(dest, buf.data(), size_); 36 } 37 38 void truncate(size_t length) { 39 assert(length >= 5 + EXTRA_HEADER_BYTES); 40 uint8_t *dest = const_cast<uint8_t *>(data_); 41 size_t l = length - (5 + EXTRA_HEADER_BYTES); 42 dest[3] = (l >> 8) & 0xff; 43 dest[4] = l & 0xff; 44 memmove(dest + length, data_ + size_, remaining_); 45 } 46 47 void drop() { 48 uint8_t *dest = const_cast<uint8_t *>(data_); 49 memmove(dest, data_ + size_, remaining_); 50 } 51 52 const uint8_t *data() { return data_; } 53 size_t remaining() { return remaining_; } 54 size_t size() { return size_; } 55 56 private: 57 Record(const uint8_t *data, size_t size, size_t remaining) 58 : data_(data), remaining_(remaining), size_(size) {} 59 60 const uint8_t *data_; 61 size_t remaining_; 62 size_t size_; 63 }; 64 65 // Parse records contained in a given TLS transcript. 66 std::vector<std::unique_ptr<Record>> ParseRecords(const uint8_t *data, 67 size_t size) { 68 std::vector<std::unique_ptr<Record>> records; 69 nss_test::TlsParser parser(data, size); 70 71 while (parser.remaining()) { 72 size_t offset = parser.consumed(); 73 74 // Skip type, version, and DTLS seqnums. 75 if (!parser.Skip(3 + EXTRA_HEADER_BYTES)) { 76 break; 77 } 78 79 nss_test::DataBuffer fragment; 80 if (!parser.ReadVariable(&fragment, 2)) { 81 break; 82 } 83 84 records.push_back(Record::Create(data + offset, 85 fragment.len() + 5 + EXTRA_HEADER_BYTES, 86 parser.remaining())); 87 } 88 89 return records; 90 } 91 92 namespace TlsMutators { 93 94 // Mutator that drops whole TLS records. 95 size_t DropRecord(uint8_t *data, size_t size, size_t maxSize, 96 unsigned int seed) { 97 std::mt19937 rng(seed); 98 99 // Find TLS records in the corpus. 100 auto records = ParseRecords(data, size); 101 if (records.empty()) { 102 return 0; 103 } 104 105 // Pick a record to drop at random. 106 std::uniform_int_distribution<size_t> dist(0, records.size() - 1); 107 auto &rec = records.at(dist(rng)); 108 109 // Drop the record. 110 rec->drop(); 111 112 // Return the new final size. 113 return size - rec->size(); 114 } 115 116 // Mutator that shuffles TLS records in a transcript. 117 size_t ShuffleRecords(uint8_t *data, size_t size, size_t maxSize, 118 unsigned int seed) { 119 std::mt19937 rng(seed); 120 121 // Find TLS records in the corpus. 122 auto records = ParseRecords(data, size); 123 if (records.empty()) { 124 return 0; 125 } 126 127 // Store the original corpus. 128 std::vector<uint8_t> buf(size); 129 memcpy(buf.data(), data, size); 130 131 // Find offset of first record in target buffer. 132 uint8_t *dest = const_cast<uint8_t *>(records.at(0)->data()); 133 134 // Shuffle record order. 135 std::shuffle(records.begin(), records.end(), rng); 136 137 // Write records to their new positions. 138 for (auto &rec : records) { 139 memcpy(dest, buf.data() + (rec->data() - data), rec->size()); 140 dest += rec->size(); 141 } 142 143 // Final size hasn't changed. 144 return size; 145 } 146 147 // Mutator that duplicates a single TLS record and randomly inserts it. 148 size_t DuplicateRecord(uint8_t *data, size_t size, size_t maxSize, 149 unsigned int seed) { 150 std::mt19937 rng(seed); 151 152 // Find TLS records in the corpus. 153 const auto records = ParseRecords(data, size); 154 if (records.empty()) { 155 return 0; 156 } 157 158 // Pick a record to duplicate at random. 159 std::uniform_int_distribution<size_t> dist(0, records.size() - 1); 160 auto &rec = records.at(dist(rng)); 161 if (size + rec->size() > maxSize) { 162 return 0; 163 } 164 165 // Insert before random record. 166 rec->insert_before(records.at(dist(rng))); 167 168 // Return the new final size. 169 return size + rec->size(); 170 } 171 172 // Mutator that truncates a TLS record. 173 size_t TruncateRecord(uint8_t *data, size_t size, size_t maxSize, 174 unsigned int seed) { 175 std::mt19937 rng(seed); 176 177 // Find TLS records in the corpus. 178 const auto records = ParseRecords(data, size); 179 if (records.empty()) { 180 return 0; 181 } 182 183 // Pick a record to truncate at random. 184 std::uniform_int_distribution<size_t> dist(0, records.size() - 1); 185 auto &rec = records.at(dist(rng)); 186 187 // Need a record with data. 188 if (rec->size() <= 5 + EXTRA_HEADER_BYTES) { 189 return 0; 190 } 191 192 // Truncate. 193 std::uniform_int_distribution<size_t> dist2(5 + EXTRA_HEADER_BYTES, 194 rec->size() - 1); 195 size_t new_length = dist2(rng); 196 rec->truncate(new_length); 197 198 // Return the new final size. 199 return size + new_length - rec->size(); 200 } 201 202 // Mutator that splits a TLS record in two. 203 size_t FragmentRecord(uint8_t *data, size_t size, size_t maxSize, 204 unsigned int seed) { 205 std::mt19937 rng(seed); 206 207 // We can't deal with DTLS yet. 208 if (EXTRA_HEADER_BYTES > 0) { 209 return 0; 210 } 211 212 if (size + 5 > maxSize) { 213 return 0; 214 } 215 216 // Find TLS records in the corpus. 217 const auto records = ParseRecords(data, size); 218 if (records.empty()) { 219 return 0; 220 } 221 222 // Pick a record to fragment at random. 223 std::uniform_int_distribution<size_t> rand_record(0, records.size() - 1); 224 auto &rec = records.at(rand_record(rng)); 225 uint8_t *rdata = const_cast<uint8_t *>(rec->data()); 226 size_t length = rec->size(); 227 size_t content_length = length - 5; 228 229 if (content_length < 2) { 230 return 0; 231 } 232 233 // Assign a new length to the first fragment. 234 std::uniform_int_distribution<size_t> rand_size(1, content_length - 1); 235 size_t first_length = rand_size(rng); 236 size_t second_length = content_length - first_length; 237 rdata[3] = (first_length >> 8) & 0xff; 238 rdata[4] = first_length & 0xff; 239 uint8_t *second_record = rdata + 5 + first_length; 240 241 // Make room for the header of the second record. 242 memmove(second_record + 5, second_record, 243 rec->remaining() + content_length - first_length); 244 245 // Write second header. 246 memcpy(second_record, rdata, 3); 247 second_record[3] = (second_length >> 8) & 0xff; 248 second_record[4] = second_length & 0xff; 249 250 return size + 5; 251 } 252 253 // Cross-over function that merges and shuffles two transcripts. 254 size_t CrossOver(const uint8_t *data1, size_t size1, const uint8_t *data2, 255 size_t size2, uint8_t *out, size_t maxOutSize, 256 unsigned int seed) { 257 std::mt19937 rng(seed); 258 259 // Find TLS records in the corpus. 260 auto records1 = ParseRecords(data1, size1); 261 if (records1.empty()) { 262 return 0; 263 } 264 265 { // Merge the two vectors. 266 auto records2 = ParseRecords(data2, size2); 267 if (records2.empty()) { 268 return 0; 269 } 270 std::move(records2.begin(), records2.end(), std::back_inserter(records1)); 271 } 272 273 // Shuffle record order. 274 std::shuffle(records1.begin(), records1.end(), rng); 275 276 size_t total = 0; 277 for (auto &rec : records1) { 278 size_t length = rec->size(); 279 if (total + length > maxOutSize) { 280 break; 281 } 282 283 // Write record to its new position. 284 memcpy(out + total, rec->data(), length); 285 total += length; 286 } 287 288 return total; 289 } 290 291 } // namespace TlsMutators