• 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 <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