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 <functional>
16 #include <map>
17 #include <memory>
18 #include <tuple>
19 #include <utility>
20 #include <vector>
21
22 #include "absl/functional/any_invocable.h"
23 #include "absl/status/status.h"
24 #include "absl/types/optional.h"
25
26 #include <grpc/support/log.h>
27
28 #include "src/core/lib/promise/activity.h"
29 #include "src/core/lib/promise/join.h"
30 #include "src/core/lib/promise/map.h"
31 #include "src/core/lib/promise/poll.h"
32 #include "src/core/lib/promise/promise.h"
33 #include "src/core/lib/promise/race.h"
34 #include "src/core/lib/promise/seq.h"
35 #include "src/libfuzzer/libfuzzer_macro.h"
36 #include "test/core/promise/promise_fuzzer.pb.h"
37
38 bool squelch = true;
39 bool leak_check = true;
40
41 namespace grpc_core {
42 // Return type for infallible promises.
43 // We choose this so that it's easy to construct, and will trigger asan failures
44 // if misused, and is copyable.
45 using IntHdl = std::shared_ptr<int>;
46
47 template <typename T>
48 using PromiseFactory = std::function<Promise<T>(T)>;
49
50 namespace {
51 class Fuzzer {
52 public:
Run(const promise_fuzzer::Msg & msg)53 void Run(const promise_fuzzer::Msg& msg) {
54 // If there's no promise we can't construct and activity and... we're done.
55 if (!msg.has_promise()) {
56 return;
57 }
58 // Construct activity.
59 activity_ = MakeActivity(
60 [msg, this] {
61 return Seq(MakePromise(msg.promise()),
62 [] { return absl::OkStatus(); });
63 },
64 Scheduler{this},
65 [this](absl::Status status) {
66 // Must only be called once
67 GPR_ASSERT(!done_);
68 // If we became certain of the eventual status, verify it.
69 if (expected_status_.has_value()) {
70 GPR_ASSERT(status == *expected_status_);
71 }
72 // Mark ourselves done.
73 done_ = true;
74 });
75 for (int i = 0; !done_ && activity_ != nullptr && i < msg.actions_size();
76 i++) {
77 // Do some things
78 const auto& action = msg.actions(i);
79 switch (action.action_type_case()) {
80 // Force a wakeup
81 case promise_fuzzer::Action::kForceWakeup:
82 activity_->ForceWakeup();
83 break;
84 // Cancel from the outside
85 case promise_fuzzer::Action::kCancel:
86 ExpectCancelled();
87 activity_.reset();
88 break;
89 // Flush any pending wakeups
90 case promise_fuzzer::Action::kFlushWakeup:
91 if (wakeup_ != nullptr) std::exchange(wakeup_, nullptr)();
92 break;
93 // Drop some wakeups (external system closed?)
94 case promise_fuzzer::Action::kDropWaker: {
95 int n = action.drop_waker();
96 auto v = std::move(wakers_[n]);
97 wakers_.erase(n);
98 break;
99 }
100 // Wakeup some wakeups
101 case promise_fuzzer::Action::kAwakeWaker: {
102 int n = action.awake_waker();
103 auto v = std::move(wakers_[n]);
104 wakers_.erase(n);
105 for (auto& w : v) {
106 w.Wakeup();
107 }
108 break;
109 }
110 case promise_fuzzer::Action::ACTION_TYPE_NOT_SET:
111 break;
112 }
113 }
114 ExpectCancelled();
115 activity_.reset();
116 if (wakeup_ != nullptr) std::exchange(wakeup_, nullptr)();
117 GPR_ASSERT(done_);
118 }
119
120 private:
121 // Schedule wakeups against the fuzzer
122 struct Scheduler {
123 Fuzzer* fuzzer;
124 template <typename ActivityType>
125 class BoundScheduler {
126 public:
BoundScheduler(Scheduler scheduler)127 explicit BoundScheduler(Scheduler scheduler)
128 : fuzzer_(scheduler.fuzzer) {}
ScheduleWakeup()129 void ScheduleWakeup() {
130 GPR_ASSERT(static_cast<ActivityType*>(this) ==
131 fuzzer_->activity_.get());
132 GPR_ASSERT(fuzzer_->wakeup_ == nullptr);
133 fuzzer_->wakeup_ = [this]() {
134 static_cast<ActivityType*>(this)->RunScheduledWakeup();
135 };
136 }
137
138 private:
139 Fuzzer* fuzzer_;
140 };
141 };
142
143 // We know that if not already finished, the status when finished will be
144 // cancelled.
ExpectCancelled()145 void ExpectCancelled() {
146 if (!done_ && !expected_status_.has_value()) {
147 expected_status_ = absl::CancelledError();
148 }
149 }
150
151 // Construct a promise factory from a protobuf
MakePromiseFactory(const promise_fuzzer::PromiseFactory & p)152 PromiseFactory<IntHdl> MakePromiseFactory(
153 const promise_fuzzer::PromiseFactory& p) {
154 switch (p.promise_factory_type_case()) {
155 case promise_fuzzer::PromiseFactory::kPromise:
156 return [p, this](IntHdl) { return MakePromise(p.promise()); };
157 case promise_fuzzer::PromiseFactory::kLast:
158 return [](IntHdl h) { return [h]() { return h; }; };
159 case promise_fuzzer::PromiseFactory::PROMISE_FACTORY_TYPE_NOT_SET:
160 break;
161 }
162 return [](IntHdl) {
163 return []() -> Poll<IntHdl> { return std::make_shared<int>(42); };
164 };
165 }
166
167 // Construct a promise from a protobuf
MakePromise(const promise_fuzzer::Promise & p)168 Promise<IntHdl> MakePromise(const promise_fuzzer::Promise& p) {
169 switch (p.promise_type_case()) {
170 case promise_fuzzer::Promise::kSeq:
171 switch (p.seq().promise_factories_size()) {
172 case 1:
173 return Seq(MakePromise(p.seq().first()),
174 MakePromiseFactory(p.seq().promise_factories(0)));
175 case 2:
176 return Seq(MakePromise(p.seq().first()),
177 MakePromiseFactory(p.seq().promise_factories(0)),
178 MakePromiseFactory(p.seq().promise_factories(1)));
179 case 3:
180 return Seq(MakePromise(p.seq().first()),
181 MakePromiseFactory(p.seq().promise_factories(0)),
182 MakePromiseFactory(p.seq().promise_factories(1)),
183 MakePromiseFactory(p.seq().promise_factories(2)));
184 case 4:
185 return Seq(MakePromise(p.seq().first()),
186 MakePromiseFactory(p.seq().promise_factories(0)),
187 MakePromiseFactory(p.seq().promise_factories(1)),
188 MakePromiseFactory(p.seq().promise_factories(2)),
189 MakePromiseFactory(p.seq().promise_factories(3)));
190 case 5:
191 return Seq(MakePromise(p.seq().first()),
192 MakePromiseFactory(p.seq().promise_factories(0)),
193 MakePromiseFactory(p.seq().promise_factories(1)),
194 MakePromiseFactory(p.seq().promise_factories(2)),
195 MakePromiseFactory(p.seq().promise_factories(3)),
196 MakePromiseFactory(p.seq().promise_factories(4)));
197 case 6:
198 return Seq(MakePromise(p.seq().first()),
199 MakePromiseFactory(p.seq().promise_factories(0)),
200 MakePromiseFactory(p.seq().promise_factories(1)),
201 MakePromiseFactory(p.seq().promise_factories(2)),
202 MakePromiseFactory(p.seq().promise_factories(3)),
203 MakePromiseFactory(p.seq().promise_factories(4)),
204 MakePromiseFactory(p.seq().promise_factories(5)));
205 }
206 break;
207 case promise_fuzzer::Promise::kJoin:
208 switch (p.join().promises_size()) {
209 case 1:
210 return Map(Join(MakePromise(p.join().promises(0))),
211 [](std::tuple<IntHdl> t) { return std::get<0>(t); });
212 case 2:
213 return Map(
214 Join(MakePromise(p.join().promises(0)),
215 MakePromise(p.join().promises(1))),
216 [](std::tuple<IntHdl, IntHdl> t) { return std::get<0>(t); });
217 case 3:
218 return Map(Join(MakePromise(p.join().promises(0)),
219 MakePromise(p.join().promises(1)),
220 MakePromise(p.join().promises(2))),
221 [](std::tuple<IntHdl, IntHdl, IntHdl> t) {
222 return std::get<0>(t);
223 });
224 case 4:
225 return Map(Join(MakePromise(p.join().promises(0)),
226 MakePromise(p.join().promises(1)),
227 MakePromise(p.join().promises(2)),
228 MakePromise(p.join().promises(3))),
229 [](std::tuple<IntHdl, IntHdl, IntHdl, IntHdl> t) {
230 return std::get<0>(t);
231 });
232 case 5:
233 return Map(
234 Join(MakePromise(p.join().promises(0)),
235 MakePromise(p.join().promises(1)),
236 MakePromise(p.join().promises(2)),
237 MakePromise(p.join().promises(3)),
238 MakePromise(p.join().promises(4))),
239 [](std::tuple<IntHdl, IntHdl, IntHdl, IntHdl, IntHdl> t) {
240 return std::get<0>(t);
241 });
242 case 6:
243 return Map(
244 Join(MakePromise(p.join().promises(0)),
245 MakePromise(p.join().promises(1)),
246 MakePromise(p.join().promises(2)),
247 MakePromise(p.join().promises(3)),
248 MakePromise(p.join().promises(4)),
249 MakePromise(p.join().promises(5))),
250 [](std::tuple<IntHdl, IntHdl, IntHdl, IntHdl, IntHdl, IntHdl>
251 t) { return std::get<0>(t); });
252 }
253 break;
254 case promise_fuzzer::Promise::kRace:
255 switch (p.race().promises_size()) {
256 case 1:
257 return Race(MakePromise(p.race().promises(0)));
258 case 2:
259 return Race(MakePromise(p.race().promises(0)),
260 MakePromise(p.race().promises(1)));
261 case 3:
262 return Race(MakePromise(p.race().promises(0)),
263 MakePromise(p.race().promises(1)),
264 MakePromise(p.race().promises(2)));
265 case 4:
266 return Race(MakePromise(p.race().promises(0)),
267 MakePromise(p.race().promises(1)),
268 MakePromise(p.race().promises(2)),
269 MakePromise(p.race().promises(3)));
270 case 5:
271 return Race(MakePromise(p.race().promises(0)),
272 MakePromise(p.race().promises(1)),
273 MakePromise(p.race().promises(2)),
274 MakePromise(p.race().promises(3)),
275 MakePromise(p.race().promises(4)));
276 case 6:
277 return Race(MakePromise(p.race().promises(0)),
278 MakePromise(p.race().promises(1)),
279 MakePromise(p.race().promises(2)),
280 MakePromise(p.race().promises(3)),
281 MakePromise(p.race().promises(4)),
282 MakePromise(p.race().promises(5)));
283 }
284 break;
285 case promise_fuzzer::Promise::kNever:
286 return Never<IntHdl>();
287 case promise_fuzzer::Promise::kSleepFirstN: {
288 int n = p.sleep_first_n();
289 return [n]() mutable -> Poll<IntHdl> {
290 if (n <= 0) return std::make_shared<int>(0);
291 n--;
292 return Pending{};
293 };
294 }
295 case promise_fuzzer::Promise::kCancelFromInside:
296 return [this]() -> Poll<IntHdl> {
297 this->activity_.reset();
298 return Pending{};
299 };
300 case promise_fuzzer::Promise::kWaitOnceOnWaker: {
301 bool called = false;
302 auto config = p.wait_once_on_waker();
303 return [this, config, called]() mutable -> Poll<IntHdl> {
304 if (!called) {
305 if (config.owning()) {
306 wakers_[config.waker()].push_back(
307 GetContext<Activity>()->MakeOwningWaker());
308 } else {
309 wakers_[config.waker()].push_back(
310 GetContext<Activity>()->MakeNonOwningWaker());
311 }
312 return Pending();
313 }
314 return std::make_shared<int>(3);
315 };
316 }
317 case promise_fuzzer::Promise::PromiseTypeCase::PROMISE_TYPE_NOT_SET:
318 break;
319 }
320 return [] { return std::make_shared<int>(42); };
321 }
322
323 // Activity under test
324 ActivityPtr activity_;
325 // Scheduled wakeup (may be nullptr if no wakeup scheduled)
326 std::function<void()> wakeup_;
327 // If we are certain of the final status, then that. Otherwise, nullopt if we
328 // don't know.
329 absl::optional<absl::Status> expected_status_;
330 // Has on_done been called?
331 bool done_ = false;
332 // Wakers that may be scheduled
333 std::map<int, std::vector<Waker>> wakers_;
334 };
335 } // namespace
336
337 } // namespace grpc_core
338
DEFINE_PROTO_FUZZER(const promise_fuzzer::Msg & msg)339 DEFINE_PROTO_FUZZER(const promise_fuzzer::Msg& msg) {
340 grpc_core::Fuzzer().Run(msg);
341 }
342