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