/src/nss/fuzz/targets/lib/asn1/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 <cassert> |
8 | | #include <cstring> |
9 | | #include <random> |
10 | | #include <tuple> |
11 | | |
12 | | static std::tuple<uint8_t *, size_t> ParseItem(uint8_t *data, |
13 | 0 | size_t maxLength) { |
14 | | // Short form. Bit 8 has value "0" and bits 7-1 give the length. |
15 | 0 | if ((data[1] & 0x80) == 0) { |
16 | 0 | size_t length = std::min(static_cast<size_t>(data[1]), maxLength - 2); |
17 | 0 | return std::make_tuple(&data[2], length); |
18 | 0 | } |
19 | | |
20 | | // Constructed, indefinite length. Read until {0x00, 0x00}. |
21 | 0 | if (data[1] == 0x80) { |
22 | 0 | void *offset = memmem(&data[2], maxLength - 2, "\0", 2); |
23 | 0 | size_t length = offset ? (static_cast<uint8_t *>(offset) - &data[2]) + 2 |
24 | 0 | : maxLength - 2; |
25 | 0 | return std::make_tuple(&data[2], length); |
26 | 0 | } |
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 | 0 | size_t octets = std::min(static_cast<size_t>(data[1] & 0x7f), maxLength - 2); |
31 | | |
32 | | // Handle lengths bigger than 32 bits. |
33 | 0 | if (octets > 4) { |
34 | | // Ignore any further children, assign remaining length. |
35 | 0 | return std::make_tuple(&data[2] + octets, maxLength - 2 - octets); |
36 | 0 | } |
37 | | |
38 | | // Parse the length. |
39 | 0 | size_t length = 0; |
40 | 0 | for (size_t j = 0; j < octets; j++) { |
41 | 0 | length = (length << 8) | data[2 + j]; |
42 | 0 | } |
43 | |
|
44 | 0 | length = std::min(length, maxLength - 2 - octets); |
45 | 0 | return std::make_tuple(&data[2] + octets, length); |
46 | 0 | } |
47 | | |
48 | 0 | static std::vector<uint8_t *> ParseItems(uint8_t *data, size_t size) { |
49 | 0 | std::vector<uint8_t *> items; |
50 | 0 | std::vector<size_t> lengths; |
51 | | |
52 | | // The first item is always the whole corpus. |
53 | 0 | items.push_back(data); |
54 | 0 | 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 | 0 | for (size_t i = 0; i < items.size(); i++) { |
61 | 0 | uint8_t *item = items.at(i); |
62 | 0 | size_t remaining = lengths.at(i); |
63 | | |
64 | | // Empty or primitive items have no children. |
65 | 0 | if (remaining == 0 || (0x20 & item[0]) == 0) { |
66 | 0 | continue; |
67 | 0 | } |
68 | | |
69 | 0 | while (remaining > 2) { |
70 | 0 | uint8_t *content; |
71 | 0 | size_t length; |
72 | |
|
73 | 0 | std::tie(content, length) = ParseItem(item, remaining); |
74 | |
|
75 | 0 | if (length > 0) { |
76 | | // Record the item. |
77 | 0 | items.push_back(content); |
78 | | |
79 | | // Record the length for further parsing. |
80 | 0 | lengths.push_back(length); |
81 | 0 | } |
82 | | |
83 | | // Reduce number of bytes left in current item. |
84 | 0 | remaining -= length + (content - item); |
85 | | |
86 | | // Skip the item we just parsed. |
87 | 0 | item = content + length; |
88 | 0 | } |
89 | 0 | } |
90 | |
|
91 | 0 | return items; |
92 | 0 | } |
93 | | |
94 | | namespace ASN1Mutators { |
95 | | |
96 | | size_t FlipConstructed(uint8_t *data, size_t size, size_t maxSize, |
97 | 0 | unsigned int seed) { |
98 | 0 | auto items = ParseItems(data, size); |
99 | |
|
100 | 0 | std::mt19937 rng(seed); |
101 | 0 | std::uniform_int_distribution<size_t> dist(0, items.size() - 1); |
102 | 0 | uint8_t *item = items.at(dist(rng)); |
103 | | |
104 | | // Flip "constructed" type bit. |
105 | 0 | item[0] ^= 0x20; |
106 | |
|
107 | 0 | return size; |
108 | 0 | } |
109 | | |
110 | | size_t ChangeType(uint8_t *data, size_t size, size_t maxSize, |
111 | 0 | unsigned int seed) { |
112 | 0 | auto items = ParseItems(data, size); |
113 | |
|
114 | 0 | std::mt19937 rng(seed); |
115 | 0 | std::uniform_int_distribution<size_t> dist(0, items.size() - 1); |
116 | 0 | uint8_t *item = items.at(dist(rng)); |
117 | | |
118 | | // Change type to a random int [0, 30]. |
119 | 0 | static std::uniform_int_distribution<size_t> tdist(0, 30); |
120 | 0 | item[0] = tdist(rng); |
121 | |
|
122 | 0 | return size; |
123 | 0 | } |
124 | | |
125 | | } // namespace ASN1Mutators |