xref: /aosp_15_r20/external/cronet/net/dns/host_resolver_manager_fuzzer.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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