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