xref: /aosp_15_r20/external/grpc-grpc/test/core/promise/promise_fuzzer.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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