Coverage Report

Created: 2026-07-16 06:37

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
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
}