Coverage Report

Created: 2026-03-31 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ada-url/fuzz/ada_c.c
Line
Count
Source
1
#include "ada_c.h"
2
3
#include <stdbool.h>
4
#include <stdio.h>
5
#include <stdlib.h>
6
#include <string.h>
7
8
8.50k
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
9
8.50k
  if (size == 0) return 0;
10
11
  /**
12
   * Split input: use first half as URL input, second half as base URL
13
   */
14
8.50k
  size_t half = size / 2;
15
8.50k
  const char* input = (const char*)data;
16
8.50k
  size_t input_len = half;
17
8.50k
  const char* base = (const char*)(data + half);
18
8.50k
  size_t base_len = size - half;
19
20
  /**
21
   * ada_parse and ada_can_parse
22
   */
23
8.50k
  ada_url out = ada_parse(input, input_len);
24
8.50k
  bool is_valid = ada_is_valid(out);
25
26
8.50k
  if (is_valid) {
27
4.69k
    ada_set_href(out, input, input_len);
28
4.69k
    ada_set_host(out, input, input_len);
29
4.69k
    ada_set_hostname(out, input, input_len);
30
4.69k
    ada_set_protocol(out, input, input_len);
31
4.69k
    ada_set_username(out, input, input_len);
32
4.69k
    ada_set_password(out, input, input_len);
33
4.69k
    ada_set_port(out, input, input_len);
34
4.69k
    ada_set_pathname(out, input, input_len);
35
4.69k
    ada_set_search(out, input, input_len);
36
4.69k
    ada_set_hash(out, input, input_len);
37
38
4.69k
    ada_get_hash(out);
39
4.69k
    ada_get_host(out);
40
4.69k
    uint8_t host_type = ada_get_host_type(out);
41
    /* host_type must be in [0, 2]: DEFAULT=0, IPV4=1, IPV6=2 */
42
4.69k
    if (host_type > 2) {
43
0
      printf("ada_get_host_type returned out-of-range value: %u\n",
44
0
             (unsigned)host_type);
45
0
      abort();
46
0
    }
47
4.69k
    ada_get_hostname(out);
48
4.69k
    ada_get_href(out);
49
4.69k
    ada_owned_string out_get_origin = ada_get_origin(out);
50
4.69k
    ada_get_pathname(out);
51
4.69k
    ada_get_username(out);
52
4.69k
    ada_get_password(out);
53
4.69k
    ada_get_protocol(out);
54
4.69k
    ada_get_port(out);
55
4.69k
    ada_get_search(out);
56
57
4.69k
    uint8_t scheme_type = ada_get_scheme_type(out);
58
    /* scheme_type must be in [0, 6]: HTTP=0, NOT_SPECIAL=1, HTTPS=2,
59
     * WS=3, FTP=4, WSS=5, FILE=6 */
60
4.69k
    if (scheme_type > 6) {
61
0
      printf("ada_get_scheme_type returned out-of-range value: %u\n",
62
0
             (unsigned)scheme_type);
63
0
      abort();
64
0
    }
65
66
4.69k
    ada_has_credentials(out);
67
4.69k
    ada_has_empty_hostname(out);
68
4.69k
    ada_has_hostname(out);
69
4.69k
    ada_has_non_empty_username(out);
70
4.69k
    ada_has_non_empty_password(out);
71
4.69k
    ada_has_port(out);
72
4.69k
    ada_has_password(out);
73
4.69k
    ada_has_hash(out);
74
4.69k
    ada_has_search(out);
75
76
    /* Validate ada_url_components offsets: every non-omitted field offset
77
     * must be <= href.length, so the component sits within the href. */
78
4.69k
    ada_string href_for_comps = ada_get_href(out);
79
4.69k
    const ada_url_components* comps = ada_get_components(out);
80
4.69k
    if (comps->protocol_end != ada_url_omitted)
81
4.69k
      if (comps->protocol_end > href_for_comps.length) {
82
0
        printf("ada_url_components.protocol_end out of bounds\n");
83
0
        abort();
84
0
      }
85
4.69k
    if (comps->username_end != ada_url_omitted)
86
4.69k
      if (comps->username_end > href_for_comps.length) {
87
0
        printf("ada_url_components.username_end out of bounds\n");
88
0
        abort();
89
0
      }
90
4.69k
    if (comps->host_start != ada_url_omitted)
91
4.69k
      if (comps->host_start > href_for_comps.length) {
92
0
        printf("ada_url_components.host_start out of bounds\n");
93
0
        abort();
94
0
      }
95
4.69k
    if (comps->host_end != ada_url_omitted)
96
4.69k
      if (comps->host_end > href_for_comps.length) {
97
0
        printf("ada_url_components.host_end out of bounds\n");
98
0
        abort();
99
0
      }
100
    /* NOTE: comps->port is a port NUMBER (0-65535), not an offset. Skip. */
101
4.69k
    if (comps->pathname_start != ada_url_omitted)
102
4.69k
      if (comps->pathname_start > href_for_comps.length) {
103
0
        printf("ada_url_components.pathname_start out of bounds\n");
104
0
        abort();
105
0
      }
106
4.69k
    if (comps->search_start != ada_url_omitted)
107
4.69k
      if (comps->search_start > href_for_comps.length) {
108
0
        printf("ada_url_components.search_start out of bounds\n");
109
0
        abort();
110
0
      }
111
4.69k
    if (comps->hash_start != ada_url_omitted)
112
4.69k
      if (comps->hash_start > href_for_comps.length) {
113
0
        printf("ada_url_components.hash_start out of bounds\n");
114
0
        abort();
115
0
      }
116
117
4.69k
    ada_clear_port(out);
118
4.69k
    ada_clear_hash(out);
119
4.69k
    ada_clear_search(out);
120
121
4.69k
    ada_free_owned_string(out_get_origin);
122
123
    /* Test ada_copy */
124
4.69k
    ada_url out_copy = ada_copy(out);
125
4.69k
    bool copy_is_valid = ada_is_valid(out_copy);
126
4.69k
    if (copy_is_valid) {
127
4.69k
      ada_string href_orig = ada_get_href(out);
128
4.69k
      ada_string href_copy = ada_get_href(out_copy);
129
      /* The copy should have the same href (after our setters above) */
130
4.69k
      (void)href_orig;
131
4.69k
      (void)href_copy;
132
4.69k
    }
133
4.69k
    ada_free(out_copy);
134
135
    /* Re-parse idempotency via C API.
136
     *
137
     * After all setter mutations the URL must still be in a consistent state.
138
     * Whatever href it has now should be its own fixed point: parsing it
139
     * again must succeed and produce the same href.
140
     *
141
     * We read the href before creating the second URL so that the pointer
142
     * remains valid (we do not call any setter on 'out' after this point). */
143
4.69k
    {
144
4.69k
      ada_string final_href = ada_get_href(out);
145
4.69k
      ada_url reparsed = ada_parse(final_href.data, final_href.length);
146
4.69k
      if (ada_is_valid(reparsed)) {
147
4.69k
        ada_string reparsed_href = ada_get_href(reparsed);
148
4.69k
        if (reparsed_href.length != final_href.length ||
149
4.69k
            memcmp(reparsed_href.data, final_href.data, final_href.length) !=
150
4.69k
                0) {
151
0
          printf(
152
0
              "C API href idempotency failure!\n"
153
0
              "  final:    %.*s\n  reparsed: %.*s\n",
154
0
              (int)final_href.length, final_href.data,
155
0
              (int)reparsed_href.length, reparsed_href.data);
156
0
          ada_free(reparsed);
157
0
          abort();
158
0
        }
159
4.69k
      } else {
160
        /* After only valid setter calls the URL must remain parseable. */
161
0
        printf("C API re-parse of href failed: %.*s\n", (int)final_href.length,
162
0
               final_href.data);
163
0
        ada_free(reparsed);
164
0
        abort();
165
0
      }
166
4.69k
      ada_free(reparsed);
167
4.69k
    }
168
4.69k
  }
