1 // Copyright 2016 The Chromium Authors. All rights reserved.
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 <memory>
11 #include <vector>
12
13 #include "base/bind.h"
14 #include "base/logging.h"
15 #include "base/run_loop.h"
16 #include "base/test/task_environment.h"
17 #include "net/base/address_family.h"
18 #include "net/base/address_list.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/request_priority.h"
21 #include "net/dns/context_host_resolver.h"
22 #include "net/dns/fuzzed_host_resolver_util.h"
23 #include "net/dns/host_resolver.h"
24 #include "net/log/net_log_with_source.h"
25 #include "net/log/test_net_log.h"
26 #include "net/net_buildflags.h"
27
28 namespace {
29
30 const char* kHostNames[] = {"foo", "foo.com", "a.foo.com",
31 "bar", "localhost", "localhost6"};
32
33 class DnsRequest {
34 public:
DnsRequest(net::HostResolver * host_resolver,FuzzedDataProvider * data_provider,std::vector<std::unique_ptr<DnsRequest>> * dns_requests)35 DnsRequest(net::HostResolver* host_resolver,
36 FuzzedDataProvider* data_provider,
37 std::vector<std::unique_ptr<DnsRequest>>* dns_requests)
38 : host_resolver_(host_resolver),
39 data_provider_(data_provider),
40 dns_requests_(dns_requests) {}
41
42 ~DnsRequest() = default;
43
44 // Creates and starts a DNS request using fuzzed parameters. If the request
45 // 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)46 static void CreateRequest(
47 net::HostResolver* host_resolver,
48 FuzzedDataProvider* data_provider,
49 std::vector<std::unique_ptr<DnsRequest>>* dns_requests) {
50 std::unique_ptr<DnsRequest> dns_request(
51 new DnsRequest(host_resolver, data_provider, dns_requests));
52
53 if (dns_request->Start() == net::ERR_IO_PENDING)
54 dns_requests->push_back(std::move(dns_request));
55 }
56
57 // If |dns_requests| is non-empty, waits for a randomly chosen one of the
58 // requests to complete and removes it from |dns_requests|.
WaitForRequestComplete(FuzzedDataProvider * data_provider,std::vector<std::unique_ptr<DnsRequest>> * dns_requests)59 static void WaitForRequestComplete(
60 FuzzedDataProvider* data_provider,
61 std::vector<std::unique_ptr<DnsRequest>>* dns_requests) {
62 if (dns_requests->empty())
63 return;
64 uint32_t index = data_provider->ConsumeIntegralInRange<uint32_t>(
65 0, dns_requests->size() - 1);
66
67 // Remove the request from the list before waiting on it - this prevents one
68 // of the other callbacks from deleting the callback being waited on.
69 std::unique_ptr<DnsRequest> request = std::move((*dns_requests)[index]);
70 dns_requests->erase(dns_requests->begin() + index);
71 request->WaitUntilDone();
72 }
73
74 // If |dns_requests| is non-empty, attempts to cancel a randomly chosen one of
75 // them and removes it from |dns_requests|. If the one it picks is already
76 // complete, just removes it from the list.
CancelRequest(net::HostResolver * host_resolver,FuzzedDataProvider * data_provider,std::vector<std::unique_ptr<DnsRequest>> * dns_requests)77 static void CancelRequest(
78 net::HostResolver* host_resolver,
79 FuzzedDataProvider* data_provider,
80 std::vector<std::unique_ptr<DnsRequest>>* dns_requests) {
81 if (dns_requests->empty())
82 return;
83 uint32_t index = data_provider->ConsumeIntegralInRange<uint32_t>(
84 0, dns_requests->size() - 1);
85 auto request = dns_requests->begin() + index;
86 (*request)->Cancel();
87 dns_requests->erase(request);
88 }
89
90 private:
OnCallback(int result)91 void OnCallback(int result) {
92 CHECK_NE(net::ERR_IO_PENDING, result);
93
94 request_.reset();
95
96 // Remove |this| from |dns_requests| and take ownership of it, if it wasn't
97 // already removed from the vector. It may have been removed if this is in a
98 // WaitForRequest call, in which case, do nothing.
99 std::unique_ptr<DnsRequest> self;
100 for (auto request = dns_requests_->begin(); request != dns_requests_->end();
101 ++request) {
102 if (request->get() != this)
103 continue;
104 self = std::move(*request);
105 dns_requests_->erase(request);
106 break;
107 }
108
109 while (true) {
110 bool done = false;
111 switch (data_provider_->ConsumeIntegralInRange(0, 2)) {
112 case 0:
113 // Quit on 0, or when no data is left.
114 done = true;
115 break;
116 case 1:
117 CreateRequest(host_resolver_, data_provider_, dns_requests_);
118 break;
119 case 2:
120 CancelRequest(host_resolver_, data_provider_, dns_requests_);
121 break;
122 }
123
124 if (done)
125 break;
126 }
127
128 if (run_loop_)
129 run_loop_->Quit();
130 }
131
132 // Starts the DNS request, using a fuzzed set of parameters.
Start()133 int Start() {
134 net::HostResolver::ResolveHostParameters parameters;
135 parameters.dns_query_type =
136 data_provider_->PickValueInArray(net::kDnsQueryTypes);
137 parameters.initial_priority = static_cast<net::RequestPriority>(
138 data_provider_->ConsumeIntegralInRange<int32_t>(net::MINIMUM_PRIORITY,
139 net::MAXIMUM_PRIORITY));
140
141 parameters.source =
142 data_provider_->PickValueInArray(net::kHostResolverSources);
143 #if !BUILDFLAG(ENABLE_MDNS)
144 while (parameters.source == net::HostResolverSource::MULTICAST_DNS) {
145 parameters.source =
146 data_provider_->PickValueInArray(net::kHostResolverSources);
147 }
148 #endif // !BUILDFLAG(ENABLE_MDNS)
149
150 parameters.cache_usage =
151 data_provider_->ConsumeBool()
152 ? net::HostResolver::ResolveHostParameters::CacheUsage::ALLOWED
153 : net::HostResolver::ResolveHostParameters::CacheUsage::DISALLOWED;
154 parameters.include_canonical_name = data_provider_->ConsumeBool();
155
156 const char* hostname = data_provider_->PickValueInArray(kHostNames);
157 request_ = host_resolver_->CreateRequest(
158 net::HostPortPair(hostname, 80), net::NetLogWithSource(), parameters);
159 int rv = request_->Start(
160 base::BindOnce(&DnsRequest::OnCallback, base::Unretained(this)));
161 if (rv != net::ERR_IO_PENDING)
162 request_.reset();
163 return rv;
164 }
165
166 // Waits until the request is done, if it isn't done already.
WaitUntilDone()167 void WaitUntilDone() {
168 CHECK(!run_loop_);
169 if (request_) {
170 run_loop_.reset(new base::RunLoop());
171 run_loop_->Run();
172 run_loop_.reset();
173 }
174 }
175
176 // Cancel the request, if not already completed. Otherwise, does nothing.
Cancel()177 void Cancel() { request_.reset(); }
178
179 net::HostResolver* host_resolver_;
180 FuzzedDataProvider* data_provider_;
181 std::vector<std::unique_ptr<DnsRequest>>* dns_requests_;
182
183 // Non-null only while running.
184 std::unique_ptr<net::HostResolver::ResolveHostRequest> request_;
185 net::AddressList address_list_;
186
187 std::unique_ptr<base::RunLoop> run_loop_;
188
189 DISALLOW_COPY_AND_ASSIGN(DnsRequest);
190 };
191
192 } // namespace
193
194 // Fuzzer for HostResolverImpl. Fuzzes using both the system resolver and
195 // built-in DNS client paths.
196 //
197 // TODO(mmenke): Add coverage for things this does not cover:
198 // * Out of order completion, particularly for the platform resolver path.
199 // * Simulate network changes, including both enabling and disabling the
200 // async resolver while lookups are active as a result of the change.
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)201 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
202 {
203 FuzzedDataProvider data_provider(data, size);
204 net::RecordingTestNetLog net_log;
205
206 net::HostResolver::ManagerOptions options;
207 options.max_concurrent_resolves =
208 data_provider.ConsumeIntegralInRange(1, 8);
209 options.insecure_dns_client_enabled = data_provider.ConsumeBool();
210 bool enable_caching = data_provider.ConsumeBool();
211 std::unique_ptr<net::ContextHostResolver> host_resolver =
212 net::CreateFuzzedContextHostResolver(options, &net_log, &data_provider,
213 enable_caching);
214
215 std::vector<std::unique_ptr<DnsRequest>> dns_requests;
216 bool done = false;
217 while (!done) {
218 switch (data_provider.ConsumeIntegralInRange(0, 3)) {
219 case 0:
220 // Quit on 0, or when no data is left.
221 done = true;
222 break;
223 case 1:
224 DnsRequest::CreateRequest(host_resolver.get(), &data_provider,
225 &dns_requests);
226 break;
227 case 2:
228 DnsRequest::WaitForRequestComplete(&data_provider, &dns_requests);
229 break;
230 case 3:
231 DnsRequest::CancelRequest(host_resolver.get(), &data_provider,
232 &dns_requests);
233 break;
234 }
235 }
236 }
237
238 // Clean up any pending tasks, after deleting everything.
239 base::RunLoop().RunUntilIdle();
240
241 return 0;
242 }
243