| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_SYS_FUZZING_COMMON_RUNNER_H_ |
| #define SRC_SYS_FUZZING_COMMON_RUNNER_H_ |
| |
| #include <fuchsia/fuzzer/cpp/fidl.h> |
| #include <lib/fidl/cpp/interface_handle.h> |
| #include <lib/fit/function.h> |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "src/lib/fxl/macros.h" |
| #include "src/sys/fuzzing/common/artifact.h" |
| #include "src/sys/fuzzing/common/async-types.h" |
| #include "src/sys/fuzzing/common/input.h" |
| #include "src/sys/fuzzing/common/monitor-clients.h" |
| #include "src/sys/fuzzing/common/options.h" |
| #include "src/sys/fuzzing/common/result.h" |
| |
| namespace fuzzing { |
| |
| using ::fuchsia::fuzzer::MonitorPtr; |
| using ::fuchsia::fuzzer::Status; |
| using ::fuchsia::fuzzer::TargetAdapter; |
| using ::fuchsia::fuzzer::UpdateReason; |
| using CorpusType = ::fuchsia::fuzzer::Corpus; |
| |
| // |RunnerPtr| is the preferred way to reference a |Runner| in a future or promise without needing |
| // to wrap it in a scope. |
| class Runner; |
| using RunnerPtr = std::shared_ptr<Runner>; |
| |
| // This base class encapsulates the logic of performing a sequence of fuzzing runs. In |
| // particular, it defines virtual methods for performing the fuzzing workflows asynchronously, and |
| // invokes those methods on a dedicated worker thread to perform them without blocking the |
| // controller's FIDL dispatcher thread. |
| class Runner { |
| public: |
| // Note that the destructor cannot call |Close|, |Interrupt| or |Join|, as they are virtual. |
| // Instead, both this class and any derived class should have corresponding non-virtual "Impl" |
| // methods and call those on destruction. |
| virtual ~Runner() = default; |
| |
| // Accessors. |
| const ExecutorPtr& executor() const { return executor_; } |
| const OptionsPtr& options() const { return options_; } |
| |
| // Sets up the runner using arguments from the component manifest. |
| virtual ZxPromise<> Initialize(std::string pkg_dir, std::vector<std::string> args); |
| |
| // Examines the options, and prepares the runner for fuzzing given their values. |
| virtual ZxPromise<> Configure(); |
| |
| // Add an input to the specified corpus. Returns ZX_ERR_INVALID_ARGS if |corpus_type| is |
| // unrecognized. |
| virtual zx_status_t AddToCorpus(CorpusType corpus_type, Input input) = 0; |
| |
| // Returns a copy of all non-empty inputs in the corpus of the given |corpus_type|. |
| // The vector is sorted using Input's comparison operators. |
| virtual std::vector<Input> GetCorpus(CorpusType corpus_type) = 0; |
| |
| // Parses the given |input| as an AFL-style dictionary. For format details, see |
| // https://lcamtuf.coredump.cx/afl/technical_details.txt. Returns ZX_ERR_INVALID_ARGS if parsing |
| // fails. |
| virtual zx_status_t ParseDictionary(const Input& input) = 0; |
| |
| // Returns the current dictionary serialized into an |Input|. |
| virtual Input GetDictionaryAsInput() const = 0; |
| |
| // Adds a subscriber for status updates. |
| void AddMonitor(fidl::InterfaceHandle<Monitor> monitor); |
| |
| // Fuzzing workflows corresponding to methods in `fuchsia.fuzzer.Controller`. |
| |
| // Implementation of `fuchsia.fuzzer.Controller/Fuzz`. |
| virtual ZxPromise<Artifact> Fuzz() = 0; |
| |
| // Implementation of `fuchsia.fuzzer.Controller/TryOne`. |
| ZxPromise<Artifact> TryOne(Input input); |
| virtual ZxPromise<Artifact> TryEach(std::vector<Input> inputs) = 0; |
| |
| // Implementation of `fuchsia.fuzzer.Controller/Minimize`. |ValidateMinimize| must be called |
| // before |Minimize|. |
| virtual ZxPromise<Artifact> ValidateMinimize(Input input) = 0; |
| virtual ZxPromise<Artifact> Minimize(Artifact artifact) = 0; |
| |
| // Implementation of `fuchsia.fuzzer.Controller/Cleanse`. |
| virtual ZxPromise<Artifact> Cleanse(Input input) = 0; |
| |
| // Implementation of `fuchsia.fuzzer.Controller/Merge`. |ValidateMerge| must be called |
| // before |Merge|. |
| virtual ZxPromise<> ValidateMerge() = 0; |
| virtual ZxPromise<Artifact> Merge() = 0; |
| |
| // Creates a |Status| object representing all attached processes. |
| virtual Status CollectStatus() = 0; |
| |
| // Cancels the current workflow. |
| virtual ZxPromise<> Stop() = 0; |
| |
| protected: |
| void set_options(Options options) { *options_ = std::move(options); } |
| |
| // Represents a single fuzzing workflow, e.g. |TryOne|, |Minimize|, etc. It holds a pointer to |
| // the object that created it, but this is safe: it cannot outlive the object it is a part of. |
| // It should be used in the normal way, e.g. using |wrap_with|. |
| // |
| // Derived runners should include a `Workflow` member, and use it to wrap any returned promises |
| // that are exclusive with a fuzzing workflow, e.g. the workflows themselves and method like |
| // `Configure`. Promises that are a part of a workflow can be wrapped with the workflow's |
| // `scope()`. |
| // |
| class Workflow final { |
| public: |
| // In most cases, a fuzzing workflow is encapsulated by a promise returned by a single method, |
| // e.g. `Fuzz`. In this case, the promise is wrapped by a `Workflow` that both starts and |
| // finishes the workflow. |
| // |
| // If a workflow spans multiple promises from multiple methods, each method may specify a `mode` |
| // when wrapping its promise. For example, if a workflow is made up of 3 promises from 3 |
| // different methods: |
| // |
| // * The first may wrap its promise with `.wrap_with(workflow_, Workflow::Mode::kStart);`. |
| // * The second may wrap its promise with `.wrap_with(workflow_, Workflow::Mode::kNeither);`. |
| // * The last may wrap its promise with `.wrap_with(workflow_, Workflow::Mode::kFinish);`. |
| enum Mode { |
| kStart, |
| kFinish, |
| kBoth, |
| kNeither, |
| }; |
| |
| explicit Workflow(Runner* runner) : runner_(runner) {} |
| ~Workflow() = default; |
| |
| Scope& scope() { return scope_; } |
| |
| // Use |wrap_with(workflow_)| on promises that implement a workflow's behavior to create scoped |
| // actions on set up and tear down. |
| template <typename Promise> |
| decltype(auto) wrap(Promise promise, Mode mode = Mode::kBoth) { |
| static_assert(std::is_same<typename Promise::error_type, zx_status_t>::value, |
| "Workflows must use an error type of zx_status_t."); |
| return Start(mode) |
| .and_then(std::move(promise)) |
| .inspect([this, mode](const typename Promise::result_type& result) { Finish(mode); }) |
| .wrap_with(scope_); |
| } |
| |
| // Returns a promise to stop the current workflow. The promise completes after |Finish| is |
| // called. |
| ZxPromise<> Stop(); |
| |
| private: |
| ZxPromise<> Start(Mode mode); |
| void Finish(Mode mode); |
| |
| Runner* runner_ = nullptr; |
| ZxCompleter<> completer_; |
| ZxConsumer<> consumer_; |
| Scope scope_; |
| }; |
| |
| explicit Runner(ExecutorPtr executor); |
| |
| // These methods allow specific runners to implement actions that should be performed at the start |
| // or end of a workflow. They are called automatically by |Workflow|. The runners may also create |
| // additional tasks constrained to the workflow's |scope|. |
| virtual void StartWorkflow(Scope& scope) {} |
| virtual void FinishWorkflow() {} |
| |
| // Labels the current status with the given `reason`, and sends it all attached `Monitor`s. |
| virtual void UpdateMonitorsWithStatus(UpdateReason reason, Status status); |
| |
| // Like `UpdateMonitorsWithStatus`, but provides the status via `CollectStatus`. |
| void UpdateMonitors(UpdateReason reason); |
| |
| private: |
| // Like |UpdateMonitors|, but uses UpdateReason::DONE as the reason and disconnects monitors after |
| // they acknowledge receipt. |
| void FinishMonitoring(); |
| |
| ExecutorPtr executor_; |
| OptionsPtr options_; |
| MonitorClients monitors_; |
| Scope scope_; |
| |
| FXL_DISALLOW_COPY_ASSIGN_AND_MOVE(Runner); |
| }; |
| |
| } // namespace fuzzing |
| |
| #endif // SRC_SYS_FUZZING_COMMON_RUNNER_H_ |