169
170
8.50k
  bool can_parse_result = ada_can_parse(input, input_len);
171
8.50k
  (void)can_parse_result;
172
173
8.50k
  ada_free(out);
174
175
  /**
176
   * ada_parse_with_base and ada_can_parse_with_base
177
   */
178
8.50k
  ada_url out_with_base = ada_parse_with_base(input, input_len, base, base_len);
179
8.50k
  bool with_base_valid = ada_is_valid(out_with_base);
180
181
8.50k
  if (with_base_valid) {
182
524
    ada_string href = ada_get_href(out_with_base);
183
524
    volatile size_t len = href.length;
184
524
    (void)len;
185
186
524
    ada_owned_string origin = ada_get_origin(out_with_base);
187
524
    ada_free_owned_string(origin);
188
189
524
    ada_get_hostname(out_with_base);
190
524
    ada_get_pathname(out_with_base);
191
524
    ada_get_search(out_with_base);
192
524
    ada_get_hash(out_with_base);
193
524
    ada_get_protocol(out_with_base);
194
524
    ada_get_port(out_with_base);
195
524
    ada_get_username(out_with_base);
196
524
    ada_get_password(out_with_base);
197
198
524
    ada_has_credentials(out_with_base);
199
524
    ada_has_port(out_with_base);
200
524
    ada_has_hash(out_with_base);
201
524
    ada_has_search(out_with_base);
202
524
    ada_get_components(out_with_base);
203
524
  }
