/src/ada-url/fuzz/ada_c.cc
Line | Count | Source |
1 | | // C API fuzzer (C++ amalgamation; do not include ada_c.h — types come from |
2 | | // ada_c.cpp in the amalgamation). |
3 | | |
4 | | #include <fuzzer/FuzzedDataProvider.h> |
5 | | |
6 | | #include <cstdint> |
7 | | #include <cstdio> |
8 | | #include <cstring> |
9 | | #include <string> |
10 | | |
11 | | #include "ada.cpp" |
12 | | #include "ada.h" |
13 | | |
14 | 9.61k | static std::string make_url_candidate(FuzzedDataProvider& fdp) { |
15 | 9.61k | static constexpr const char* kSchemes[] = { |
16 | 9.61k | "http://", "https://", "ws://", "wss://", "ftp://", |
17 | 9.61k | "file://", "HTTP://", "https:", "http:", |
18 | 9.61k | }; |
19 | 9.61k | static constexpr const char* kHosts[] = { |
20 | 9.61k | "example.com", |
21 | 9.61k | "www.example.com", |
22 | 9.61k | "localhost", |
23 | 9.61k | "127.0.0.1", |
24 | 9.61k | "192.168.0.1", |
25 | 9.61k | "0x7f.1", |
26 | 9.61k | "[::1]", |
27 | 9.61k | "xn--nxasmq6b.com", |
28 | 9.61k | "user:pass@example.com", |
29 | 9.61k | "example.com:8080", |
30 | 9.61k | "a", |
31 | 9.61k | }; |
32 | 9.61k | static constexpr const char* kRest[] = { |
33 | 9.61k | "", "/", "/path", "/path?q=1", |
34 | 9.61k | "/path#frag", "/a/./b/../c", "/foo/%2e%2e", "?q=1", |
35 | 9.61k | "#f", |
36 | 9.61k | }; |
37 | 9.61k | std::string out; |
38 | 9.61k | out += kSchemes[fdp.ConsumeIntegralInRange<size_t>( |
39 | 9.61k | 0, sizeof(kSchemes) / sizeof(kSchemes[0]) - 1)]; |
40 | 9.61k | out += kHosts[fdp.ConsumeIntegralInRange<size_t>( |
41 | 9.61k | 0, sizeof(kHosts) / sizeof(kHosts[0]) - 1)]; |
42 | 9.61k | out += kRest[fdp.ConsumeIntegralInRange<size_t>( |
43 | 9.61k | 0, sizeof(kRest) / sizeof(kRest[0]) - 1)]; |
44 | 9.61k | if (fdp.ConsumeBool() && !out.empty()) { |
45 | 3.84k | std::string mid = fdp.ConsumeRandomLengthString(24); |
46 | 3.84k | size_t pos = fdp.ConsumeIntegralInRange<size_t>(0, out.size()); |
47 | 3.84k | out.insert(pos, mid); |
48 | 3.84k | } |
49 | 9.61k | return out; |
50 | 9.61k | } |
51 | | |
52 | | static void exercise_valid_url(ada_url out, const char* input, size_t input_len, |
53 | 67.7k | const char* other, size_t other_len) { |
54 | 67.7k | if (!ada_is_valid(out)) return; |
55 | | |
56 | 62.4k | ada_set_href(out, input, input_len); |
57 | 62.4k | ada_set_host(out, other, other_len); |
58 | 62.4k | ada_set_hostname(out, other, other_len); |
59 | 62.4k | ada_set_protocol(out, other, other_len); |
60 | 62.4k | ada_set_username(out, other, other_len); |
61 | 62.4k | ada_set_password(out, other, other_len); |
62 | 62.4k | ada_set_port(out, other, other_len); |
63 | 62.4k | ada_set_pathname(out, other, other_len); |
64 | 62.4k | ada_set_search(out, other, other_len); |
65 | 62.4k | ada_set_hash(out, other, other_len); |
66 | | |
67 | 62.4k | ada_get_hash(out); |
68 | 62.4k | ada_get_host(out); |
69 | 62.4k | uint8_t host_type = ada_get_host_type(out); |
70 | 62.4k | if (host_type > 2) { |
71 | 0 | printf("ada_get_host_type out of range: %u\n", (unsigned)host_type); |
72 | 0 | abort(); |
73 | 0 | } |
74 | 62.4k | ada_get_hostname(out); |
75 | 62.4k | ada_string href = ada_get_href(out); |
76 | 62.4k | ada_owned_string origin = ada_get_origin(out); |
77 | 62.4k | ada_get_pathname(out); |
78 | 62.4k | ada_get_username(out); |
79 | 62.4k | ada_get_password(out); |
80 | 62.4k | ada_get_protocol(out); |
81 | 62.4k | ada_get_port(out); |
82 | 62.4k | ada_get_search(out); |
83 | | |
84 | 62.4k | uint8_t scheme_type = ada_get_scheme_type(out); |
85 | 62.4k | if (scheme_type > 6) { |
86 | 0 | printf("ada_get_scheme_type out of range: %u\n", (unsigned)scheme_type); |
87 | 0 | abort(); |
88 | 0 | } |
89 | | |
90 | 62.4k | ada_has_credentials(out); |
91 | 62.4k | ada_has_empty_hostname(out); |
92 | 62.4k | ada_has_hostname(out); |
93 | 62.4k | ada_has_non_empty_username(out); |
94 | 62.4k | ada_has_non_empty_password(out); |
95 | 62.4k | ada_has_port(out); |
96 | 62.4k | ada_has_password(out); |
97 | 62.4k | ada_has_hash(out); |
98 | 62.4k | ada_has_search(out); |
99 | | |
100 | 62.4k | const ada_url_components* comps = ada_get_components(out); |
101 | 62.4k | constexpr uint32_t kOmitted = 0xffffffffu; |
102 | 437k | auto check_off = [&](uint32_t off, const char* name) { |
103 | 437k | if (off != kOmitted && off > href.length) { |
104 | 0 | printf("component %s out of bounds\n", name); |
105 | 0 | abort(); |
106 | 0 | } |
107 | 437k | }; |
108 | 62.4k | check_off(comps->protocol_end, "protocol_end"); |
109 | 62.4k | check_off(comps->username_end, "username_end"); |
110 | 62.4k | check_off(comps->host_start, "host_start"); |
111 | 62.4k | check_off(comps->host_end, "host_end"); |
112 | 62.4k | check_off(comps->pathname_start, "pathname_start"); |
113 | 62.4k | check_off(comps->search_start, "search_start"); |
114 | 62.4k | check_off(comps->hash_start, "hash_start"); |
115 | | |
116 | 62.4k | ada_clear_port(out); |
117 | 62.4k | ada_clear_hash(out); |
118 | 62.4k | ada_clear_search(out); |
119 | 62.4k | ada_free_owned_string(origin); |
120 | | |
121 | 62.4k | ada_url out_copy = ada_copy(out); |
122 | 62.4k | if (ada_is_valid(out_copy)) { |
123 | 62.4k | ada_get_href(out_copy); |
124 | 62.4k | } |
125 | 62.4k | ada_free(out_copy); |
126 | | |
127 | 62.4k | ada_string final_href = ada_get_href(out); |
128 | 62.4k | ada_url reparsed = ada_parse(final_href.data, final_href.length); |
129 | 62.4k | if (!ada_is_valid(reparsed)) { |
130 | 0 | printf("C API re-parse failed: %.*s\n", (int)final_href.length, |
131 | 0 | final_href.data); |
132 | 0 | ada_free(reparsed); |
133 | 0 | abort(); |
134 | 0 | } |
135 | 62.4k | ada_string reparsed_href = ada_get_href(reparsed); |
136 | 62.4k | if (reparsed_href.length != final_href.length || |
137 | 62.4k | memcmp(reparsed_href.data, final_href.data, final_href.length) != 0) { |
138 | 0 | printf("C API href idempotency failure\n"); |
139 | 0 | ada_free(reparsed); |
140 | 0 | abort(); |
141 | 0 | } |
142 | 62.4k | ada_free(reparsed); |
143 | 62.4k | } |
144 | | |
145 | 13.5k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
146 | 13.5k | FuzzedDataProvider fdp(data, size); |
147 | | |
148 | 13.5k | std::string primary = fdp.ConsumeBool() ? make_url_candidate(fdp) |
149 | 13.5k | : fdp.ConsumeRandomLengthString(256); |
150 | 13.5k | std::string secondary = fdp.ConsumeBool() |
151 | 13.5k | ? make_url_candidate(fdp) |
152 | 13.5k | : fdp.ConsumeRandomLengthString(256); |
153 | | |
154 | 13.5k | const char* input = primary.data(); |
155 | 13.5k | size_t input_len = primary.size(); |
156 | 13.5k | const char* base = secondary.data(); |
157 | 13.5k | size_t base_len = secondary.size(); |
158 | | |
159 | 13.5k | ada_url out = ada_parse(input, input_len); |
160 | 13.5k | bool is_valid = ada_is_valid(out); |
161 | 13.5k | bool can_parse_result = ada_can_parse(input, input_len); |
162 | 13.5k | if (can_parse_result != is_valid) { |
163 | 0 | printf("ada_can_parse vs ada_parse disagreement\n"); |
164 | 0 | ada_free(out); |
165 | 0 | abort(); |
166 | 0 | } |
167 | 13.5k | exercise_valid_url(out, input, input_len, base, base_len); |
168 | 13.5k | ada_free(out); |
169 | | |
170 | 13.5k | ada_url out_with_base = ada_parse_with_base(input, input_len, base, base_len); |
171 | 13.5k | bool with_base_valid = ada_is_valid(out_with_base); |
172 | 13.5k | bool can_parse_with_base = |
173 | 13.5k | ada_can_parse_with_base(input, input_len, base, base_len); |
174 | 13.5k | if (can_parse_with_base != with_base_valid) { |
175 | 0 | printf("ada_can_parse_with_base vs ada_parse_with_base disagreement\n"); |
176 | 0 | ada_free(out_with_base); |
177 | 0 | abort(); |
178 | 0 | } |
179 | 13.5k | if (with_base_valid) { |
180 | 3.75k | ada_get_href(out_with_base); |
181 | 3.75k | ada_owned_string origin = ada_get_origin(out_with_base); |
182 | 3.75k | ada_free_owned_string(origin); |
183 | 3.75k | ada_get_hostname(out_with_base); |
184 | 3.75k | ada_get_pathname(out_with_base); |
185 | 3.75k | ada_get_search(out_with_base); |
186 | 3.75k | ada_get_hash(out_with_base); |
187 | 3.75k | ada_get_protocol(out_with_base); |
188 | 3.75k | ada_get_port(out_with_base); |
189 | 3.75k | ada_get_username(out_with_base); |
190 | 3.75k | ada_get_password(out_with_base); |
191 | 3.75k | ada_has_credentials(out_with_base); |
192 | 3.75k | ada_has_port(out_with_base); |
193 | 3.75k | ada_has_hash(out_with_base); |
194 | 3.75k | ada_has_search(out_with_base); |
195 | 3.75k | ada_get_components(out_with_base); |
196 | 3.75k | } |
197 | 13.5k | ada_free(out_with_base); |
198 | | |
199 | 13.5k | { |
200 | 13.5k | ada_owned_string unicode_result = ada_idna_to_unicode(input, input_len); |
201 | 13.5k | ada_free_owned_string(unicode_result); |
202 | 13.5k | ada_owned_string ascii_result = ada_idna_to_ascii(input, input_len); |
203 | 13.5k | ada_free_owned_string(ascii_result); |
204 | 13.5k | } |
205 | | |
206 | 13.5k | { |
207 | 13.5k | const char* version = ada_get_version(); |
208 | 13.5k | if (version == nullptr) { |
209 | 0 | abort(); |
210 | 0 | } |
211 | 13.5k | (void)strlen(version); |
212 | 13.5k | (void)ada_get_version_components().major; |
213 | 13.5k | } |
214 | | |
215 | 0 | { |
216 | 13.5k | ada_url_search_params sp = ada_parse_search_params(input, input_len); |
217 | 13.5k | (void)ada_search_params_size(sp); |
218 | 13.5k | ada_search_params_append(sp, input, input_len, base, base_len); |
219 | 13.5k | ada_search_params_set(sp, input, input_len, base, base_len); |
220 | 13.5k | (void)ada_search_params_has(sp, input, input_len); |
221 | 13.5k | (void)ada_search_params_has_value(sp, input, input_len, base, base_len); |
222 | 13.5k | (void)ada_search_params_get(sp, input, input_len).length; |
223 | 13.5k | ada_strings all_vals = ada_search_params_get_all(sp, input, input_len); |
224 | 13.5k | size_t all_size = ada_strings_size(all_vals); |
225 | 27.1k | for (size_t i = 0; i < all_size; i++) { |
226 | 13.5k | (void)ada_strings_get(all_vals, i).length; |
227 | 13.5k | } |
228 | 13.5k | ada_free_strings(all_vals); |
229 | 13.5k | ada_search_params_sort(sp); |
230 | 13.5k | ada_owned_string sp_str = ada_search_params_to_string(sp); |
231 | 13.5k | ada_free_owned_string(sp_str); |
232 | | |
233 | 13.5k | ada_url_search_params_keys_iter keys = ada_search_params_get_keys(sp); |
234 | 36.9k | while (ada_search_params_keys_iter_has_next(keys)) { |
235 | 23.3k | (void)ada_search_params_keys_iter_next(keys).length; |
236 | 23.3k | } |
237 | 13.5k | ada_free_search_params_keys_iter(keys); |
238 | | |
239 | 13.5k | ada_url_search_params_values_iter vals = ada_search_params_get_values(sp); |
240 | 36.9k | while (ada_search_params_values_iter_has_next(vals)) { |
241 | 23.3k | (void)ada_search_params_values_iter_next(vals).length; |
242 | 23.3k | } |
243 | 13.5k | ada_free_search_params_values_iter(vals); |
244 | | |
245 | 13.5k | ada_url_search_params_entries_iter ents = ada_search_params_get_entries(sp); |
246 | 36.9k | while (ada_search_params_entries_iter_has_next(ents)) { |
247 | 23.3k | ada_string_pair e = ada_search_params_entries_iter_next(ents); |
248 | 23.3k | (void)e.key.length; |
249 | 23.3k | (void)e.value.length; |
250 | 23.3k | } |
251 | 13.5k | ada_free_search_params_entries_iter(ents); |
252 | | |
253 | 13.5k | ada_search_params_remove(sp, input, input_len); |
254 | 13.5k | ada_search_params_remove_value(sp, input, input_len, base, base_len); |
255 | 13.5k | ada_search_params_reset(sp, base, base_len); |
256 | 13.5k | ada_free_search_params(sp); |
257 | 13.5k | } |
258 | | |
259 | 13.5k | static constexpr const char* kAnchors[] = { |
260 | 13.5k | "https://example.com/", |
261 | 13.5k | "http://127.0.0.1/x", |
262 | 13.5k | "https://user:pass@host:8080/p?q=1#f", |
263 | 13.5k | "file:///tmp/x", |
264 | 13.5k | }; |
265 | 54.2k | for (const char* a : kAnchors) { |
266 | 54.2k | size_t n = strlen(a); |
267 | 54.2k | ada_url u = ada_parse(a, n); |
268 | 54.2k | if (ada_is_valid(u) != ada_can_parse(a, n)) { |
269 | 0 | ada_free(u); |
270 | 0 | abort(); |
271 | 0 | } |
272 | 54.2k | exercise_valid_url(u, a, n, "x", 1); |
273 | 54.2k | ada_free(u); |
274 | 54.2k | } |
275 | | |
276 | 13.5k | return 0; |
277 | 13.5k | } |