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