1 // Copyright 2023 The gRPC Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include <algorithm>
15 #include <memory>
16 #include <string>
17 #include <type_traits>
18 #include <utility>
19 #include <vector>
20 
21 #include "absl/base/thread_annotations.h"
22 #include "absl/functional/any_invocable.h"
23 #include "absl/status/status.h"
24 #include "absl/status/statusor.h"
25 #include "absl/strings/str_cat.h"
26 #include "absl/strings/string_view.h"
27 
28 #include <grpc/event_engine/event_engine.h>
29 #include <grpc/support/log.h>
30 #include <grpc/support/port_platform.h>
31 
32 #include "src/core/lib/channel/channel_args.h"
33 #include "src/core/lib/event_engine/default_event_engine.h"
34 #include "src/core/lib/event_engine/tcp_socket_utils.h"
35 #include "src/core/lib/experiments/config.h"
36 #include "src/core/lib/gprpp/debug_location.h"
37 #include "src/core/lib/gprpp/orphanable.h"
38 #include "src/core/lib/gprpp/work_serializer.h"
39 #include "src/core/lib/uri/uri_parser.h"
40 #include "src/core/resolver/dns/event_engine/event_engine_client_channel_resolver.h"
41 #include "src/core/resolver/resolver.h"
42 #include "src/core/resolver/resolver_factory.h"
43 #include "src/libfuzzer/libfuzzer_macro.h"
44 #include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h"
45 #include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.pb.h"
46 #include "test/core/event_engine/util/aborting_event_engine.h"
47 #include "test/core/ext/filters/event_engine_client_channel_resolver/resolver_fuzzer.pb.h"
48 #include "test/core/util/fuzz_config_vars.h"
49 #include "test/core/util/fuzzing_channel_args.h"
50 
51 bool squelch = true;
dont_log(gpr_log_func_args *)52 static void dont_log(gpr_log_func_args* /*args*/) {}
53 
54 namespace {
55 
56 using event_engine_client_channel_resolver::ExecutionStep;
57 using event_engine_client_channel_resolver::TXTRecordType;
58 using grpc_core::EventEngineClientChannelDNSResolverFactory;
59 using grpc_event_engine::experimental::EventEngine;
60 using grpc_event_engine::experimental::FuzzingEventEngine;
61 using grpc_event_engine::experimental::URIToResolvedAddress;
62 
63 constexpr char g_grpc_config_prefix[] = "grpc_config=";
64 
ErrorToAbslStatus(const event_engine_client_channel_resolver::Error & error)65 absl::Status ErrorToAbslStatus(
66     const event_engine_client_channel_resolver::Error& error) {
67   // clamp error.code() in (0, 16]
68   return absl::Status(static_cast<absl::StatusCode>(error.code() % 16 + 1),
69                       error.message());
70 }
71 
72 class FuzzingResolverEventEngine
73     : public grpc_event_engine::experimental::AbortingEventEngine,
74       public std::enable_shared_from_this<FuzzingResolverEventEngine> {
75  public:
FuzzingResolverEventEngine(const event_engine_client_channel_resolver::Msg & msg,bool * done_resolving)76   explicit FuzzingResolverEventEngine(
77       const event_engine_client_channel_resolver::Msg& msg,
78       bool* done_resolving)
79       : runner_(FuzzingEventEngine::Options(), fuzzing_event_engine::Actions()),
80         done_resolving_(done_resolving),
81         should_orphan_at_step_(msg.should_orphan_at_step()) {
82     // Set hostname responses
83     if (msg.has_hostname_error()) {
84       hostname_responses_ = ErrorToAbslStatus(msg.hostname_error());
85     } else if (msg.has_hostname_response()) {
86       hostname_responses_.emplace();
87       for (const auto& address : msg.hostname_response().addresses()) {
88         hostname_responses_->emplace_back(*URIToResolvedAddress(
89             absl::StrCat("ipv4:127.0.0.1:", address.port() % 65535)));
90       }
91     }
92     // Set SRV Responses
93     if (msg.has_srv_error()) {
94       srv_responses_ = ErrorToAbslStatus(msg.srv_error());
95     } else if (msg.has_srv_response()) {
96       srv_responses_.emplace();
97       for (const auto& srv_record : msg.srv_response().srv_records()) {
98         EventEngine::DNSResolver::SRVRecord final_r;
99         final_r.host = srv_record.host();
100         final_r.port = srv_record.port();
101         final_r.priority = srv_record.priority();
102         final_r.weight = srv_record.weight();
103         srv_responses_->emplace_back(final_r);
104       }
105     }
106     // Set TXT Responses
107     if (msg.has_txt_error()) {
108       txt_responses_ = ErrorToAbslStatus(msg.txt_error());
109     } else if (msg.has_txt_response()) {
110       txt_responses_.emplace();
111       for (const auto& txt_record : msg.txt_response().txt_records()) {
112         if (txt_record.has_enumerated_value()) {
113           switch (txt_record.enumerated_value()) {
114             case TXTRecordType::TXT_UNDEFINED:
115               break;
116             case TXTRecordType::TXT_VALID:
117               txt_responses_->emplace_back(txt_valid_config_);
118               break;
119             case TXTRecordType::TXT_RANDOM_NON_CONFIG:
120               txt_responses_->emplace_back(txt_record.arbitrary_value());
121               break;
122             case TXTRecordType::TXT_RANDOM_PREFIXED_CONFIG:
123               txt_responses_->emplace_back(absl::StrCat(
124                   g_grpc_config_prefix, txt_record.arbitrary_value()));
125               break;
126             default:
127               //  ignored
128               break;
129           }
130         }
131       }
132     }
133   }
134 
GetDNSResolver(const DNSResolver::ResolverOptions &)135   absl::StatusOr<std::unique_ptr<DNSResolver>> GetDNSResolver(
136       const DNSResolver::ResolverOptions& /* options */) override {
137     return std::make_unique<FuzzingDNSResolver>(this);
138   }
139 
RunAfter(Duration,absl::AnyInvocable<void ()>)140   TaskHandle RunAfter(Duration /* when */,
141                       absl::AnyInvocable<void()> /* closure */) override {
142     return TaskHandle::kInvalid;
143   }
Cancel(TaskHandle)144   bool Cancel(TaskHandle /* handle */) override { return true; }
145 
Run(absl::AnyInvocable<void ()> fn)146   void Run(absl::AnyInvocable<void()> fn) override {
147     runner_.Run(std::move(fn));
148   }
149 
Run(Closure * fn)150   void Run(Closure* fn) override { runner_.Run(fn); }
151 
Tick()152   void Tick() { runner_.Tick(); }
153 
154  private:
155   class FuzzingDNSResolver : public DNSResolver {
156    public:
FuzzingDNSResolver(FuzzingResolverEventEngine * engine)157     explicit FuzzingDNSResolver(FuzzingResolverEventEngine* engine)
158         : engine_(engine) {}
159 
LookupHostname(LookupHostnameCallback on_resolve,absl::string_view,absl::string_view)160     void LookupHostname(LookupHostnameCallback on_resolve,
161                         absl::string_view /* name */,
162                         absl::string_view /* default_port */) override {
163       engine_->CheckAndSetOrphan(ExecutionStep::DURING_LOOKUP_HOSTNAME);
164       if (!engine_->has_been_orphaned_) {
165         engine_->runner_.Run(
166             [engine = engine_, cb = std::move(on_resolve)]() mutable {
167               engine->CheckAndSetOrphan(
168                   ExecutionStep::AFTER_LOOKUP_HOSTNAME_CALLBACK);
169               cb(engine->hostname_responses_);
170             });
171       }
172     }
LookupSRV(LookupSRVCallback on_resolve,absl::string_view)173     void LookupSRV(LookupSRVCallback on_resolve,
174                    absl::string_view /* name */) override {
175       engine_->CheckAndSetOrphan(ExecutionStep::DURING_LOOKUP_SRV);
176       if (!engine_->has_been_orphaned_) {
177         engine_->runner_.Run([engine = engine_,
178                               cb = std::move(on_resolve)]() mutable {
179           engine->CheckAndSetOrphan(ExecutionStep::AFTER_LOOKUP_SRV_CALLBACK);
180           cb(engine->srv_responses_);
181         });
182       }
183     }
LookupTXT(LookupTXTCallback on_resolve,absl::string_view)184     void LookupTXT(LookupTXTCallback on_resolve,
185                    absl::string_view /* name */) override {
186       engine_->CheckAndSetOrphan(ExecutionStep::DURING_LOOKUP_TXT);
187       if (!engine_->has_been_orphaned_) {
188         engine_->runner_.Run([engine = engine_,
189                               cb = std::move(on_resolve)]() mutable {
190           engine->CheckAndSetOrphan(ExecutionStep::AFTER_LOOKUP_TXT_CALLBACK);
191           cb(engine->txt_responses_);
192         });
193       }
194     }
195 
196    private:
197     FuzzingResolverEventEngine* engine_;
198   };
199 
CheckAndSetOrphan(ExecutionStep current_execution_step)200   void CheckAndSetOrphan(ExecutionStep current_execution_step) {
201     if (should_orphan_at_step_ == current_execution_step) {
202       *done_resolving_ = true;
203       has_been_orphaned_ = true;
204     }
205   }
206 
207   // members
208   FuzzingEventEngine runner_;
209   bool* done_resolving_;
210   ExecutionStep should_orphan_at_step_;
211   bool has_been_orphaned_ = false;
212 
213   // responses
214   absl::StatusOr<std::vector<EventEngine::ResolvedAddress>> hostname_responses_;
215   absl::StatusOr<std::vector<EventEngine::DNSResolver::SRVRecord>>
216       srv_responses_;
217   absl::StatusOr<std::vector<std::string>> txt_responses_;
218 
219   // base case for a valid gRPC config
220   const std::string txt_valid_config_ =
221       "grpc_config=[{\"serviceConfig\":{\"loadBalancingPolicy\":\"round_"
222       "robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":"
223       "\"SimpleService\"}],\"waitForReady\":true}]}}]";
224 };
225 
226 class FuzzingResultHandler : public grpc_core::Resolver::ResultHandler {
227  public:
FuzzingResultHandler(bool * done_resolving)228   explicit FuzzingResultHandler(bool* done_resolving)
229       : done_resolving_(done_resolving) {}
ReportResult(grpc_core::Resolver::Result)230   void ReportResult(grpc_core::Resolver::Result /* result */) override {
231     *done_resolving_ = true;
232   }
233 
234  private:
235   bool* done_resolving_;
236 };
237 
ConstructResolverArgs(const grpc_core::ChannelArgs & channel_args,bool * done_resolving,std::shared_ptr<grpc_core::WorkSerializer> work_serializer)238 grpc_core::ResolverArgs ConstructResolverArgs(
239     const grpc_core::ChannelArgs& channel_args, bool* done_resolving,
240     std::shared_ptr<grpc_core::WorkSerializer> work_serializer) {
241   grpc_core::ResolverArgs resolver_args;
242   auto uri = grpc_core::URI::Parse("dns:localhost");
243   GPR_ASSERT(uri.ok());
244   resolver_args.uri = *uri;
245   resolver_args.args = channel_args;
246   resolver_args.pollset_set = nullptr;
247   resolver_args.work_serializer = std::move(work_serializer);
248   auto result_handler = std::make_unique<FuzzingResultHandler>(done_resolving);
249   resolver_args.result_handler = std::move(result_handler);
250   return resolver_args;
251 }
252 
253 }  // namespace
254 
DEFINE_PROTO_FUZZER(const event_engine_client_channel_resolver::Msg & msg)255 DEFINE_PROTO_FUZZER(const event_engine_client_channel_resolver::Msg& msg) {
256   if (squelch) gpr_set_log_function(dont_log);
257   bool done_resolving = false;
258   grpc_core::ApplyFuzzConfigVars(msg.config_vars());
259   grpc_core::TestOnlyReloadExperimentsFromConfigVariables();
260   grpc_event_engine::experimental::SetEventEngineFactory([msg,
261                                                           &done_resolving]() {
262     return std::make_unique<FuzzingResolverEventEngine>(msg, &done_resolving);
263   });
264   auto engine = std::static_pointer_cast<FuzzingResolverEventEngine>(
265       grpc_event_engine::experimental::GetDefaultEventEngine());
266   {
267     // scoped to ensure the resolver is orphaned when done resolving.
268     auto work_serializer = std::make_shared<grpc_core::WorkSerializer>(engine);
269     EventEngineClientChannelDNSResolverFactory resolver_factory;
270     auto resolver_args = ConstructResolverArgs(
271         grpc_core::testing::CreateChannelArgsFromFuzzingConfiguration(
272             msg.channel_args(), {})
273             .Set(GRPC_INTERNAL_ARG_EVENT_ENGINE, engine),
274         &done_resolving, work_serializer);
275     auto resolver = resolver_factory.CreateResolver(std::move(resolver_args));
276     work_serializer->Run(
277         [resolver_ptr = resolver.get()]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(
278             *work_serializer) { resolver_ptr->StartLocked(); },
279         DEBUG_LOCATION);
280     // wait for result (no need to check validity)
281     while (!done_resolving) {
282       engine->Tick();
283     }
284   }
285   // If orphaned early, callbacks may still need to run, which may keep the
286   // resolver alive.
287   while (engine.use_count() > 1) engine->Tick();
288 }
289