• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 //
3 // Copyright 2016 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include <grpc/credentials.h>
20 #include <grpc/event_engine/event_engine.h>
21 #include <grpc/grpc.h>
22 #include <grpc/grpc_security.h>
23 #include <grpc/support/alloc.h>
24 #include <grpc/support/string_util.h>
25 #include <string.h>
26 
27 #include <algorithm>
28 #include <atomic>
29 #include <functional>
30 #include <initializer_list>
31 #include <memory>
32 #include <string>
33 #include <utility>
34 #include <vector>
35 
36 #include "absl/log/check.h"
37 #include "absl/status/status.h"
38 #include "absl/status/statusor.h"
39 #include "absl/strings/str_join.h"
40 #include "absl/strings/str_split.h"
41 #include "absl/strings/string_view.h"
42 #include "absl/time/clock.h"
43 #include "absl/time/time.h"
44 #include "absl/types/optional.h"
45 #include "src/core/ext/transport/inproc/inproc_transport.h"
46 #include "src/core/lib/address_utils/parse_address.h"
47 #include "src/core/lib/channel/channel_args.h"
48 #include "src/core/lib/event_engine/default_event_engine.h"
49 #include "src/core/lib/experiments/config.h"
50 #include "src/core/lib/iomgr/closure.h"
51 #include "src/core/lib/iomgr/exec_ctx.h"
52 #include "src/core/lib/iomgr/iomgr_fwd.h"
53 #include "src/core/lib/iomgr/resolve_address.h"
54 #include "src/core/lib/iomgr/resolved_address.h"
55 #include "src/core/resolver/dns/c_ares/grpc_ares_wrapper.h"
56 #include "src/core/resolver/endpoint_addresses.h"
57 #include "src/core/util/debug_location.h"
58 #include "src/core/util/env.h"
59 #include "src/core/util/ref_counted_ptr.h"
60 #include "src/core/util/time.h"
61 #include "src/libfuzzer/libfuzzer_macro.h"
62 #include "test/core/end2end/data/ssl_test_data.h"
63 #include "test/core/end2end/fuzzers/api_fuzzer.pb.h"
64 #include "test/core/end2end/fuzzers/fuzzing_common.h"
65 #include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h"
66 #include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.pb.h"
67 #include "test/core/test_util/fuzz_config_vars.h"
68 #include "test/core/test_util/fuzzing_channel_args.h"
69 #include "test/core/test_util/test_config.h"
70 
71 // IWYU pragma: no_include <google/protobuf/repeated_ptr_field.h>
72 
73 ////////////////////////////////////////////////////////////////////////////////
74 // logging
75 
76 bool squelch = true;
77 bool leak_check = true;
78 
79 ////////////////////////////////////////////////////////////////////////////////
80 // dns resolution
81 
82 typedef struct addr_req {
83   char* addr;
84   grpc_closure* on_done;
85   std::unique_ptr<grpc_core::EndpointAddressesList>* addresses;
86 } addr_req;
87 
finish_resolve(addr_req r)88 static void finish_resolve(addr_req r) {
89   if (0 == strcmp(r.addr, "server")) {
90     *r.addresses = std::make_unique<grpc_core::EndpointAddressesList>();
91     grpc_resolved_address fake_resolved_address;
92     CHECK(grpc_parse_ipv4_hostport("1.2.3.4:5", &fake_resolved_address, false));
93     (*r.addresses)
94         ->emplace_back(fake_resolved_address, grpc_core::ChannelArgs());
95     grpc_core::ExecCtx::Run(DEBUG_LOCATION, r.on_done, absl::OkStatus());
96   } else {
97     grpc_core::ExecCtx::Run(DEBUG_LOCATION, r.on_done,
98                             absl::UnknownError("Resolution failed"));
99   }
100   gpr_free(r.addr);
101 }
102 
103 namespace {
104 
105 using grpc_event_engine::experimental::FuzzingEventEngine;
106 using grpc_event_engine::experimental::GetDefaultEventEngine;
107 
108 class FuzzerDNSResolver : public grpc_core::DNSResolver {
109  public:
110   class FuzzerDNSRequest {
111    public:
FuzzerDNSRequest(absl::string_view name,std::function<void (absl::StatusOr<std::vector<grpc_resolved_address>>)> on_done)112     FuzzerDNSRequest(
113         absl::string_view name,
114         std::function<void(absl::StatusOr<std::vector<grpc_resolved_address>>)>
115             on_done)
116         : name_(std::string(name)), on_done_(std::move(on_done)) {
117       GetDefaultEventEngine()->RunAfter(
118           grpc_core::Duration::Seconds(1), [this] {
119             grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
120             grpc_core::ExecCtx exec_ctx;
121             FinishResolve();
122           });
123     }
124 
125    private:
FinishResolve()126     void FinishResolve() {
127       if (name_ == "server") {
128         std::vector<grpc_resolved_address> addrs;
129         grpc_resolved_address addr;
130         memset(&addr, 0, sizeof(addr));
131         addrs.push_back(addr);
132         on_done_(std::move(addrs));
133       } else {
134         on_done_(absl::UnknownError("Resolution failed"));
135       }
136       delete this;
137     }
138 
139     const std::string name_;
140     const std::function<void(
141         absl::StatusOr<std::vector<grpc_resolved_address>>)>
142         on_done_;
143   };
144 
FuzzerDNSResolver(FuzzingEventEngine * engine)145   explicit FuzzerDNSResolver(FuzzingEventEngine* engine) : engine_(engine) {}
146 
LookupHostname(std::function<void (absl::StatusOr<std::vector<grpc_resolved_address>>)> on_resolved,absl::string_view name,absl::string_view,grpc_core::Duration,grpc_pollset_set *,absl::string_view)147   TaskHandle LookupHostname(
148       std::function<void(absl::StatusOr<std::vector<grpc_resolved_address>>)>
149           on_resolved,
150       absl::string_view name, absl::string_view /* default_port */,
151       grpc_core::Duration /* timeout */,
152       grpc_pollset_set* /* interested_parties */,
153       absl::string_view /* name_server */) override {
154     new FuzzerDNSRequest(name, std::move(on_resolved));
155     return kNullHandle;
156   }
157 
LookupHostnameBlocking(absl::string_view name,absl::string_view default_port)158   absl::StatusOr<std::vector<grpc_resolved_address>> LookupHostnameBlocking(
159       absl::string_view name, absl::string_view default_port) override {
160     // To mimic the resolution delay
161     absl::SleepFor(absl::Seconds(1));
162     if (name == "server") {
163       std::vector<grpc_resolved_address> addrs;
164       grpc_resolved_address addr;
165       memset(&addr, 0, sizeof(addr));
166       addrs.push_back(addr);
167       return addrs;
168     } else {
169       return absl::UnknownError("Resolution failed");
170     }
171   }
172 
LookupSRV(std::function<void (absl::StatusOr<std::vector<grpc_resolved_address>>)> on_resolved,absl::string_view,grpc_core::Duration,grpc_pollset_set *,absl::string_view)173   TaskHandle LookupSRV(
174       std::function<void(absl::StatusOr<std::vector<grpc_resolved_address>>)>
175           on_resolved,
176       absl::string_view /* name */, grpc_core::Duration /* timeout */,
177       grpc_pollset_set* /* interested_parties */,
178       absl::string_view /* name_server */) override {
179     engine_->Run([on_resolved] {
180       grpc_core::ApplicationCallbackExecCtx app_exec_ctx;
181       grpc_core::ExecCtx exec_ctx;
182       on_resolved(absl::UnimplementedError(
183           "The Fuzzing DNS resolver does not support looking up SRV records"));
184     });
185     return {-1, -1};
186   };
187 
LookupTXT(std::function<void (absl::StatusOr<std::string>)> on_resolved,absl::string_view,grpc_core::Duration,grpc_pollset_set *,absl::string_view)188   TaskHandle LookupTXT(
189       std::function<void(absl::StatusOr<std::string>)> on_resolved,
190       absl::string_view /* name */, grpc_core::Duration /* timeout */,
191       grpc_pollset_set* /* interested_parties */,
192       absl::string_view /* name_server */) override {
193     // Not supported
194     engine_->Run([on_resolved] {
195       grpc_core::ApplicationCallbackExecCtx app_exec_ctx;
196       grpc_core::ExecCtx exec_ctx;
197       on_resolved(absl::UnimplementedError(
198           "The Fuzing DNS resolver does not support looking up TXT records"));
199     });
200     return {-1, -1};
201   };
202 
203   // FuzzerDNSResolver does not support cancellation.
Cancel(TaskHandle)204   bool Cancel(TaskHandle /*handle*/) override { return false; }
205 
206  private:
207   FuzzingEventEngine* engine_;
208 };
209 
210 }  // namespace
211 
my_dns_lookup_hostname_ares(const char *,const char * addr,const char *,grpc_pollset_set *,grpc_closure * on_done,std::unique_ptr<grpc_core::EndpointAddressesList> * addresses,int)212 grpc_ares_request* my_dns_lookup_hostname_ares(
213     const char* /*dns_server*/, const char* addr, const char* /*default_port*/,
214     grpc_pollset_set* /*interested_parties*/, grpc_closure* on_done,
215     std::unique_ptr<grpc_core::EndpointAddressesList>* addresses,
216     int /*query_timeout*/) {
217   addr_req r;
218   r.addr = gpr_strdup(addr);
219   r.on_done = on_done;
220   r.addresses = addresses;
221   GetDefaultEventEngine()->RunAfter(grpc_core::Duration::Seconds(1), [r] {
222     grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
223     grpc_core::ExecCtx exec_ctx;
224     finish_resolve(r);
225   });
226   return nullptr;
227 }
228 
my_dns_lookup_srv_ares(const char *,const char * name,grpc_pollset_set *,grpc_closure * on_done,std::unique_ptr<grpc_core::EndpointAddressesList> * balancer_addresses,int)229 grpc_ares_request* my_dns_lookup_srv_ares(
230     const char* /*dns_server*/, const char* name,
231     grpc_pollset_set* /*interested_parties*/, grpc_closure* on_done,
232     std::unique_ptr<grpc_core::EndpointAddressesList>* balancer_addresses,
233     int /*query_timeout*/) {
234   addr_req r;
235   r.addr = gpr_strdup(name);
236   r.on_done = on_done;
237   r.addresses = balancer_addresses;
238   GetDefaultEventEngine()->RunAfter(grpc_core::Duration::Seconds(1), [r] {
239     grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
240     grpc_core::ExecCtx exec_ctx;
241     finish_resolve(r);
242   });
243   return nullptr;
244 }
245 
my_cancel_ares_request(grpc_ares_request * request)246 static void my_cancel_ares_request(grpc_ares_request* request) {
247   CHECK_NE(request, nullptr);
248 }
249 
250 ////////////////////////////////////////////////////////////////////////////////
251 // globals
252 
253 namespace grpc_core {
254 namespace testing {
255 
256 class ApiFuzzer final : public BasicFuzzer {
257  public:
258   explicit ApiFuzzer(const fuzzing_event_engine::Actions& actions);
259   ~ApiFuzzer();
260   void Tick() override;
Server()261   grpc_server* Server() { return server_; }
262 
263  private:
264   Result CreateChannel(
265       const api_fuzzer::CreateChannel& create_channel) override;
266 
267   Result CreateServer(const api_fuzzer::CreateServer& create_server) override;
268   void DestroyServer() override;
269   void DestroyChannel() override;
270 
server()271   grpc_server* server() override { return server_; }
channel()272   grpc_channel* channel() override { return channel_; }
273 
274   grpc_server* server_ = nullptr;
275   grpc_channel* channel_ = nullptr;
276   std::atomic<bool> channel_force_delete_{false};
277 };
278 
279 }  // namespace testing
280 }  // namespace grpc_core
281 
282 ////////////////////////////////////////////////////////////////////////////////
283 // test driver
284 
ReadCredArtifact(const api_fuzzer::CredArtifact & artifact,std::initializer_list<const char * > builtins)285 static const char* ReadCredArtifact(
286     const api_fuzzer::CredArtifact& artifact,
287     std::initializer_list<const char*> builtins) {
288   switch (artifact.type_case()) {
289     case api_fuzzer::CredArtifact::kCustom:
290       return artifact.custom().c_str();
291     case api_fuzzer::CredArtifact::kBuiltin:
292       if (artifact.builtin() < 0) return nullptr;
293       if (artifact.builtin() < static_cast<int>(builtins.size())) {
294         return *(builtins.begin() + artifact.builtin());
295       }
296       return nullptr;
297     case api_fuzzer::CredArtifact::TYPE_NOT_SET:
298       return nullptr;
299   }
300 }
301 
ReadSslChannelCreds(const api_fuzzer::SslChannelCreds & creds)302 static grpc_channel_credentials* ReadSslChannelCreds(
303     const api_fuzzer::SslChannelCreds& creds) {
304   const char* root_certs =
305       creds.has_root_certs()
306           ? ReadCredArtifact(creds.root_certs(), {test_root_cert})
307           : nullptr;
308   const char* private_key =
309       creds.has_private_key()
310           ? ReadCredArtifact(creds.private_key(),
311                              {test_server1_key, test_self_signed_client_key,
312                               test_signed_client_key})
313           : nullptr;
314   const char* certs =
315       creds.has_certs()
316           ? ReadCredArtifact(creds.certs(),
317                              {test_server1_cert, test_self_signed_client_cert,
318                               test_signed_client_cert})
319           : nullptr;
320   grpc_ssl_pem_key_cert_pair key_cert_pair = {private_key, certs};
321   return grpc_ssl_credentials_create(
322       root_certs,
323       private_key != nullptr && certs != nullptr ? &key_cert_pair : nullptr,
324       nullptr, nullptr);
325 }
326 
ReadCallCreds(const api_fuzzer::CallCreds & creds)327 static grpc_call_credentials* ReadCallCreds(
328     const api_fuzzer::CallCreds& creds) {
329   switch (creds.type_case()) {
330     case api_fuzzer::CallCreds::TYPE_NOT_SET:
331       return nullptr;
332     case api_fuzzer::CallCreds::kNull:
333       return nullptr;
334     case api_fuzzer::CallCreds::kCompositeCallCreds: {
335       grpc_call_credentials* out = nullptr;
336       for (const auto& child_creds :
337            creds.composite_call_creds().call_creds()) {
338         grpc_call_credentials* child = ReadCallCreds(child_creds);
339         if (child != nullptr) {
340           if (out == nullptr) {
341             out = child;
342           } else {
343             auto* composed =
344                 grpc_composite_call_credentials_create(out, child, nullptr);
345             grpc_call_credentials_release(child);
346             grpc_call_credentials_release(out);
347             out = composed;
348           }
349         }
350       }
351       return out;
352     }
353     case api_fuzzer::CallCreds::kAccessToken:
354       return grpc_access_token_credentials_create(creds.access_token().c_str(),
355                                                   nullptr);
356     case api_fuzzer::CallCreds::kIam:
357       return grpc_google_iam_credentials_create(
358           creds.iam().auth_token().c_str(), creds.iam().auth_selector().c_str(),
359           nullptr);
360       // TODO(ctiller): more cred types here
361   }
362 }
363 
ReadChannelCreds(const api_fuzzer::ChannelCreds & creds)364 static grpc_channel_credentials* ReadChannelCreds(
365     const api_fuzzer::ChannelCreds& creds) {
366   switch (creds.type_case()) {
367     case api_fuzzer::ChannelCreds::TYPE_NOT_SET:
368       return nullptr;
369     case api_fuzzer::ChannelCreds::kSslChannelCreds:
370       return ReadSslChannelCreds(creds.ssl_channel_creds());
371     case api_fuzzer::ChannelCreds::kCompositeChannelCreds: {
372       const auto& comp = creds.composite_channel_creds();
373       grpc_channel_credentials* c1 =
374           comp.has_channel_creds() ? ReadChannelCreds(comp.channel_creds())
375                                    : nullptr;
376       grpc_call_credentials* c2 =
377           comp.has_call_creds() ? ReadCallCreds(comp.call_creds()) : nullptr;
378       if (c1 != nullptr && c2 != nullptr) {
379         grpc_channel_credentials* out =
380             grpc_composite_channel_credentials_create(c1, c2, nullptr);
381         grpc_channel_credentials_release(c1);
382         grpc_call_credentials_release(c2);
383         return out;
384       } else if (c1 != nullptr) {
385         return c1;
386       } else if (c2 != nullptr) {
387         grpc_call_credentials_release(c2);
388         return nullptr;
389       } else {
390         return nullptr;
391       }
392       GPR_UNREACHABLE_CODE(return nullptr);
393     }
394     case api_fuzzer::ChannelCreds::kNull:
395       return nullptr;
396   }
397 }
398 
ReadServerCreds(const api_fuzzer::ServerCreds & creds)399 static grpc_server_credentials* ReadServerCreds(
400     const api_fuzzer::ServerCreds& creds) {
401   switch (creds.type_case()) {
402     case api_fuzzer::ServerCreds::TYPE_NOT_SET:
403       return nullptr;
404     case api_fuzzer::ServerCreds::kInsecureCreds:
405       return grpc_insecure_server_credentials_create();
406     case api_fuzzer::ServerCreds::kNull:
407       return nullptr;
408   }
409 }
410 
411 namespace grpc_core {
412 namespace testing {
413 
ApiFuzzer(const fuzzing_event_engine::Actions & actions)414 ApiFuzzer::ApiFuzzer(const fuzzing_event_engine::Actions& actions)
415     : BasicFuzzer(actions) {
416   ResetDNSResolver(std::make_unique<FuzzerDNSResolver>(engine().get()));
417   grpc_dns_lookup_hostname_ares = my_dns_lookup_hostname_ares;
418   grpc_dns_lookup_srv_ares = my_dns_lookup_srv_ares;
419   grpc_cancel_ares_request = my_cancel_ares_request;
420 
421   CHECK_EQ(channel_, nullptr);
422   CHECK_EQ(server_, nullptr);
423 }
424 
~ApiFuzzer()425 ApiFuzzer::~ApiFuzzer() {
426   CHECK_EQ(channel_, nullptr);
427   CHECK_EQ(server_, nullptr);
428 }
429 
Tick()430 void ApiFuzzer::Tick() {
431   BasicFuzzer::Tick();
432   if (channel_force_delete_.exchange(false) && channel_) {
433     grpc_channel_destroy(channel_);
434     channel_ = nullptr;
435   }
436 }
437 
438 namespace {
439 
440 // If there are more than 1K comma-delimited strings in target, remove
441 // the extra ones.
SanitizeTargetUri(absl::string_view target)442 std::string SanitizeTargetUri(absl::string_view target) {
443   constexpr size_t kMaxCommaDelimitedStrings = 1000;
444   std::vector<absl::string_view> parts = absl::StrSplit(target, ',');
445   if (parts.size() > kMaxCommaDelimitedStrings) {
446     parts.resize(kMaxCommaDelimitedStrings);
447   }
448   return absl::StrJoin(parts, ",");
449 }
450 
451 }  // namespace
452 
CreateChannel(const api_fuzzer::CreateChannel & create_channel)453 ApiFuzzer::Result ApiFuzzer::CreateChannel(
454     const api_fuzzer::CreateChannel& create_channel) {
455   if (channel_ != nullptr) return Result::kComplete;
456   // ExecCtx is needed for ChannelArgs destruction.
457   ExecCtx exec_ctx;
458   testing::FuzzingEnvironment fuzzing_env;
459   fuzzing_env.resource_quota = resource_quota();
460   ChannelArgs args = testing::CreateChannelArgsFromFuzzingConfiguration(
461       create_channel.channel_args(), fuzzing_env);
462   if (create_channel.inproc()) {
463     if (server_ == nullptr) return Result::kFailed;
464     channel_ = grpc_inproc_channel_create(server_, args.ToC().get(), nullptr);
465   } else {
466     grpc_channel_credentials* creds =
467         create_channel.has_channel_creds()
468             ? ReadChannelCreds(create_channel.channel_creds())
469             : grpc_insecure_credentials_create();
470     channel_ =
471         grpc_channel_create(SanitizeTargetUri(create_channel.target()).c_str(),
472                             creds, args.ToC().get());
473     grpc_channel_credentials_release(creds);
474   }
475   CHECK_NE(channel_, nullptr);
476   channel_force_delete_ = false;
477   return Result::kComplete;
478 }
479 
CreateServer(const api_fuzzer::CreateServer & create_server)480 ApiFuzzer::Result ApiFuzzer::CreateServer(
481     const api_fuzzer::CreateServer& create_server) {
482   if (server_ == nullptr) {
483     // ExecCtx is needed for ChannelArgs destruction.
484     ExecCtx exec_ctx;
485     testing::FuzzingEnvironment fuzzing_env;
486     fuzzing_env.resource_quota = resource_quota();
487     ChannelArgs args = testing::CreateChannelArgsFromFuzzingConfiguration(
488         create_server.channel_args(), fuzzing_env);
489     server_ = grpc_server_create(args.ToC().get(), nullptr);
490     CHECK_NE(server_, nullptr);
491     grpc_server_register_completion_queue(server_, cq(), nullptr);
492     for (const auto& http2_port : create_server.http2_ports()) {
493       auto* creds = ReadServerCreds(http2_port.server_creds());
494       auto addr = absl::StrCat("localhost:", http2_port.port());
495       grpc_server_add_http2_port(server_, addr.c_str(), creds);
496       grpc_server_credentials_release(creds);
497     }
498     grpc_server_start(server_);
499     ResetServerState();
500   } else {
501     return Result::kFailed;
502   }
503   return Result::kComplete;
504 }
505 
DestroyServer()506 void ApiFuzzer::DestroyServer() {
507   grpc_server_destroy(server_);
508   server_ = nullptr;
509 }
510 
DestroyChannel()511 void ApiFuzzer::DestroyChannel() {
512   grpc_channel_destroy(channel_);
513   channel_ = nullptr;
514 }
515 
516 }  // namespace testing
517 }  // namespace grpc_core
518 
519 using grpc_core::testing::ApiFuzzer;
520 
DEFINE_PROTO_FUZZER(const api_fuzzer::Msg & msg)521 DEFINE_PROTO_FUZZER(const api_fuzzer::Msg& msg) {
522   if (squelch && !grpc_core::GetEnv("GRPC_TRACE_FUZZER").has_value()) {
523     grpc_disable_all_absl_logs();
524   }
525   grpc_core::ApplyFuzzConfigVars(msg.config_vars());
526   grpc_core::TestOnlyReloadExperimentsFromConfigVariables();
527   ApiFuzzer(msg.event_engine_actions()).Run(msg.actions());
528 }
529