Coverage Report

Created: 2026-09-03 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}