204
205
8.50k
  bool can_parse_with_base =
206
8.50k
      ada_can_parse_with_base(input, input_len, base, base_len);
207
208
  /* Consistency check: can_parse_with_base should match
209
   * ada_is_valid(ada_parse_with_base(...)) */
210
8.50k
  if (can_parse_with_base != with_base_valid) {
211
0
    printf("ada_can_parse_with_base inconsistency: can_parse=%d is_valid=%d\n",
212
0
           can_parse_with_base, with_base_valid);
213
0
    abort();
214
0
  }
215
216
8.50k
  ada_free(out_with_base);
217
218
  /**
219
   * IDNA C API
220
   */
221
8.50k
  {
222
8.50k
    ada_owned_string unicode_result = ada_idna_to_unicode(input, input_len);
223
8.50k
    volatile size_t ulen = unicode_result.length;
224
8.50k
    (void)ulen;
225
8.50k
    ada_free_owned_string(unicode_result);
226
227
8.50k
    ada_owned_string ascii_result = ada_idna_to_ascii(input, input_len);
228
8.50k
    volatile size_t alen = ascii_result.length;
229
8.50k
    (void)alen;
230
8.50k
    ada_free_owned_string(ascii_result);
231
8.50k
  }
232
233
  /**
234
   * Version API
235
   */
236
8.50k
  {
237
8.50k
    const char* version = ada_get_version();
238
8.50k
    volatile size_t vlen = strlen(version);
239
8.50k
    (void)vlen;
240
241
8.50k
    ada_version_components ver_comps = ada_get_version_components();
242
8.50k
    volatile int major = ver_comps.major;
243
8.50k
    (void)major;
244
8.50k
  }
245
246
  /**
247
   * Search params C API - comprehensive coverage
248
   */
