xref: /aosp_15_r20/external/cronet/net/dns/fuzzed_host_resolver_util.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 "net/dns/fuzzed_host_resolver_util.h"
6 
7 #include <stdint.h>
8 
9 #include <fuzzer/FuzzedDataProvider.h>
10 
11 #include <limits>
12 #include <memory>
13 #include <string>
14 #include <utility>
15 #include <vector>
16 
17 #include "base/check.h"
18 #include "base/functional/bind.h"
19 #include "base/memory/raw_ptr.h"
20 #include "base/memory/ref_counted.h"
21 #include "base/memory/weak_ptr.h"
22 #include "base/notreached.h"
23 #include "base/ranges/algorithm.h"
24 #include "base/task/single_thread_task_runner.h"
25 #include "net/base/address_list.h"
26 #include "net/base/completion_once_callback.h"
27 #include "net/base/io_buffer.h"
28 #include "net/base/ip_address.h"
29 #include "net/base/ip_endpoint.h"
30 #include "net/base/net_errors.h"
31 #include "net/dns/context_host_resolver.h"
32 #include "net/dns/dns_client.h"
33 #include "net/dns/dns_config.h"
34 #include "net/dns/dns_hosts.h"
35 #include "net/dns/host_cache.h"
36 #include "net/dns/host_resolver_manager.h"
37 #include "net/dns/host_resolver_proc.h"
38 #include "net/dns/host_resolver_system_task.h"
39 #include "net/dns/mdns_client.h"
40 #include "net/dns/public/util.h"
41 #include "net/dns/resolve_context.h"
42 #include "net/log/net_log.h"
43 #include "net/log/net_log_with_source.h"
44 #include "net/socket/datagram_server_socket.h"
45 #include "net/socket/fuzzed_socket_factory.h"
46 
47 namespace net {
48 
49 namespace {
50 
51 // Returns a fuzzed non-zero port number.
FuzzPort(FuzzedDataProvider * data_provider)52 uint16_t FuzzPort(FuzzedDataProvider* data_provider) {
53   return data_provider->ConsumeIntegral<uint16_t>();
54 }
55 
56 // Returns a fuzzed IPv4 address.  Can return invalid / reserved addresses.
FuzzIPv4Address(FuzzedDataProvider * data_provider)57 IPAddress FuzzIPv4Address(FuzzedDataProvider* data_provider) {
58   return IPAddress(data_provider->ConsumeIntegral<uint8_t>(),
59                    data_provider->ConsumeIntegral<uint8_t>(),
60                    data_provider->ConsumeIntegral<uint8_t>(),
61                    data_provider->ConsumeIntegral<uint8_t>());
62 }
63 
64 // Returns a fuzzed IPv6 address.  Can return invalid / reserved addresses.
FuzzIPv6Address(FuzzedDataProvider * data_provider)65 IPAddress FuzzIPv6Address(FuzzedDataProvider* data_provider) {
66   return IPAddress(data_provider->ConsumeIntegral<uint8_t>(),
67                    data_provider->ConsumeIntegral<uint8_t>(),
68                    data_provider->ConsumeIntegral<uint8_t>(),
69                    data_provider->ConsumeIntegral<uint8_t>(),
70                    data_provider->ConsumeIntegral<uint8_t>(),
71                    data_provider->ConsumeIntegral<uint8_t>(),
72                    data_provider->ConsumeIntegral<uint8_t>(),
73                    data_provider->ConsumeIntegral<uint8_t>(),
74                    data_provider->ConsumeIntegral<uint8_t>(),
75                    data_provider->ConsumeIntegral<uint8_t>(),
76                    data_provider->ConsumeIntegral<uint8_t>(),
77                    data_provider->ConsumeIntegral<uint8_t>(),
78                    data_provider->ConsumeIntegral<uint8_t>(),
79                    data_provider->ConsumeIntegral<uint8_t>(),
80                    data_provider->ConsumeIntegral<uint8_t>(),
81                    data_provider->ConsumeIntegral<uint8_t>());
82 }
83 
84 // Returns a fuzzed address, which can be either IPv4 or IPv6.  Can return
85 // invalid / reserved addresses.
FuzzIPAddress(FuzzedDataProvider * data_provider)86 IPAddress FuzzIPAddress(FuzzedDataProvider* data_provider) {
87   if (data_provider->ConsumeBool())
88     return FuzzIPv4Address(data_provider);
89   return FuzzIPv6Address(data_provider);
90 }
91 
GetFuzzedDnsConfig(FuzzedDataProvider * data_provider)92 DnsConfig GetFuzzedDnsConfig(FuzzedDataProvider* data_provider) {
93   // Fuzz DNS configuration.
94   DnsConfig config;
95 
96   // Fuzz name servers.
97   uint32_t num_nameservers = data_provider->ConsumeIntegralInRange(0, 4);
98   for (uint32_t i = 0; i < num_nameservers; ++i) {
99     config.nameservers.push_back(
100         IPEndPoint(FuzzIPAddress(data_provider), FuzzPort(data_provider)));
101   }
102 
103   // Fuzz suffix search list.
104   switch (data_provider->ConsumeIntegralInRange(0, 3)) {
105     case 3:
106       config.search.push_back("foo.com");
107       [[fallthrough]];
108     case 2:
109       config.search.push_back("bar");
110       [[fallthrough]];
111     case 1:
112       config.search.push_back("com");
113       [[fallthrough]];
114     default:
115       break;
116   }
117 
118   net::DnsHosts hosts;
119   // Fuzz hosts file.
120   uint8_t num_hosts_entries = data_provider->ConsumeIntegral<uint8_t>();
121   for (uint8_t i = 0; i < num_hosts_entries; ++i) {
122     const char* kHostnames[] = {"foo", "foo.com",   "a.foo.com",
123                                 "bar", "localhost", "localhost6"};
124     const char* hostname = data_provider->PickValueInArray(kHostnames);
125     net::IPAddress address = FuzzIPAddress(data_provider);
126     config.hosts[net::DnsHostsKey(hostname, net::GetAddressFamily(address))] =
127         address;
128   }
129 
130   config.unhandled_options = data_provider->ConsumeBool();
131   config.append_to_multi_label_name = data_provider->ConsumeBool();
132   config.ndots = data_provider->ConsumeIntegralInRange(0, 3);
133   config.attempts = data_provider->ConsumeIntegralInRange(1, 3);
134 
135   // Fallback periods don't really work for fuzzing. Even a period of 0
136   // milliseconds will be increased after the first expiration, resulting in
137   // inconsistent behavior.
138   config.fallback_period = base::Days(10);
139 
140   config.rotate = data_provider->ConsumeBool();
141 
142   config.use_local_ipv6 = data_provider->ConsumeBool();
143 
144   return config;
145 }
146 
147 // HostResolverProc that returns a random set of results, and can succeed or
148 // fail. Must only be run on the thread it's created on.
149 class FuzzedHostResolverProc : public HostResolverProc {
150  public:
151   // Can safely be used after the destruction of |data_provider|. This can
152   // happen if a request is issued but the code never waits for the result
153   // before the test ends.
FuzzedHostResolverProc(base::WeakPtr<FuzzedDataProvider> data_provider)154   explicit FuzzedHostResolverProc(
155       base::WeakPtr<FuzzedDataProvider> data_provider)
156       : HostResolverProc(nullptr),
157         data_provider_(data_provider),
158         network_task_runner_(
159             base::SingleThreadTaskRunner::GetCurrentDefault()) {}
160 
161   FuzzedHostResolverProc(const FuzzedHostResolverProc&) = delete;
162   FuzzedHostResolverProc& operator=(const FuzzedHostResolverProc&) = delete;
163 
Resolve(const std::string & host,AddressFamily address_family,HostResolverFlags host_resolver_flags,AddressList * addrlist,int * os_error)164   int Resolve(const std::string& host,
165               AddressFamily address_family,
166               HostResolverFlags host_resolver_flags,
167               AddressList* addrlist,
168               int* os_error) override {
169     DCHECK(network_task_runner_->BelongsToCurrentThread());
170 
171     if (os_error)
172       *os_error = 0;
173 
174     // If the data provider is no longer avaiable, just fail. The HostResolver
175     // has already been deleted by this point, anyways.
176     if (!data_provider_)
177       return ERR_FAILED;
178 
179     AddressList result;
180 
181     // Put IPv6 addresses before IPv4 ones. This code doesn't sort addresses
182     // correctly, but when sorted according to spec, IPv6 addresses are
183     // generally before IPv4 ones.
184     if (address_family == ADDRESS_FAMILY_UNSPECIFIED ||
185         address_family == ADDRESS_FAMILY_IPV6) {
186       uint8_t num_ipv6_addresses = data_provider_->ConsumeIntegral<uint8_t>();
187       for (uint8_t i = 0; i < num_ipv6_addresses; ++i) {
188         result.push_back(
189             net::IPEndPoint(FuzzIPv6Address(data_provider_.get()), 0));
190       }
191     }
192 
193     if (address_family == ADDRESS_FAMILY_UNSPECIFIED ||
194         address_family == ADDRESS_FAMILY_IPV4) {
195       uint8_t num_ipv4_addresses = data_provider_->ConsumeIntegral<uint8_t>();
196       for (uint8_t i = 0; i < num_ipv4_addresses; ++i) {
197         result.push_back(
198             net::IPEndPoint(FuzzIPv4Address(data_provider_.get()), 0));
199       }
200     }
201 
202     if (result.empty())
203       return ERR_NAME_NOT_RESOLVED;
204 
205     if (host_resolver_flags & HOST_RESOLVER_CANONNAME) {
206       // Don't bother to fuzz this - almost nothing cares.
207       std::vector<std::string> aliases({"foo.com"});
208       result.SetDnsAliases(std::move(aliases));
209     }
210 
211     *addrlist = result;
212     return OK;
213   }
214 
215  private:
216   ~FuzzedHostResolverProc() override = default;
217 
218   base::WeakPtr<FuzzedDataProvider> data_provider_;
219 
220   // Just used for thread-safety checks.
221   scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
222 };
223 
224 const Error kMdnsErrors[] = {ERR_FAILED,
225                              ERR_ACCESS_DENIED,
226                              ERR_INTERNET_DISCONNECTED,
227                              ERR_TIMED_OUT,
228                              ERR_CONNECTION_RESET,
229                              ERR_CONNECTION_ABORTED,
230                              ERR_CONNECTION_REFUSED,
231                              ERR_ADDRESS_UNREACHABLE};
232 // Fuzzed socket implementation to handle the limited functionality used by
233 // MDnsClientImpl. Uses a FuzzedDataProvider to generate errors or responses for
234 // RecvFrom calls.
235 class FuzzedMdnsSocket : public DatagramServerSocket {
236  public:
FuzzedMdnsSocket(FuzzedDataProvider * data_provider)237   explicit FuzzedMdnsSocket(FuzzedDataProvider* data_provider)
238       : data_provider_(data_provider),
239         local_address_(FuzzIPAddress(data_provider_), 5353) {}
240 
Listen(const IPEndPoint & address)241   int Listen(const IPEndPoint& address) override { return OK; }
242 
RecvFrom(IOBuffer * buffer,int buffer_length,IPEndPoint * out_address,CompletionOnceCallback callback)243   int RecvFrom(IOBuffer* buffer,
244                int buffer_length,
245                IPEndPoint* out_address,
246                CompletionOnceCallback callback) override {
247     if (data_provider_->ConsumeBool())
248       return GenerateResponse(buffer, buffer_length, out_address);
249 
250     // Maybe never receive any responses.
251     if (data_provider_->ConsumeBool()) {
252       base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
253           FROM_HERE,
254           base::BindOnce(&FuzzedMdnsSocket::CompleteRecv,
255                          weak_factory_.GetWeakPtr(), std::move(callback),
256                          base::RetainedRef(buffer), buffer_length,
257                          out_address));
258     }
259 
260     return ERR_IO_PENDING;
261   }
262 
SendTo(IOBuffer * buf,int buf_len,const IPEndPoint & address,CompletionOnceCallback callback)263   int SendTo(IOBuffer* buf,
264              int buf_len,
265              const IPEndPoint& address,
266              CompletionOnceCallback callback) override {
267     if (data_provider_->ConsumeBool()) {
268       return data_provider_->ConsumeBool()
269                  ? OK
270                  : data_provider_->PickValueInArray(kMdnsErrors);
271     }
272 
273     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
274         FROM_HERE,
275         base::BindOnce(&FuzzedMdnsSocket::CompleteSend,
276                        weak_factory_.GetWeakPtr(), std::move(callback)));
277     return ERR_IO_PENDING;
278   }
279 
SetReceiveBufferSize(int32_t size)280   int SetReceiveBufferSize(int32_t size) override { return OK; }
SetSendBufferSize(int32_t size)281   int SetSendBufferSize(int32_t size) override { return OK; }
282 
AllowAddressReuse()283   void AllowAddressReuse() override {}
AllowBroadcast()284   void AllowBroadcast() override {}
AllowAddressSharingForMulticast()285   void AllowAddressSharingForMulticast() override {}
286 
JoinGroup(const IPAddress & group_address) const287   int JoinGroup(const IPAddress& group_address) const override { return OK; }
LeaveGroup(const IPAddress & group_address) const288   int LeaveGroup(const IPAddress& group_address) const override { return OK; }
SetMulticastInterface(uint32_t interface_index)289   int SetMulticastInterface(uint32_t interface_index) override { return OK; }
SetMulticastTimeToLive(int time_to_live)290   int SetMulticastTimeToLive(int time_to_live) override { return OK; }
SetMulticastLoopbackMode(bool loopback)291   int SetMulticastLoopbackMode(bool loopback) override { return OK; }
292 
SetDiffServCodePoint(DiffServCodePoint dscp)293   int SetDiffServCodePoint(DiffServCodePoint dscp) override { return OK; }
294 
DetachFromThread()295   void DetachFromThread() override {}
296 
Close()297   void Close() override {}
GetPeerAddress(IPEndPoint * address) const298   int GetPeerAddress(IPEndPoint* address) const override {
299     return ERR_SOCKET_NOT_CONNECTED;
300   }
GetLocalAddress(IPEndPoint * address) const301   int GetLocalAddress(IPEndPoint* address) const override {
302     *address = local_address_;
303     return OK;
304   }
UseNonBlockingIO()305   void UseNonBlockingIO() override {}
SetDoNotFragment()306   int SetDoNotFragment() override { return OK; }
SetRecvTos()307   int SetRecvTos() override { return OK; }
SetTos(DiffServCodePoint dscp,EcnCodePoint ecn)308   int SetTos(DiffServCodePoint dscp, EcnCodePoint ecn) override { return OK; }
SetMsgConfirm(bool confirm)309   void SetMsgConfirm(bool confirm) override {}
NetLog() const310   const NetLogWithSource& NetLog() const override { return net_log_; }
GetLastTos() const311   DscpAndEcn GetLastTos() const override { return {DSCP_DEFAULT, ECN_DEFAULT}; }
312 
313  private:
CompleteRecv(CompletionOnceCallback callback,IOBuffer * buffer,int buffer_length,IPEndPoint * out_address)314   void CompleteRecv(CompletionOnceCallback callback,
315                     IOBuffer* buffer,
316                     int buffer_length,
317                     IPEndPoint* out_address) {
318     int rv = GenerateResponse(buffer, buffer_length, out_address);
319     std::move(callback).Run(rv);
320   }
321 
GenerateResponse(IOBuffer * buffer,int buffer_length,IPEndPoint * out_address)322   int GenerateResponse(IOBuffer* buffer,
323                        int buffer_length,
324                        IPEndPoint* out_address) {
325     if (data_provider_->ConsumeBool()) {
326       std::string data =
327           data_provider_->ConsumeRandomLengthString(buffer_length);
328       base::ranges::copy(data, buffer->data());
329       *out_address =
330           IPEndPoint(FuzzIPAddress(data_provider_), FuzzPort(data_provider_));
331       return data.size();
332     }
333 
334     return data_provider_->PickValueInArray(kMdnsErrors);
335   }
336 
CompleteSend(CompletionOnceCallback callback)337   void CompleteSend(CompletionOnceCallback callback) {
338     if (data_provider_->ConsumeBool())
339       std::move(callback).Run(OK);
340     else
341       std::move(callback).Run(data_provider_->PickValueInArray(kMdnsErrors));
342   }
343 
344   const raw_ptr<FuzzedDataProvider> data_provider_;
345   const IPEndPoint local_address_;
346   const NetLogWithSource net_log_;
347 
348   base::WeakPtrFactory<FuzzedMdnsSocket> weak_factory_{this};
349 };
350 
351 class FuzzedMdnsSocketFactory : public MDnsSocketFactory {
352  public:
FuzzedMdnsSocketFactory(FuzzedDataProvider * data_provider)353   explicit FuzzedMdnsSocketFactory(FuzzedDataProvider* data_provider)
354       : data_provider_(data_provider) {}
355 
CreateSockets(std::vector<std::unique_ptr<DatagramServerSocket>> * sockets)356   void CreateSockets(
357       std::vector<std::unique_ptr<DatagramServerSocket>>* sockets) override {
358     int num_sockets = data_provider_->ConsumeIntegralInRange(1, 4);
359     for (int i = 0; i < num_sockets; ++i)
360       sockets->push_back(std::make_unique<FuzzedMdnsSocket>(data_provider_));
361   }
362 
363  private:
364   const raw_ptr<FuzzedDataProvider> data_provider_;
365 };
366 
367 class FuzzedHostResolverManager : public HostResolverManager {
368  public:
369   // |data_provider| and |net_log| must outlive the FuzzedHostResolver.
370   // TODO(crbug.com/971411): Fuzz system DNS config changes through a non-null
371   // SystemDnsConfigChangeNotifier.
FuzzedHostResolverManager(const HostResolver::ManagerOptions & options,NetLog * net_log,FuzzedDataProvider * data_provider)372   FuzzedHostResolverManager(const HostResolver::ManagerOptions& options,
373                             NetLog* net_log,
374                             FuzzedDataProvider* data_provider)
375       : HostResolverManager(options,
376                             nullptr /* system_dns_config_notifier */,
377                             net_log),
378         data_provider_(data_provider),
379         is_globally_reachable_(data_provider->ConsumeBool()),
380         start_globally_reachable_async_(data_provider->ConsumeBool()),
381         socket_factory_(data_provider_),
382         net_log_(net_log),
383         data_provider_weak_factory_(data_provider) {
384     HostResolverSystemTask::Params system_task_params(
385         base::MakeRefCounted<FuzzedHostResolverProc>(
386             data_provider_weak_factory_.GetWeakPtr()),
387         // Retries are only used when the original request hangs, which this
388         // class currently can't simulate.
389         0 /* max_retry_attempts */);
390     set_host_resolver_system_params_for_test(system_task_params);  // IN-TEST
391     SetMdnsSocketFactoryForTesting(
392         std::make_unique<FuzzedMdnsSocketFactory>(data_provider_));
393     std::unique_ptr<DnsClient> dns_client = DnsClient::CreateClientForTesting(
394         net_log_, base::BindRepeating(
395                       &FuzzedDataProvider::ConsumeIntegralInRange<int32_t>,
396                       base::Unretained(data_provider_)));
397     dns_client->SetSystemConfig(GetFuzzedDnsConfig(data_provider_));
398     HostResolverManager::SetDnsClientForTesting(std::move(dns_client));
399   }
400 
401   FuzzedHostResolverManager(const FuzzedHostResolverManager&) = delete;
402   FuzzedHostResolverManager& operator=(const FuzzedHostResolverManager&) =
403       delete;
404 
405   ~FuzzedHostResolverManager() override = default;
406 
SetDnsClientForTesting(std::unique_ptr<DnsClient> dns_client)407   void SetDnsClientForTesting(std::unique_ptr<DnsClient> dns_client) {
408     // The only DnsClient that is supported is the one created by the
409     // FuzzedHostResolverManager since that DnsClient contains the necessary
410     // fuzzing logic.
411     NOTREACHED();
412   }
413 
414  private:
415   // HostResolverManager implementation:
StartGloballyReachableCheck(const IPAddress & dest,const NetLogWithSource & net_log,ClientSocketFactory * client_socket_factory,CompletionOnceCallback callback)416   int StartGloballyReachableCheck(const IPAddress& dest,
417                                   const NetLogWithSource& net_log,
418                                   ClientSocketFactory* client_socket_factory,
419                                   CompletionOnceCallback callback) override {
420     int reachable_rv = is_globally_reachable_ ? OK : ERR_FAILED;
421     if (start_globally_reachable_async_) {
422       base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
423           FROM_HERE, base::BindOnce(std::move(callback), reachable_rv));
424       return ERR_IO_PENDING;
425     }
426     return reachable_rv;
427   }
428 
RunLoopbackProbeJob()429   void RunLoopbackProbeJob() override {
430     SetHaveOnlyLoopbackAddresses(data_provider_->ConsumeBool());
431   }
432 
433   const raw_ptr<FuzzedDataProvider> data_provider_;
434 
435   // Fixed value to be returned by StartGloballyReachableCheck.
436   const bool is_globally_reachable_;
437   // Determines if StartGloballyReachableCheck returns sync or async.
438   const bool start_globally_reachable_async_;
439 
440   // Used for UDP and TCP sockets if the async resolver is enabled.
441   FuzzedSocketFactory socket_factory_;
442 
443   const raw_ptr<NetLog> net_log_;
444 
445   base::WeakPtrFactory<FuzzedDataProvider> data_provider_weak_factory_;
446 };
447 
448 }  // namespace
449 
CreateFuzzedContextHostResolver(const HostResolver::ManagerOptions & options,NetLog * net_log,FuzzedDataProvider * data_provider,bool enable_caching)450 std::unique_ptr<ContextHostResolver> CreateFuzzedContextHostResolver(
451     const HostResolver::ManagerOptions& options,
452     NetLog* net_log,
453     FuzzedDataProvider* data_provider,
454     bool enable_caching) {
455   auto manager = std::make_unique<FuzzedHostResolverManager>(options, net_log,
456                                                              data_provider);
457   auto resolve_context = std::make_unique<ResolveContext>(
458       nullptr /* url_request_context */, enable_caching);
459   return std::make_unique<ContextHostResolver>(std::move(manager),
460                                                std::move(resolve_context));
461 }
462 
463 }  // namespace net
464