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