249
8.50k
  {
250
8.50k
    ada_url_search_params sp = ada_parse_search_params(input, input_len);
251
252
    /* Size */
253
8.50k
    volatile size_t sp_size = ada_search_params_size(sp);
254
8.50k
    (void)sp_size;
255
256
    /* Append */
257
8.50k
    ada_search_params_append(sp, input, input_len, base, base_len);
258
259
    /* Set (replaces first match) */
260
8.50k
    ada_search_params_set(sp, input, input_len, base, base_len);
261
262
    /* has */
263
8.50k
    volatile bool has_key = ada_search_params_has(sp, input, input_len);
264
8.50k
    (void)has_key;
265
266
    /* has_value */
267
8.50k
    volatile bool has_kv =
268
8.50k
        ada_search_params_has_value(sp, input, input_len, base, base_len);
269
8.50k
    (void)has_kv;
270
271
    /* get - returns ada_string (may have data=NULL if not found) */
272
8.50k
    ada_string got = ada_search_params_get(sp, input, input_len);
273
8.50k
    volatile size_t got_len = got.length;
274
8.50k
    (void)got_len;
275
276
    /* get_all */
277
8.50k
    ada_strings all_vals = ada_search_params_get_all(sp, input, input_len);
278
8.50k
    volatile size_t all_size = ada_strings_size(all_vals);
279
17.0k
    for (size_t i = 0; i < all_size; i++) {
280
8.50k
      ada_string s = ada_strings_get(all_vals, i);
281
8.50k
      volatile size_t slen = s.length;
282
8.50k
      (void)slen;
283
8.50k
    }
284
8.50k
    ada_free_strings(all_vals);
285
286
    /* sort */
287
8.50k
    ada_search_params_sort(sp);
288
289
    /* to_string */
290
8.50k
    ada_owned_string sp_str = ada_search_params_to_string(sp);
291
8.50k
    volatile size_t str_len = sp_str.length;
292
8.50k
    (void)str_len;
293
8.50k
    ada_free_owned_string(sp_str);
294
295
    /* keys iterator */
296
8.50k
    ada_url_search_params_keys_iter keys_iter = ada_search_params_get_keys(sp);
297
17.5k
    while (ada_search_params_keys_iter_has_next(keys_iter)) {
298
9.07k
      ada_string k = ada_search_params_keys_iter_next(keys_iter);
299
9.07k
      volatile size_t klen = k.length;
300
9.07k
      (void)klen;
301
9.07k
    }
302
8.50k
    ada_free_search_params_keys_iter(keys_iter);
303
304
    /* values iterator */
305
8.50k
    ada_url_search_params_values_iter values_iter =
306
8.50k
        ada_search_params_get_values(sp);
307
17.5k
    while (ada_search_params_values_iter_has_next(values_iter)) {
308
9.07k
      ada_string v = ada_search_params_values_iter_next(values_iter);
309
9.07k
      volatile size_t vlen = v.length;
310
9.07k
      (void)vlen;
311
9.07k
    }
312
8.50k
    ada_free_search_params_values_iter(values_iter);
313
314
    /* entries iterator */
315
8.50k
    ada_url_search_params_entries_iter entries_iter =
316
8.50k
        ada_search_params_get_entries(sp);
317
17.5k
    while (ada_search_params_entries_iter_has_next(entries_iter)) {
318
9.07k
      ada_string_pair entry = ada_search_params_entries_iter_next(entries_iter);
319
9.07k
      volatile size_t ek = entry.key.length;
320
9.07k
      volatile size_t ev = entry.value.length;
321
9.07k
      (void)ek;
322
9.07k
      (void)ev;
323
9.07k
    }
324
8.50k
    ada_free_search_params_entries_iter(entries_iter);
325
326
    /* remove */
327
8.50k
    ada_search_params_remove(sp, input, input_len);
328
329
    /* remove_value */
330
8.50k
    ada_search_params_remove_value(sp, input, input_len, base, base_len);
331
332
    /* reset */
333
8.50k
    ada_search_params_reset(sp, base, base_len);
334
335
    /* Verify size after reset */
336
8.50k
    volatile size_t sp_size_after = ada_search_params_size(sp);
337
8.50k
    (void)sp_size_after;
338
339
8.50k
    ada_free_search_params(sp);
340
8.50k
  }
341
342
8.50k
  return 0;
343
8.50k
}