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