• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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 
15 #include <stdint.h>
16 #include <sys/types.h>
17 
18 #include <functional>
19 #include <limits>
20 #include <map>
21 #include <memory>
22 #include <utility>
23 
24 #include "absl/status/status.h"
25 #include "absl/strings/str_cat.h"
26 #include "absl/types/optional.h"
27 
28 #include <grpc/event_engine/memory_allocator.h>
29 #include <grpc/event_engine/memory_request.h>
30 #include <grpc/support/log.h>
31 
32 #include "src/core/lib/debug/trace.h"
33 #include "src/core/lib/experiments/config.h"
34 #include "src/core/lib/gpr/useful.h"
35 #include "src/core/lib/gprpp/debug_location.h"
36 #include "src/core/lib/iomgr/closure.h"
37 #include "src/core/lib/iomgr/error.h"
38 #include "src/core/lib/iomgr/exec_ctx.h"
39 #include "src/core/lib/resource_quota/memory_quota.h"
40 #include "src/libfuzzer/libfuzzer_macro.h"
41 #include "test/core/resource_quota/call_checker.h"
42 #include "test/core/resource_quota/memory_quota_fuzzer.pb.h"
43 #include "test/core/util/fuzz_config_vars.h"
44 
45 bool squelch = true;
46 bool leak_check = true;
47 
48 namespace grpc_core {
49 namespace testing {
50 namespace {
MapReclamationPass(memory_quota_fuzzer::Reclaimer::Pass pass)51 ReclamationPass MapReclamationPass(memory_quota_fuzzer::Reclaimer::Pass pass) {
52   switch (pass) {
53     case memory_quota_fuzzer::Reclaimer::BENIGN:
54       return ReclamationPass::kBenign;
55     case memory_quota_fuzzer::Reclaimer::IDLE:
56       return ReclamationPass::kIdle;
57     case memory_quota_fuzzer::Reclaimer::DESTRUCTIVE:
58       return ReclamationPass::kDestructive;
59     default:
60       return ReclamationPass::kBenign;
61   }
62 }
63 
64 class Fuzzer {
65  public:
Run(const memory_quota_fuzzer::Msg & msg)66   void Run(const memory_quota_fuzzer::Msg& msg) {
67     ExecCtx exec_ctx;
68     RunMsg(msg);
69     do {
70       memory_quotas_.clear();
71       memory_allocators_.clear();
72       allocations_.clear();
73       exec_ctx.Flush();
74     } while (!memory_quotas_.empty() || !memory_allocators_.empty() ||
75              !allocations_.empty());
76   }
77 
78  private:
RunMsg(const memory_quota_fuzzer::Msg & msg)79   void RunMsg(const memory_quota_fuzzer::Msg& msg) {
80     for (int i = 0; i < msg.actions_size(); ++i) {
81       const auto& action = msg.actions(i);
82       switch (action.action_type_case()) {
83         case memory_quota_fuzzer::Action::kFlushExecCtx:
84           ExecCtx::Get()->Flush();
85           break;
86         case memory_quota_fuzzer::Action::kCreateQuota:
87           memory_quotas_.emplace(action.quota(),
88                                  MemoryQuota(absl::StrCat("quota-step-", i)));
89           break;
90         case memory_quota_fuzzer::Action::kDeleteQuota:
91           memory_quotas_.erase(action.quota());
92           break;
93         case memory_quota_fuzzer::Action::kCreateAllocator:
94           WithQuota(action.quota(), [this, action](MemoryQuota* q) {
95             memory_allocators_.emplace(action.allocator(),
96                                        q->CreateMemoryOwner());
97           });
98           break;
99         case memory_quota_fuzzer::Action::kDeleteAllocator:
100           memory_allocators_.erase(action.allocator());
101           break;
102         case memory_quota_fuzzer::Action::kSetQuotaSize:
103           WithQuota(action.quota(), [action](MemoryQuota* q) {
104             q->SetSize(Clamp(action.set_quota_size(), uint64_t{0},
105                              uint64_t{std::numeric_limits<ssize_t>::max()}));
106           });
107           break;
108         case memory_quota_fuzzer::Action::kCreateAllocation: {
109           auto min = action.create_allocation().min();
110           auto max = action.create_allocation().max();
111           if (min > max) break;
112           if (max > MemoryRequest::max_allowed_size()) break;
113           MemoryRequest req(min, max);
114           WithAllocator(
115               action.allocator(), [this, action, req](MemoryOwner* a) {
116                 auto alloc = a->MakeReservation(req);
117                 allocations_.emplace(action.allocation(), std::move(alloc));
118               });
119         } break;
120         case memory_quota_fuzzer::Action::kDeleteAllocation:
121           allocations_.erase(action.allocation());
122           break;
123         case memory_quota_fuzzer::Action::kPostReclaimer: {
124           std::function<void(absl::optional<ReclamationSweep>)> reclaimer;
125           auto cfg = action.post_reclaimer();
126           if (cfg.synchronous()) {
127             reclaimer = [this, cfg](absl::optional<ReclamationSweep>) {
128               RunMsg(cfg.msg());
129             };
130           } else {
131             reclaimer = [cfg, this](absl::optional<ReclamationSweep> sweep) {
132               struct Args {
133                 absl::optional<ReclamationSweep> sweep;
134                 memory_quota_fuzzer::Msg msg;
135                 Fuzzer* fuzzer;
136               };
137               auto* args = new Args{std::move(sweep), cfg.msg(), this};
138               auto* closure = GRPC_CLOSURE_CREATE(
139                   [](void* arg, grpc_error_handle) {
140                     auto* args = static_cast<Args*>(arg);
141                     args->fuzzer->RunMsg(args->msg);
142                     delete args;
143                   },
144                   args, nullptr);
145               ExecCtx::Get()->Run(DEBUG_LOCATION, closure, absl::OkStatus());
146             };
147             auto pass = MapReclamationPass(cfg.pass());
148             WithAllocator(
149                 action.allocator(), [pass, reclaimer](MemoryOwner* a) {
150                   // ensure called exactly once
151                   auto call_checker = CallChecker::MakeOptional();
152                   a->PostReclaimer(pass,
153                                    [reclaimer, call_checker](
154                                        absl::optional<ReclamationSweep> sweep) {
155                                      call_checker->Called();
156                                      reclaimer(std::move(sweep));
157                                    });
158                 });
159           }
160         } break;
161         case memory_quota_fuzzer::Action::ACTION_TYPE_NOT_SET:
162           break;
163       }
164     }
165   }
166 
167   template <typename F>
WithQuota(int quota,F f)168   void WithQuota(int quota, F f) {
169     auto it = memory_quotas_.find(quota);
170     if (it == memory_quotas_.end()) return;
171     f(&it->second);
172   }
173 
174   template <typename F>
WithAllocator(int allocator,F f)175   void WithAllocator(int allocator, F f) {
176     auto it = memory_allocators_.find(allocator);
177     if (it == memory_allocators_.end()) return;
178     f(&it->second);
179   }
180 
181   std::map<int, MemoryQuota> memory_quotas_;
182   std::map<int, MemoryOwner> memory_allocators_;
183   std::map<int, MemoryAllocator::Reservation> allocations_;
184 };
185 
186 }  // namespace
187 }  // namespace testing
188 }  // namespace grpc_core
189 
dont_log(gpr_log_func_args *)190 static void dont_log(gpr_log_func_args* /*args*/) {}
191 
DEFINE_PROTO_FUZZER(const memory_quota_fuzzer::Msg & msg)192 DEFINE_PROTO_FUZZER(const memory_quota_fuzzer::Msg& msg) {
193   if (squelch) gpr_set_log_function(dont_log);
194   grpc_core::ApplyFuzzConfigVars(msg.config_vars());
195   grpc_core::TestOnlyReloadExperimentsFromConfigVariables();
196   gpr_log_verbosity_init();
197   grpc_tracer_init();
198   grpc_core::testing::Fuzzer().Run(msg);
199 }
200