/src/ada-url/fuzz/simple_absolute.cc
Line | Count | Source |
1 | | #include <fuzzer/FuzzedDataProvider.h> |
2 | | |
3 | | #include <cstdio> |
4 | | #include <string> |
5 | | #include <string_view> |
6 | | |
7 | | #include "ada.cpp" |
8 | | #include "ada.h" |
9 | | |
10 | | static constexpr const char* kSchemes[] = { |
11 | | "http://", "https://", "HTTP://", "Https://", |
12 | | "http:", "https:", "htTP://", "HTTPS://"}; |
13 | | |
14 | | static constexpr const char* kHosts[] = { |
15 | | "example.com", |
16 | | "www.example.com", |
17 | | "WWW.Example.COM", |
18 | | "a", |
19 | | "a.b.c", |
20 | | "maps.google.com", |
21 | | "192.168.0.1", |
22 | | "0x7f.1", |
23 | | "127.1", |
24 | | "0", |
25 | | "3232235777", |
26 | | "xn--nxasmq6b.com", |
27 | | "xn--a", |
28 | | "XN--A", |
29 | | "user@example.com", |
30 | | "user:pass@example.com", |
31 | | "example.com:8080", |
32 | | "example.com:0", |
33 | | "example.com:65535", |
34 | | "", |
35 | | }; |
36 | | |
37 | | static constexpr const char* kPaths[] = { |
38 | | "", |
39 | | "/", |
40 | | "/path", |
41 | | "/path/file.js", |
42 | | "/a/./b/../c", |
43 | | "/foo/%2e", |
44 | | "/foo/%2e%2e", |
45 | | "/foo/%2E%2E/bar", |
46 | | "/path with space", |
47 | | "/path\twith\ttab", |
48 | | "\\path", |
49 | | "/continue=https%3A%2F%2Fexample.com%2F", |
50 | | "/imghp", |
51 | | "/./", |
52 | | "/../", |
53 | | "/%2e%2e%2f", |
54 | | }; |
55 | | |
56 | | static constexpr const char* kQueries[] = { |
57 | | "", |
58 | | "?", |
59 | | "?q=1", |
60 | | "?hl=en&tab=wi", |
61 | | "?continue=https%3A%2F%2Fexample.com%2F", |
62 | | "?a=1&b=2&c=3", |
63 | | }; |
64 | | |
65 | | static constexpr const char* kFrags[] = { |
66 | | "", "#", "#frag", "#x%20y", "#/", |
67 | | }; |
68 | | |
69 | | template <size_t N> |
70 | 34.3k | static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) { |
71 | 34.3k | return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)]; |
72 | 34.3k | } simple_absolute.cc:char const* pick<8ul>(FuzzedDataProvider&, char const* const (&) [8ul]) Line | Count | Source | 70 | 6.86k | static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) { | 71 | 6.86k | return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)]; | 72 | 6.86k | } |
simple_absolute.cc:char const* pick<20ul>(FuzzedDataProvider&, char const* const (&) [20ul]) Line | Count | Source | 70 | 6.86k | static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) { | 71 | 6.86k | return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)]; | 72 | 6.86k | } |
simple_absolute.cc:char const* pick<16ul>(FuzzedDataProvider&, char const* const (&) [16ul]) Line | Count | Source | 70 | 6.86k | static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) { | 71 | 6.86k | return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)]; | 72 | 6.86k | } |
simple_absolute.cc:char const* pick<6ul>(FuzzedDataProvider&, char const* const (&) [6ul]) Line | Count | Source | 70 | 6.86k | static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) { | 71 | 6.86k | return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)]; | 72 | 6.86k | } |
simple_absolute.cc:char const* pick<5ul>(FuzzedDataProvider&, char const* const (&) [5ul]) Line | Count | Source | 70 | 6.86k | static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) { | 71 | 6.86k | return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)]; | 72 | 6.86k | } |
|
73 | | |
74 | 6.86k | static std::string make_candidate(FuzzedDataProvider& fdp) { |
75 | 6.86k | std::string out; |
76 | 6.86k | out += pick(fdp, kSchemes); |
77 | 6.86k | out += pick(fdp, kHosts); |
78 | 6.86k | out += pick(fdp, kPaths); |
79 | 6.86k | out += pick(fdp, kQueries); |
80 | 6.86k | out += pick(fdp, kFrags); |
81 | | |
82 | 6.86k | if (fdp.ConsumeBool() && !out.empty()) { |
83 | 1.82k | std::string mid = fdp.ConsumeRandomLengthString(24); |
84 | 1.82k | size_t pos = fdp.ConsumeIntegralInRange<size_t>(0, out.size()); |
85 | 1.82k | out.insert(pos, mid); |
86 | 1.82k | } |
87 | 6.86k | if (fdp.ConsumeBool() && !out.empty()) { |
88 | 1.46k | size_t i = fdp.ConsumeIntegralInRange<size_t>(0, out.size() - 1); |
89 | 1.46k | out[i] = static_cast<char>(fdp.ConsumeIntegral<uint8_t>()); |
90 | 1.46k | } |
91 | 6.86k | return out; |
92 | 6.86k | } |
93 | | |
94 | 236k | static void check_href_size(const ada::url& u, std::string_view input) { |
95 | 236k | if (u.get_href_size() != u.get_href().size()) { |
96 | 0 | printf( |
97 | 0 | "get_href_size mismatch (url)\n" |
98 | 0 | " input: %.*s\n" |
99 | 0 | " size: %zu href.size: %zu\n" |
100 | 0 | " href: %s\n", |
101 | 0 | static_cast<int>(input.size()), input.data(), u.get_href_size(), |
102 | 0 | u.get_href().size(), u.get_href().c_str()); |
103 | 0 | abort(); |
104 | 0 | } |
105 | 236k | } |
106 | | |
107 | | static void check_href_size(const ada::url_aggregator& u, |
108 | 236k | std::string_view input) { |
109 | 236k | if (u.get_href_size() != u.get_href().size()) { |
110 | 0 | printf( |
111 | 0 | "get_href_size mismatch (aggregator)\n" |
112 | 0 | " input: %.*s\n" |
113 | 0 | " size: %zu href.size: %zu\n" |
114 | 0 | " href: %s\n", |
115 | 0 | static_cast<int>(input.size()), input.data(), u.get_href_size(), |
116 | 0 | u.get_href().size(), std::string(u.get_href()).c_str()); |
117 | 0 | abort(); |
118 | 0 | } |
119 | 236k | } |
120 | | |
121 | | template <class Result> |
122 | | static void check_reparse_idempotent(const Result& parsed, |
123 | 236k | std::string_view input) { |
124 | 236k | const std::string href = std::string(parsed.get_href()); |
125 | 236k | auto again = ada::parse<Result>(href); |
126 | 236k | if (!again) { |
127 | 0 | printf( |
128 | 0 | "re-parse of href failed\n" |
129 | 0 | " input: %.*s\n" |
130 | 0 | " href: %s\n", |
131 | 0 | static_cast<int>(input.size()), input.data(), href.c_str()); |
132 | 0 | abort(); |
133 | 0 | } |
134 | 236k | if (std::string(again->get_href()) != href) { |
135 | 0 | printf( |
136 | 0 | "href not idempotent\n" |
137 | 0 | " input: %.*s\n" |
138 | 0 | " href1: %s\n" |
139 | 0 | " href2: %s\n", |
140 | 0 | static_cast<int>(input.size()), input.data(), href.c_str(), |
141 | 0 | std::string(again->get_href()).c_str()); |
142 | 0 | abort(); |
143 | 0 | } |
144 | 236k | } simple_absolute.cc:void check_reparse_idempotent<ada::url>(ada::url const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 123 | 118k | std::string_view input) { | 124 | 118k | const std::string href = std::string(parsed.get_href()); | 125 | 118k | auto again = ada::parse<Result>(href); | 126 | 118k | if (!again) { | 127 | 0 | printf( | 128 | 0 | "re-parse of href failed\n" | 129 | 0 | " input: %.*s\n" | 130 | 0 | " href: %s\n", | 131 | 0 | static_cast<int>(input.size()), input.data(), href.c_str()); | 132 | 0 | abort(); | 133 | 0 | } | 134 | 118k | if (std::string(again->get_href()) != href) { | 135 | 0 | printf( | 136 | 0 | "href not idempotent\n" | 137 | 0 | " input: %.*s\n" | 138 | 0 | " href1: %s\n" | 139 | 0 | " href2: %s\n", | 140 | 0 | static_cast<int>(input.size()), input.data(), href.c_str(), | 141 | 0 | std::string(again->get_href()).c_str()); | 142 | 0 | abort(); | 143 | 0 | } | 144 | 118k | } |
simple_absolute.cc:void check_reparse_idempotent<ada::url_aggregator>(ada::url_aggregator const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 123 | 118k | std::string_view input) { | 124 | 118k | const std::string href = std::string(parsed.get_href()); | 125 | 118k | auto again = ada::parse<Result>(href); | 126 | 118k | if (!again) { | 127 | 0 | printf( | 128 | 0 | "re-parse of href failed\n" | 129 | 0 | " input: %.*s\n" | 130 | 0 | " href: %s\n", | 131 | 0 | static_cast<int>(input.size()), input.data(), href.c_str()); | 132 | 0 | abort(); | 133 | 0 | } | 134 | 118k | if (std::string(again->get_href()) != href) { | 135 | 0 | printf( | 136 | 0 | "href not idempotent\n" | 137 | 0 | " input: %.*s\n" | 138 | 0 | " href1: %s\n" | 139 | 0 | " href2: %s\n", | 140 | 0 | static_cast<int>(input.size()), input.data(), href.c_str(), | 141 | 0 | std::string(again->get_href()).c_str()); | 142 | 0 | abort(); | 143 | 0 | } | 144 | 118k | } |
|
145 | | |
146 | | static void check_components_agree(const ada::url& u, |
147 | | const ada::url_aggregator& a, |
148 | 118k | std::string_view input) { |
149 | 118k | if (u.get_protocol() != a.get_protocol() || |
150 | 118k | u.get_href() != std::string(a.get_href()) || |
151 | 118k | std::string(u.get_hostname()) != std::string(a.get_hostname()) || |
152 | 118k | std::string(u.get_pathname()) != std::string(a.get_pathname()) || |
153 | 118k | std::string(u.get_search()) != std::string(a.get_search()) || |
154 | 118k | std::string(u.get_hash()) != std::string(a.get_hash()) || |
155 | 118k | std::string(u.get_port()) != std::string(a.get_port()) || |
156 | 118k | u.get_username() != std::string(a.get_username()) || |
157 | 118k | u.get_password() != std::string(a.get_password()) || |
158 | 118k | std::string(u.get_host()) != std::string(a.get_host())) { |
159 | 0 | printf( |
160 | 0 | "url vs aggregator component mismatch\n" |
161 | 0 | " input: %.*s\n" |
162 | 0 | " url href: %s\n" |
163 | 0 | " agg href: %s\n", |
164 | 0 | static_cast<int>(input.size()), input.data(), u.get_href().c_str(), |
165 | 0 | std::string(a.get_href()).c_str()); |
166 | 0 | abort(); |
167 | 0 | } |
168 | 118k | } |
169 | | |
170 | 121k | static void fuzz_one_input(std::string_view input) { |
171 | 121k | auto url = ada::parse<ada::url>(input); |
172 | 121k | auto agg = ada::parse<ada::url_aggregator>(input); |
173 | | |
174 | 121k | if (url.has_value() != agg.has_value()) { |
175 | 0 | printf( |
176 | 0 | "parse agreement failure\n" |
177 | 0 | " input: %.*s\n" |
178 | 0 | " url: %d aggregator: %d\n", |
179 | 0 | static_cast<int>(input.size()), input.data(), url.has_value(), |
180 | 0 | agg.has_value()); |
181 | 0 | abort(); |
182 | 0 | } |
183 | | |
184 | 121k | if (!url) { |
185 | 2.88k | return; |
186 | 2.88k | } |
187 | | |
188 | 118k | check_components_agree(*url, *agg, input); |
189 | 118k | check_href_size(*url, input); |
190 | 118k | check_href_size(*agg, input); |
191 | 118k | check_reparse_idempotent(*url, input); |
192 | 118k | check_reparse_idempotent(*agg, input); |
193 | | |
194 | 118k | const std::string href = url->get_href(); |
195 | 118k | url->set_href(href); |
196 | 118k | agg->set_href(href); |
197 | 118k | if (url->get_href() != std::string(agg->get_href())) { |
198 | 0 | printf( |
199 | 0 | "set_href agreement failure\n" |
200 | 0 | " href: %s\n", |
201 | 0 | href.c_str()); |
202 | 0 | abort(); |
203 | 0 | } |
204 | 118k | check_href_size(*url, href); |
205 | 118k | check_href_size(*agg, href); |
206 | | |
207 | 118k | volatile bool valid = agg->validate(); |
208 | 118k | (void)valid; |
209 | 118k | } |
210 | | |
211 | 6.86k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
212 | 6.86k | FuzzedDataProvider fdp(data, size); |
213 | | |
214 | 6.86k | fuzz_one_input(make_candidate(fdp)); |
215 | | |
216 | 6.86k | if (fdp.remaining_bytes() > 0) { |
217 | 4.50k | std::string raw = fdp.ConsumeRemainingBytesAsString(); |
218 | 4.50k | if (raw.size() > 512) { |
219 | 28 | raw.resize(512); |
220 | 28 | } |
221 | 4.50k | if (!raw.empty() && (static_cast<unsigned char>(raw[0]) & 1u)) { |
222 | 1.63k | raw = std::string("https://") + raw; |
223 | 2.87k | } else if (!raw.empty()) { |
224 | 2.87k | raw = std::string("http://") + raw; |
225 | 2.87k | } |
226 | 4.50k | fuzz_one_input(raw); |
227 | 4.50k | } |
228 | | |
229 | 6.86k | static constexpr const char* kAnchors[] = { |
230 | 6.86k | "https://example.com", |
231 | 6.86k | "https://example.com/", |
232 | 6.86k | "https://example.com?q=1", |
233 | 6.86k | "https://example.com#frag", |
234 | 6.86k | "https://example.com/?q=1#frag", |
235 | 6.86k | "http://www.example.com/path/file.js", |
236 | 6.86k | "http://WWW.Example.COM/file.js", |
237 | 6.86k | "https://www.google.com/imghp?hl=en&tab=wi", |
238 | 6.86k | "http://192.168.0.1/x", |
239 | 6.86k | "http://0x7f.1/", |
240 | 6.86k | "https://user:pass@example.com/x", |
241 | 6.86k | "https://example.com:8080/x", |
242 | 6.86k | "https://example.com/a/./b/../c", |
243 | 6.86k | "https://example.com/foo/%2e%2e", |
244 | 6.86k | "https://xn--nxasmq6b.com/", |
245 | 6.86k | "https://xn--a/", |
246 | 6.86k | }; |
247 | 109k | for (const char* seed : kAnchors) { |
248 | 109k | fuzz_one_input(seed); |
249 | 109k | } |
250 | | |
251 | 6.86k | return 0; |
252 | 6.86k | } |