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