// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include #include #include "base/bind.h" #include "base/check_op.h" #include "base/run_loop.h" #include "base/test/task_environment.h" #include "net/base/address_family.h" #include "net/base/address_list.h" #include "net/base/net_errors.h" #include "net/base/network_isolation_key.h" #include "net/base/request_priority.h" #include "net/dns/context_host_resolver.h" #include "net/dns/fuzzed_host_resolver_util.h" #include "net/dns/host_resolver.h" #include "net/dns/public/dns_query_type.h" #include "net/dns/public/host_resolver_source.h" #include "net/log/net_log.h" #include "net/log/net_log_with_source.h" #include "net/log/test_net_log.h" #include "net/net_buildflags.h" namespace { const char* kHostNames[] = {"foo", "foo.com", "a.foo.com", "bar", "localhost", "localhost6"}; class DnsRequest { public: DnsRequest(net::HostResolver* host_resolver, FuzzedDataProvider* data_provider, std::vector>* dns_requests) : host_resolver_(host_resolver), data_provider_(data_provider), dns_requests_(dns_requests) {} DnsRequest(const DnsRequest&) = delete; DnsRequest& operator=(const DnsRequest&) = delete; ~DnsRequest() = default; // Creates and starts a DNS request using fuzzed parameters. If the request // doesn't complete synchronously, adds it to |dns_requests|. static void CreateRequest( net::HostResolver* host_resolver, FuzzedDataProvider* data_provider, std::vector>* dns_requests) { auto dns_request = std::make_unique( host_resolver, data_provider, dns_requests); if (dns_request->Start() == net::ERR_IO_PENDING) dns_requests->push_back(std::move(dns_request)); } // If |dns_requests| is non-empty, waits for a randomly chosen one of the // requests to complete and removes it from |dns_requests|. static void WaitForRequestComplete( FuzzedDataProvider* data_provider, std::vector>* dns_requests) { if (dns_requests->empty()) return; uint32_t index = data_provider->ConsumeIntegralInRange( 0, dns_requests->size() - 1); // Remove the request from the list before waiting on it - this prevents one // of the other callbacks from deleting the callback being waited on. std::unique_ptr request = std::move((*dns_requests)[index]); dns_requests->erase(dns_requests->begin() + index); request->WaitUntilDone(); } // If |dns_requests| is non-empty, attempts to cancel a randomly chosen one of // them and removes it from |dns_requests|. If the one it picks is already // complete, just removes it from the list. static void CancelRequest( net::HostResolver* host_resolver, FuzzedDataProvider* data_provider, std::vector>* dns_requests) { if (dns_requests->empty()) return; uint32_t index = data_provider->ConsumeIntegralInRange( 0, dns_requests->size() - 1); auto request = dns_requests->begin() + index; (*request)->Cancel(); dns_requests->erase(request); } private: void OnCallback(int result) { CHECK_NE(net::ERR_IO_PENDING, result); request_.reset(); // Remove |this| from |dns_requests| and take ownership of it, if it wasn't // already removed from the vector. It may have been removed if this is in a // WaitForRequest call, in which case, do nothing. std::unique_ptr self; for (auto request = dns_requests_->begin(); request != dns_requests_->end(); ++request) { if (request->get() != this) continue; self = std::move(*request); dns_requests_->erase(request); break; } while (true) { bool done = false; switch (data_provider_->ConsumeIntegralInRange(0, 2)) { case 0: // Quit on 0, or when no data is left. done = true; break; case 1: CreateRequest(host_resolver_, data_provider_, dns_requests_); break; case 2: CancelRequest(host_resolver_, data_provider_, dns_requests_); break; } if (done) break; } if (run_loop_) run_loop_->Quit(); } // Starts the DNS request, using a fuzzed set of parameters. int Start() { net::HostResolver::ResolveHostParameters parameters; auto* query_types_it = net::kDnsQueryTypes.cbegin(); std::advance(query_types_it, data_provider_->ConsumeIntegralInRange( 0, net::kDnsQueryTypes.size() - 1)); parameters.dns_query_type = query_types_it->first; parameters.initial_priority = static_cast( data_provider_->ConsumeIntegralInRange(net::MINIMUM_PRIORITY, net::MAXIMUM_PRIORITY)); parameters.source = data_provider_->PickValueInArray(net::kHostResolverSources); #if !BUILDFLAG(ENABLE_MDNS) while (parameters.source == net::HostResolverSource::MULTICAST_DNS) { parameters.source = data_provider_->PickValueInArray(net::kHostResolverSources); } #endif // !BUILDFLAG(ENABLE_MDNS) parameters.cache_usage = data_provider_->ConsumeBool() ? net::HostResolver::ResolveHostParameters::CacheUsage::ALLOWED : net::HostResolver::ResolveHostParameters::CacheUsage::DISALLOWED; // `include_canonical_name` only allowed for address queries and only when // the system resolver can be used. if (net::IsAddressType(parameters.dns_query_type) && parameters.source != net::HostResolverSource::DNS && parameters.source != net::HostResolverSource::MULTICAST_DNS) { parameters.include_canonical_name = data_provider_->ConsumeBool(); } if (!IsParameterCombinationAllowed(parameters)) { return net::ERR_FAILED; } const char* hostname = data_provider_->PickValueInArray(kHostNames); request_ = host_resolver_->CreateRequest( net::HostPortPair(hostname, 80), net::NetworkIsolationKey(), net::NetLogWithSource(), parameters); int rv = request_->Start( base::BindOnce(&DnsRequest::OnCallback, base::Unretained(this))); if (rv != net::ERR_IO_PENDING) request_.reset(); return rv; } // Waits until the request is done, if it isn't done already. void WaitUntilDone() { CHECK(!run_loop_); if (request_) { run_loop_ = std::make_unique(); run_loop_->Run(); run_loop_.reset(); } } // Some combinations of request parameters are disallowed and expected to // DCHECK. Returns whether or not |parameters| represents one of those cases. static bool IsParameterCombinationAllowed( net::HostResolver::ResolveHostParameters parameters) { // SYSTEM requests only support address types. if (parameters.source == net::HostResolverSource::SYSTEM && !net::IsAddressType(parameters.dns_query_type)) { return false; } // Multiple parameters disallowed for mDNS requests. if (parameters.source == net::HostResolverSource::MULTICAST_DNS && (parameters.include_canonical_name || parameters.loopback_only || parameters.cache_usage != net::HostResolver::ResolveHostParameters::CacheUsage::ALLOWED)) { return false; } return true; } // Cancel the request, if not already completed. Otherwise, does nothing. void Cancel() { request_.reset(); } net::HostResolver* host_resolver_; FuzzedDataProvider* data_provider_; std::vector>* dns_requests_; // Non-null only while running. std::unique_ptr request_; net::AddressList address_list_; std::unique_ptr run_loop_; }; } // namespace // Fuzzer for HostResolverImpl. Fuzzes using both the system resolver and // built-in DNS client paths. // // TODO(mmenke): Add coverage for things this does not cover: // * Out of order completion, particularly for the platform resolver path. // * Simulate network changes, including both enabling and disabling the // async resolver while lookups are active as a result of the change. extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { { FuzzedDataProvider data_provider(data, size); // Including an observer; even though the recorded results aren't currently // used, it'll ensure the netlogging code is fuzzed as well. net::RecordingNetLogObserver net_log_observer; net::HostResolver::ManagerOptions options; options.max_concurrent_resolves = data_provider.ConsumeIntegralInRange(1, 8); options.insecure_dns_client_enabled = data_provider.ConsumeBool(); bool enable_caching = data_provider.ConsumeBool(); std::unique_ptr host_resolver = net::CreateFuzzedContextHostResolver(options, net::NetLog::Get(), &data_provider, enable_caching); std::vector> dns_requests; bool done = false; while (!done) { switch (data_provider.ConsumeIntegralInRange(0, 3)) { case 0: // Quit on 0, or when no data is left. done = true; break; case 1: DnsRequest::CreateRequest(host_resolver.get(), &data_provider, &dns_requests); break; case 2: DnsRequest::WaitForRequestComplete(&data_provider, &dns_requests); break; case 3: DnsRequest::CancelRequest(host_resolver.get(), &data_provider, &dns_requests); break; } } } // Clean up any pending tasks, after deleting everything. base::RunLoop().RunUntilIdle(); return 0; }