blob: b4f65c59e874771637034d00747fb3edc35ba805 [file] [log] [blame]
mmenke91c17162016-06-02 16:03:231// 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 <memory>
9#include <vector>
10
11#include "base/bind.h"
12#include "base/logging.h"
13#include "base/run_loop.h"
14#include "net/base/address_family.h"
15#include "net/base/address_list.h"
16#include "net/base/fuzzed_data_provider.h"
17#include "net/base/net_errors.h"
18#include "net/base/request_priority.h"
19#include "net/dns/fuzzed_host_resolver.h"
20#include "net/dns/host_resolver.h"
21#include "net/log/test_net_log.h"
22
23namespace {
24
25const char* kHostNames[] = {"foo", "foo.com", "a.foo.com",
26 "bar", "localhost", "localhost6"};
27
28net::AddressFamily kAddressFamilies[] = {
29 net::ADDRESS_FAMILY_UNSPECIFIED, net::ADDRESS_FAMILY_IPV4,
30 net::ADDRESS_FAMILY_IPV6,
31};
32
33class DnsRequest {
34 public:
35 DnsRequest(net::HostResolver* host_resolver,
36 net::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),
mmenke91c17162016-06-02 16:03:2341 is_running_(false) {}
42
maksim.sisov31452af2016-07-27 06:38:1043 ~DnsRequest() {}
mmenke91c17162016-06-02 16:03:2344
45 // Creates and starts a DNS request using fuzzed parameters. If the request
46 // doesn't complete synchronously, adds it to |dns_requests|.
47 static void CreateRequest(
48 net::HostResolver* host_resolver,
49 net::FuzzedDataProvider* data_provider,
50 std::vector<std::unique_ptr<DnsRequest>>* dns_requests) {
51 std::unique_ptr<DnsRequest> dns_request(
52 new DnsRequest(host_resolver, data_provider, dns_requests));
53
54 if (dns_request->Start() == net::ERR_IO_PENDING)
55 dns_requests->push_back(std::move(dns_request));
56 }
57
58 // If |dns_requests| is non-empty, waits for a randomly chosen one of the
59 // requests to complete and removes it from |dns_requests|.
60 static void WaitForRequestComplete(
61 net::FuzzedDataProvider* data_provider,
62 std::vector<std::unique_ptr<DnsRequest>>* dns_requests) {
63 if (dns_requests->empty())
64 return;
65 uint32_t index =
66 data_provider->ConsumeUint32InRange(0, dns_requests->size() - 1);
67
68 // Remove the request from the list before waiting on it - this prevents one
69 // of the other callbacks from deleting the callback being waited on.
70 std::unique_ptr<DnsRequest> request = std::move((*dns_requests)[index]);
71 dns_requests->erase(dns_requests->begin() + index);
72 request->WaitUntilDone();
73 }
74
75 // If |dns_requests| is non-empty, attempts to cancel a randomly chosen one of
76 // them and removes it from |dns_requests|. If the one it picks is already
77 // complete, just removes it from the list.
78 static void CancelRequest(
79 net::HostResolver* host_resolver,
80 net::FuzzedDataProvider* data_provider,
81 std::vector<std::unique_ptr<DnsRequest>>* dns_requests) {
82 if (dns_requests->empty())
83 return;
84 uint32_t index =
85 data_provider->ConsumeUint32InRange(0, dns_requests->size() - 1);
86 auto request = dns_requests->begin() + index;
87 (*request)->Cancel();
88 dns_requests->erase(request);
89 }
90
91 private:
92 void OnCallback(int result) {
93 CHECK_NE(net::ERR_IO_PENDING, result);
94
95 is_running_ = false;
maksim.sisov31452af2016-07-27 06:38:1096 request_.reset();
mmenke91c17162016-06-02 16:03:2397
98 // Remove |this| from |dns_requests| and take ownership of it, if it wasn't
99 // already removed from the vector. It may have been removed if this is in a
100 // WaitForRequest call, in which case, do nothing.
101 std::unique_ptr<DnsRequest> self;
102 for (auto request = dns_requests_->begin(); request != dns_requests_->end();
103 ++request) {
104 if (request->get() != this)
105 continue;
106 self = std::move(*request);
107 dns_requests_->erase(request);
108 break;
109 }
110
111 while (true) {
112 bool done = false;
113 switch (data_provider_->ConsumeInt32InRange(0, 2)) {
114 case 0:
115 // Quit on 0, or when no data is left.
116 done = true;
117 break;
118 case 1:
119 CreateRequest(host_resolver_, data_provider_, dns_requests_);
120 break;
121 case 2:
122 CancelRequest(host_resolver_, data_provider_, dns_requests_);
123 break;
124 }
125
126 if (done)
127 break;
128 }
129
130 if (run_loop_)
131 run_loop_->Quit();
132 }
133
134 // Starts the DNS request, using a fuzzed set of parameters.
135 int Start() {
136 const char* hostname = data_provider_->PickValueInArray(kHostNames);
137 net::HostResolver::RequestInfo info(net::HostPortPair(hostname, 80));
138 info.set_address_family(data_provider_->PickValueInArray(kAddressFamilies));
139 if (data_provider_->ConsumeBool())
140 info.set_host_resolver_flags(net::HOST_RESOLVER_CANONNAME);
141
142 net::RequestPriority priority =
143 static_cast<net::RequestPriority>(data_provider_->ConsumeInt32InRange(
144 net::MINIMUM_PRIORITY, net::MAXIMUM_PRIORITY));
145
146 // Decide if should be a cache-only resolution.
147 if (data_provider_->ConsumeBool()) {
148 return host_resolver_->ResolveFromCache(info, &address_list_,
149 net::BoundNetLog());
150 }
151
152 info.set_allow_cached_response(data_provider_->ConsumeBool());
153 return host_resolver_->Resolve(
154 info, priority, &address_list_,
maksim.sisov31452af2016-07-27 06:38:10155 base::Bind(&DnsRequest::OnCallback, base::Unretained(this)), &request_,
mmenke91c17162016-06-02 16:03:23156 net::BoundNetLog());
157 }
158
159 // Waits until the request is done, if it isn't done already.
160 void WaitUntilDone() {
161 CHECK(!run_loop_);
162 if (is_running_) {
163 run_loop_.reset(new base::RunLoop());
164 run_loop_->Run();
165 run_loop_.reset();
maksim.sisov31452af2016-07-27 06:38:10166 DCHECK(request_);
mmenke91c17162016-06-02 16:03:23167 }
168 }
169
170 // Cancel the request, if not already completed. Otherwise, does nothing.
171 void Cancel() {
maksim.sisov31452af2016-07-27 06:38:10172 request_.reset();
mmenke91c17162016-06-02 16:03:23173 is_running_ = false;
174 }
175
176 net::HostResolver* host_resolver_;
177 net::FuzzedDataProvider* data_provider_;
178 std::vector<std::unique_ptr<DnsRequest>>* dns_requests_;
179
maksim.sisov31452af2016-07-27 06:38:10180 std::unique_ptr<net::HostResolver::Request> request_;
mmenke91c17162016-06-02 16:03:23181 net::AddressList address_list_;
182
183 bool is_running_;
184
185 std::unique_ptr<base::RunLoop> run_loop_;
186
187 DISALLOW_COPY_AND_ASSIGN(DnsRequest);
188};
189
190} // namespace
191
192// Fuzzer for HostResolverImpl. Fuzzes using both the system resolver and
193// built-in DNS client paths.
194//
195// TODO(mmenke): Add coverage for things this does not cover:
196// * Out of order completion, particularly for the platform resolver path.
197// * Simulate network changes, including both enabling and disabling the
198// async resolver while lookups are active as a result of the change.
199extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
200 {
201 net::FuzzedDataProvider data_provider(data, size);
202 net::TestNetLog net_log;
203
204 net::HostResolver::Options options;
205 options.max_concurrent_resolves = data_provider.ConsumeUint32InRange(1, 8);
206 options.enable_caching = data_provider.ConsumeBool();
207 net::FuzzedHostResolver host_resolver(options, &net_log, &data_provider);
208 host_resolver.SetDnsClientEnabled(data_provider.ConsumeBool());
209
210 std::vector<std::unique_ptr<DnsRequest>> dns_requests;
211 while (true) {
212 switch (data_provider.ConsumeInt32InRange(0, 3)) {
213 case 0:
214 // Quit on 0, or when no data is left.
215 return 0;
216 case 1:
217 DnsRequest::CreateRequest(&host_resolver, &data_provider,
218 &dns_requests);
219 break;
220 case 2:
221 DnsRequest::WaitForRequestComplete(&data_provider, &dns_requests);
222 break;
223 case 3:
224 DnsRequest::CancelRequest(&host_resolver, &data_provider,
225 &dns_requests);
226 break;
227 }
228 }
229 }
230
231 // Clean up any pending tasks, after deleting everything.
232 base::RunLoop().RunUntilIdle();
233
234 return 0;
235}