1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stddef.h>
6 #include <stdint.h>
7
8 #include <fuzzer/FuzzedDataProvider.h>
9
10 #include <iterator>
11 #include <memory>
12 #include <vector>
13
14 #include "base/check_op.h"
15 #include "base/functional/bind.h"
16 #include "base/memory/raw_ptr.h"
17 #include "base/run_loop.h"
18 #include "base/task/sequenced_task_runner.h"
19 #include "base/test/task_environment.h"
20 #include "net/base/address_family.h"
21 #include "net/base/address_list.h"
22 #include "net/base/net_errors.h"
23 #include "net/base/network_isolation_key.h"
24 #include "net/base/request_priority.h"
25 #include "net/dns/context_host_resolver.h"
26 #include "net/dns/fuzzed_host_resolver_util.h"
27 #include "net/dns/host_resolver.h"
28 #include "net/dns/host_resolver_proc.h"
29 #include "net/dns/public/dns_query_type.h"
30 #include "net/dns/public/host_resolver_source.h"
31 #include "net/log/net_log.h"
32 #include "net/log/net_log_with_source.h"
33 #include "net/log/test_net_log.h"
34 #include "net/net_buildflags.h"
35
36 namespace {
37
38 const char* kHostNames[] = {"foo", "foo.com", "a.foo.com",
39 "bar", "localhost", "localhost6"};
40
41 class DnsRequest {
42 public:
DnsRequest(net::HostResolver * host_resolver,FuzzedDataProvider * data_provider,std::vector<std::unique_ptr<DnsRequest>> * dns_requests)43 DnsRequest(net::HostResolver* host_resolver,
44 FuzzedDataProvider* data_provider,
45 std::vector<std::unique_ptr<DnsRequest>>* dns_requests)
46 : host_resolver_(host_resolver),
47 data_provider_(data_provider),
48 dns_requests_(dns_requests) {}
49
50 DnsRequest(const DnsRequest&) = delete;
51 DnsRequest& operator=(const DnsRequest&) = delete;
52
53 ~DnsRequest() = default;
54
55 // Creates and starts a DNS request using fuzzed parameters. If the request
56 // doesn't complete synchronously, adds it to |dns_requests|.
CreateRequest(net::HostResolver * host_resolver,FuzzedDataProvider * data_provider,std::vector<std::unique_ptr<DnsRequest>> * dns_requests)57 static void CreateRequest(
58 net::HostResolver* host_resolver,
59 FuzzedDataProvider* data_provider,
60 std::vector<std::unique_ptr<DnsRequest>>* dns_requests) {
61 auto dns_request = std::make_unique<DnsRequest>(
62 host_resolver, data_provider, dns_requests);
63
64 if (dns_request->Start() == net::ERR_IO_PENDING)
65 dns_requests->push_back(std::move(dns_request));
66 }
67
68 // If |dns_requests| is non-empty, waits for a randomly chosen one of the
69 // requests to complete and removes it from |dns_requests|.
WaitForRequestComplete(FuzzedDataProvider * data_provider,std::vector<std::unique_ptr<DnsRequest>> * dns_requests)70 static void WaitForRequestComplete(
71 FuzzedDataProvider* data_provider,
72 std::vector<std::unique_ptr<DnsRequest>>* dns_requests) {
73 if (dns_requests->empty())
74 return;
75 uint32_t index = data_provider->ConsumeIntegralInRange<uint32_t>(
76 0, dns_requests->size() - 1);
77
78 // Remove the request from the list before waiting on it - this prevents one
79 // of the other callbacks from deleting the callback being waited on.
80 std::unique_ptr<DnsRequest> request = std::move((*dns_requests)[index]);
81 dns_requests->erase(dns_requests->begin() + index);
82 request->WaitUntilDone();
83 }
84
85 // If |dns_requests| is non-empty, attempts to cancel a randomly chosen one of
86 // them and removes it from |dns_requests|. If the one it picks is already
87 // complete, just removes it from the list.
CancelRequest(net::HostResolver * host_resolver,FuzzedDataProvider * data_provider,std::vector<std::unique_ptr<DnsRequest>> * dns_requests)88 static void CancelRequest(
89 net::HostResolver* host_resolver,
90 FuzzedDataProvider* data_provider,
91 std::vector<std::unique_ptr<DnsRequest>>* dns_requests) {
92 if (dns_requests->empty())
93 return;
94 uint32_t index = data_provider->ConsumeIntegralInRange<uint32_t>(
95 0, dns_requests->size() - 1);
96 auto request = dns_requests->begin() + index;
97 (*request)->Cancel();
98 dns_requests->erase(request);
99 }
100
101 private:
OnCallback(int result)102 void OnCallback(int result) {
103 CHECK_NE(net::ERR_IO_PENDING, result);
104
105 request_.reset();
106
107 // Remove |this| from |dns_requests| and take ownership of it, if it wasn't
108 // already removed from the vector. It may have been removed if this is in a
109 // WaitForRequest call, in which case, do nothing.
110 std::unique_ptr<DnsRequest> self;
111 for (auto request = dns_requests_->begin(); request != dns_requests_->end();
112 ++request) {
113 if (request->get() != this)
114 continue;
115 self = std::move(*request);
116 dns_requests_->erase(request);
117 break;
118 }
119
120 while (true) {
121 bool done = false;
122 switch (data_provider_->ConsumeIntegralInRange(0, 2)) {
123 case 0:
124 // Quit on 0, or when no data is left.
125 done = true;
126 break;
127 case 1:
128 CreateRequest(host_resolver_, data_provider_, dns_requests_);
129 break;
130 case 2:
131 CancelRequest(host_resolver_, data_provider_, dns_requests_);
132 break;
133 }
134
135 if (done)
136 break;
137 }
138
139 if (run_loop_)
140 run_loop_->Quit();
141 }
142
143 // Starts the DNS request, using a fuzzed set of parameters.
Start()144 int Start() {
145 net::HostResolver::ResolveHostParameters parameters;
146
147 auto query_types_it = net::kDnsQueryTypes.cbegin();
148 std::advance(query_types_it, data_provider_->ConsumeIntegralInRange<size_t>(
149 0, net::kDnsQueryTypes.size() - 1));
150 parameters.dns_query_type = query_types_it->first;
151
152 parameters.initial_priority = static_cast<net::RequestPriority>(
153 data_provider_->ConsumeIntegralInRange<int32_t>(net::MINIMUM_PRIORITY,
154 net::MAXIMUM_PRIORITY));
155
156 parameters.source =
157 data_provider_->PickValueInArray(net::kHostResolverSources);
158 #if !BUILDFLAG(ENABLE_MDNS)
159 while (parameters.source == net::HostResolverSource::MULTICAST_DNS) {
160 parameters.source =
161 data_provider_->PickValueInArray(net::kHostResolverSources);
162 }
163 #endif // !BUILDFLAG(ENABLE_MDNS)
164
165 parameters.cache_usage =
166 data_provider_->ConsumeBool()
167 ? net::HostResolver::ResolveHostParameters::CacheUsage::ALLOWED
168 : net::HostResolver::ResolveHostParameters::CacheUsage::DISALLOWED;
169
170 // `include_canonical_name` only allowed for address queries and only when
171 // the system resolver can be used.
172 if (net::IsAddressType(parameters.dns_query_type) &&
173 parameters.source != net::HostResolverSource::DNS &&
174 parameters.source != net::HostResolverSource::MULTICAST_DNS) {
175 parameters.include_canonical_name = data_provider_->ConsumeBool();
176 }
177
178 if (!IsParameterCombinationAllowed(parameters)) {
179 return net::ERR_FAILED;
180 }
181
182 const char* hostname = data_provider_->PickValueInArray(kHostNames);
183 request_ = host_resolver_->CreateRequest(
184 net::HostPortPair(hostname, 80), net::NetworkAnonymizationKey(),
185 net::NetLogWithSource(), parameters);
186 int rv = request_->Start(
187 base::BindOnce(&DnsRequest::OnCallback, base::Unretained(this)));
188 if (rv != net::ERR_IO_PENDING)
189 request_.reset();
190 return rv;
191 }
192
193 // Waits until the request is done, if it isn't done already.
WaitUntilDone()194 void WaitUntilDone() {
195 CHECK(!run_loop_);
196 if (request_) {
197 run_loop_ = std::make_unique<base::RunLoop>();
198 run_loop_->Run();
199 run_loop_.reset();
200 }
201 }
202
203 // Some combinations of request parameters are disallowed and expected to
204 // DCHECK. Returns whether or not |parameters| represents one of those cases.
IsParameterCombinationAllowed(net::HostResolver::ResolveHostParameters parameters)205 static bool IsParameterCombinationAllowed(
206 net::HostResolver::ResolveHostParameters parameters) {
207 // SYSTEM requests only support address types.
208 if (parameters.source == net::HostResolverSource::SYSTEM &&
209 !net::IsAddressType(parameters.dns_query_type)) {
210 return false;
211 }
212
213 // Multiple parameters disallowed for mDNS requests.
214 if (parameters.source == net::HostResolverSource::MULTICAST_DNS &&
215 (parameters.include_canonical_name || parameters.loopback_only ||
216 parameters.cache_usage !=
217 net::HostResolver::ResolveHostParameters::CacheUsage::ALLOWED ||
218 parameters.dns_query_type == net::DnsQueryType::HTTPS)) {
219 return false;
220 }
221
222 return true;
223 }
224
225 // Cancel the request, if not already completed. Otherwise, does nothing.
Cancel()226 void Cancel() { request_.reset(); }
227
228 raw_ptr<net::HostResolver> host_resolver_;
229 raw_ptr<FuzzedDataProvider> data_provider_;
230 raw_ptr<std::vector<std::unique_ptr<DnsRequest>>> dns_requests_;
231
232 // Non-null only while running.
233 std::unique_ptr<net::HostResolver::ResolveHostRequest> request_;
234 net::AddressList address_list_;
235
236 std::unique_ptr<base::RunLoop> run_loop_;
237 };
238
239 class FuzzerEnvironment {
240 public:
FuzzerEnvironment()241 FuzzerEnvironment() {
242 net::SetSystemDnsResolutionTaskRunnerForTesting( // IN-TEST
243 base::SequencedTaskRunner::GetCurrentDefault());
244 }
245 ~FuzzerEnvironment() = default;
246 };
247
EnsureInitFuzzerEnvironment()248 void EnsureInitFuzzerEnvironment() {
249 static FuzzerEnvironment init_environment;
250 }
251
252 } // namespace
253
254 // Fuzzer for HostResolverImpl. Fuzzes using both the system resolver and
255 // built-in DNS client paths.
256 //
257 // TODO(mmenke): Add coverage for things this does not cover:
258 // * Out of order completion, particularly for the platform resolver path.
259 // * Simulate network changes, including both enabling and disabling the
260 // async resolver while lookups are active as a result of the change.
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)261 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
262 {
263 FuzzedDataProvider data_provider(data, size);
264
265 EnsureInitFuzzerEnvironment();
266
267 // Including an observer; even though the recorded results aren't currently
268 // used, it'll ensure the netlogging code is fuzzed as well.
269 net::RecordingNetLogObserver net_log_observer;
270
271 net::HostResolver::ManagerOptions options;
272 options.max_concurrent_resolves =
273 data_provider.ConsumeIntegralInRange(1, 8);
274 options.insecure_dns_client_enabled = data_provider.ConsumeBool();
275 bool enable_caching = data_provider.ConsumeBool();
276 std::unique_ptr<net::ContextHostResolver> host_resolver =
277 net::CreateFuzzedContextHostResolver(options, net::NetLog::Get(),
278 &data_provider, enable_caching);
279
280 std::vector<std::unique_ptr<DnsRequest>> dns_requests;
281 bool done = false;
282 while (!done) {
283 switch (data_provider.ConsumeIntegralInRange(0, 3)) {
284 case 0:
285 // Quit on 0, or when no data is left.
286 done = true;
287 break;
288 case 1:
289 DnsRequest::CreateRequest(host_resolver.get(), &data_provider,
290 &dns_requests);
291 break;
292 case 2:
293 DnsRequest::WaitForRequestComplete(&data_provider, &dns_requests);
294 break;
295 case 3:
296 DnsRequest::CancelRequest(host_resolver.get(), &data_provider,
297 &dns_requests);
298 break;
299 }
300 }
301 }
302
303 // Clean up any pending tasks, after deleting everything.
304 base::RunLoop().RunUntilIdle();
305
306 return 0;
307 }
308