blob: 12a2d4fa2210fc9f61216dbe528696ef00377ba3 [file] [log] [blame]
// 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 "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_; }
// Adds default values to unspecified options that are needed by objects of this class.
virtual void AddDefaults(Options* options) = 0;
// 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 the input at the given |offset| in the corpus of the given |corpus_type|,
// or an emtpy input if |offset| is invalid.
virtual Input ReadFromCorpus(CorpusType corpus_type, size_t offset) = 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;
// Fuzzing workflows.
virtual ZxPromise<> Configure(const OptionsPtr& options) = 0;
virtual ZxPromise<FuzzResult> Execute(Input input) = 0;
virtual ZxPromise<Input> Minimize(Input input) = 0;
virtual ZxPromise<Input> Cleanse(Input input) = 0;
virtual ZxPromise<Artifact> Fuzz() = 0;
virtual ZxPromise<> Merge() = 0;
// Cancels the current workflow.
virtual ZxPromise<> Stop() = 0;
// Adds a subscriber for status updates.
void AddMonitor(fidl::InterfaceHandle<Monitor> monitor);
// Creates a |Status| object representing all attached processes.
virtual Status CollectStatus() = 0;
protected:
// Represents a single fuzzing workflow, e.g. |Execute|, |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|.
class Workflow final {
public:
explicit Workflow(Runner* runner) : runner_(runner) {}
~Workflow() = default;
// 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) {
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()
.and_then(std::move(promise))
.inspect([this](const typename Promise::result_type& result) { Finish(); })
.wrap_with(scope_);
}
// Returns a promise to stop the current workflow. The promise completes after |Finish| is
// called.
ZxPromise<> Stop();
private:
ZxPromise<> Start();
void Finish();
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() {}
// Collects the current status, labels it with the given |reason|, and sends it all attached
//|Monitor|s.
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_;
MonitorClients monitors_;
Scope scope_;
FXL_DISALLOW_COPY_ASSIGN_AND_MOVE(Runner);
};
} // namespace fuzzing
#endif // SRC_SYS_FUZZING_COMMON_RUNNER_H_