Coverage Report

Created: 2026-08-13 07:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
33.8k
static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) {
71
33.8k
  return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)];
72
33.8k
}
simple_absolute.cc:char const* pick<8ul>(FuzzedDataProvider&, char const* const (&) [8ul])
Line
Count
Source
70
6.77k
static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) {
71
6.77k
  return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)];
72
6.77k
}
simple_absolute.cc:char const* pick<20ul>(FuzzedDataProvider&, char const* const (&) [20ul])
Line
Count
Source
70
6.77k
static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) {
71
6.77k
  return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)];
72
6.77k
}
simple_absolute.cc:char const* pick<16ul>(FuzzedDataProvider&, char const* const (&) [16ul])
Line
Count
Source
70
6.77k
static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) {
71
6.77k
  return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)];
72
6.77k
}
simple_absolute.cc:char const* pick<6ul>(FuzzedDataProvider&, char const* const (&) [6ul])
Line
Count
Source
70
6.77k
static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) {
71
6.77k
  return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)];
72
6.77k
}
simple_absolute.cc:char const* pick<5ul>(FuzzedDataProvider&, char const* const (&) [5ul])
Line
Count
Source
70
6.77k
static const char* pick(FuzzedDataProvider& fdp, const char* const (&arr)[N]) {
71
6.77k
  return arr[fdp.ConsumeIntegralInRange<size_t>(0, N - 1)];
72
6.77k
}
73
74
6.77k
static std::string make_candidate(FuzzedDataProvider& fdp) {
75
6.77k
  std::string out;
76
6.77k
  out += pick(fdp, kSchemes);
77
6.77k
  out += pick(fdp, kHosts);
78
6.77k
  out += pick(fdp, kPaths);
79
6.77k
  out += pick(fdp, kQueries);
80
6.77k
  out += pick(fdp, kFrags);
81
82
6.77k
  if (fdp.ConsumeBool() && !out.empty()) {
83
2.22k
    std::string mid = fdp.ConsumeRandomLengthString(24);
84
2.22k
    size_t pos = fdp.ConsumeIntegralInRange<size_t>(0, out.size());
85
2.22k
    out.insert(pos, mid);
86
2.22k
  }
87
6.77k
  if (fdp.ConsumeBool() && !out.empty()) {
88
1.35k
    size_t i = fdp.ConsumeIntegralInRange<size_t>(0, out.size() - 1);
89
1.35k
    out[i] = static_cast<char>(fdp.ConsumeIntegral<uint8_t>());
90
1.35k
  }
91
6.77k
  return out;
92
6.77k
}
93
94
232k
static void check_href_size(const ada::url& u, std::string_view input) {
95
232k
  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
232k
}
106
107
static void check_href_size(const ada::url_aggregator& u,
108
232k
                            std::string_view input) {
109
232k
  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
232k
}
120
121
template <class Result>
122
static void check_reparse_idempotent(const Result& parsed,
123
232k
                                     std::string_view input) {
124
232k
  const std::string href = std::string(parsed.get_href());
125
232k
  auto again = ada::parse<Result>(href);
126
232k
  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
232k
  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
232k
}
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
116k
                                     std::string_view input) {
124
116k
  const std::string href = std::string(parsed.get_href());
125
116k
  auto again = ada::parse<Result>(href);
126
116k
  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
116k
  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
116k
}
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
116k
                                     std::string_view input) {
124
116k
  const std::string href = std::string(parsed.get_href());
125
116k
  auto again = ada::parse<Result>(href);
126
116k
  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
116k
  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
116k
}
145
146
static void check_components_agree(const ada::url& u,
147
                                   const ada::url_aggregator& a,
148
116k
                                   std::string_view input) {
149
116k
  if (u.get_protocol() != a.get_protocol() ||
150
116k
      u.get_href() != std::string(a.get_href()) ||
151
116k
      std::string(u.get_hostname()) != std::string(a.get_hostname()) ||
152
116k
      std::string(u.get_pathname()) != std::string(a.get_pathname()) ||
153
116k
      std::string(u.get_search()) != std::string(a.get_search()) ||
154
116k
      std::string(u.get_hash()) != std::string(a.get_hash()) ||
155
116k
      std::string(u.get_port()) != std::string(a.get_port()) ||
156
116k
      u.get_username() != std::string(a.get_username()) ||
157
116k
      u.get_password() != std::string(a.get_password()) ||
158
116k
      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
116k
}
169
170
119k
static void fuzz_one_input(std::string_view input) {
171
119k
  auto url = ada::parse<ada::url>(input);
172
119k
  auto agg = ada::parse<ada::url_aggregator>(input);
173
174
119k
  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
119k
  if (!url) {
185
2.70k
    return;
186
2.70k
  }
187
188
116k
  check_components_agree(*url, *agg, input);
189
116k
  check_href_size(*url, input);
190
116k
  check_href_size(*agg, input);
191
116k
  check_reparse_idempotent(*url, input);
192
116k
  check_reparse_idempotent(*agg, input);
193
194
116k
  const std::string href = url->get_href();
195
116k
  url->set_href(href);
196
116k
  agg->set_href(href);
197
116k
  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
116k
  check_href_size(*url, href);
205
116k
  check_href_size(*agg, href);
206
207
116k
  volatile bool valid = agg->validate();
208
116k
  (void)valid;
209
116k
}
210
211
6.77k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
212
6.77k
  FuzzedDataProvider fdp(data, size);
213
214
6.77k
  fuzz_one_input(make_candidate(fdp));
215
216
6.77k
  if (fdp.remaining_bytes() > 0) {
217
3.97k
    std::string raw = fdp.ConsumeRemainingBytesAsString();
218
3.97k
    if (raw.size() > 512) {
219
24
      raw.resize(512);
220
24
    }
221
3.97k
    if (!raw.empty() && (static_cast<unsigned char>(raw[0]) & 1u)) {
222
1.83k
      raw = std::string("https://") + raw;
223
2.13k
    } else if (!raw.empty()) {
224
2.13k
      raw = std::string("http://") + raw;
225
2.13k
    }
226
3.97k
    fuzz_one_input(raw);
227
3.97k
  }
228
229
6.77k
  static constexpr const char* kAnchors[] = {
230
6.77k
      "https://example.com",
231
6.77k
      "https://example.com/",
232
6.77k
      "https://example.com?q=1",
233
6.77k
      "https://example.com#frag",
234
6.77k
      "https://example.com/?q=1#frag",
235
6.77k
      "http://www.example.com/path/file.js",
236
6.77k
      "http://WWW.Example.COM/file.js",
237
6.77k
      "https://www.google.com/imghp?hl=en&tab=wi",
238
6.77k
      "http://192.168.0.1/x",
239
6.77k
      "http://0x7f.1/",
240
6.77k
      "https://user:pass@example.com/x",
241
6.77k
      "https://example.com:8080/x",
242
6.77k
      "https://example.com/a/./b/../c",
243
6.77k
      "https://example.com/foo/%2e%2e",
244
6.77k
      "https://xn--nxasmq6b.com/",
245
6.77k
      "https://xn--a/",
246
6.77k
  };
247
108k
  for (const char* seed : kAnchors) {
248
108k
    fuzz_one_input(seed);
249
108k
  }
250
251
6.77k
  return 0;
252
6.77k
}