| // 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. |
| |
| #include "src/sys/fuzzing/common/controller.h" |
| |
| #include <lib/syslog/cpp/macros.h> |
| #include <zircon/sanitizer.h> |
| #include <zircon/status.h> |
| |
| #include <iostream> |
| |
| #include "src/sys/fuzzing/common/async-eventpair.h" |
| #include "src/sys/fuzzing/common/async-socket.h" |
| #include "src/sys/fuzzing/common/async-types.h" |
| #include "src/sys/fuzzing/common/corpus-reader-client.h" |
| #include "src/sys/fuzzing/common/options.h" |
| |
| namespace fuzzing { |
| |
| using ::fuchsia::fuzzer::DONE_MARKER; |
| |
| ControllerImpl::ControllerImpl(RunnerPtr runner) |
| : binding_(this), |
| executor_(runner->executor()), |
| runner_(runner), |
| artifact_(fpromise::ok(Artifact())) { |
| if (auto status = zx::event::create(0, &changed_); status != ZX_OK) { |
| FX_LOGS(FATAL) << "Failed to create artifact event: " << zx_status_get_string(status); |
| } |
| if (auto status = changed_.signal(0, Signal::kSync); status != ZX_OK) { |
| FX_LOGS(FATAL) << "Failed to signal artifact event: " << zx_status_get_string(status); |
| } |
| } |
| |
| void ControllerImpl::Bind(fidl::InterfaceRequest<Controller> request) { |
| FX_DCHECK(runner_); |
| binding_.Bind(std::move(request)); |
| } |
| |
| ZxPromise<> ControllerImpl::ResetArtifact() { |
| return fpromise::make_promise([this]() -> ZxResult<> { |
| if (artifact_.is_ok() && artifact_.value().is_empty()) { |
| return fpromise::ok(); |
| } |
| artifact_ = fpromise::ok(Artifact()); |
| if (auto status = changed_.signal(0, Signal::kSync); status != ZX_OK) { |
| FX_LOGS(ERROR) << "Failed to signal artifact event: " << zx_status_get_string(status); |
| return fpromise::error(status); |
| } |
| return fpromise::ok(); |
| }) |
| .wrap_with(scope_); |
| } |
| |
| ZxPromise<> ControllerImpl::Finish(ZxResult<Artifact> result) { |
| return fpromise::make_promise([this, result = std::move(result)]() mutable -> ZxResult<> { |
| FX_CHECK(artifact_.is_ok() && artifact_.value().is_empty()); |
| if (result.is_ok()) { |
| artifact_ = fpromise::ok(result.take_value()); |
| } else { |
| artifact_ = fpromise::error(result.error()); |
| } |
| if (auto status = changed_.signal(0, Signal::kSync); status != ZX_OK) { |
| FX_LOGS(ERROR) << "Failed to signal artifact event: " << zx_status_get_string(status); |
| return fpromise::error(status); |
| } |
| std::cout << std::endl << DONE_MARKER << std::endl; |
| std::cerr << std::endl << DONE_MARKER << std::endl; |
| FX_LOGS(INFO) << DONE_MARKER; |
| return fpromise::ok(); |
| }) |
| .wrap_with(scope_); |
| } |
| |
| /////////////////////////////////////////////////////////////// |
| // FIDL methods. |
| |
| void ControllerImpl::Configure(Options options, ConfigureCallback callback) { |
| auto task = |
| fpromise::make_promise([this, overrides = std::move(options)]() mutable -> ZxResult<> { |
| auto options = runner_->options(); |
| SetOptions(options.get(), overrides); |
| return fpromise::ok(); |
| }) |
| .and_then(runner_->Configure()) |
| .then( |
| [callback = std::move(callback)](ZxResult<>& result) { callback(std::move(result)); }) |
| .wrap_with(scope_); |
| executor_->schedule_task(std::move(task)); |
| } |
| |
| void ControllerImpl::GetOptions(GetOptionsCallback callback) { |
| const auto& options = runner_->options(); |
| callback(CopyOptions(*options)); |
| } |
| |
| void ControllerImpl::AddToCorpus(CorpusType corpus_type, FidlInput fidl_input, |
| AddToCorpusCallback callback) { |
| auto task = AsyncSocketRead(executor_, std::move(fidl_input)) |
| .and_then([this, corpus_type](Input& received) -> ZxResult<> { |
| return AsZxResult(runner_->AddToCorpus(corpus_type, std::move(received))); |
| }) |
| .then([callback = std::move(callback)](ZxResult<>& result) { |
| callback(std::move(result)); |
| }) |
| .wrap_with(scope_); |
| executor_->schedule_task(std::move(task)); |
| } |
| |
| void ControllerImpl::ReadCorpus(CorpusType corpus_type, fidl::InterfaceHandle<CorpusReader> reader, |
| ReadCorpusCallback callback) { |
| auto client = std::make_unique<CorpusReaderClient>(executor_); |
| client->Bind(std::move(reader)); |
| |
| // The client must be moved into the promise to prevent it going out of scope. |
| auto task = fpromise::make_promise([runner = runner_, corpus_type, client = std::move(client), |
| send = ZxFuture<>()](Context& context) mutable -> ZxResult<> { |
| if (!send) { |
| send = client->Send(runner->GetCorpus(corpus_type)); |
| } |
| if (!send(context)) { |
| return fpromise::pending(); |
| } |
| return send.take_result(); |
| }).and_then([callback = std::move(callback)]() { callback(); }); |
| executor_->schedule_task(std::move(task)); |
| } |
| |
| void ControllerImpl::WriteDictionary(FidlInput dictionary, WriteDictionaryCallback callback) { |
| auto task = AsyncSocketRead(executor_, std::move(dictionary)) |
| .and_then([this](Input& received) -> ZxResult<> { |
| return AsZxResult(runner_->ParseDictionary(std::move(received))); |
| }) |
| .then([callback = std::move(callback)](ZxResult<>& result) { |
| callback(std::move(result)); |
| }) |
| .wrap_with(scope_); |
| executor_->schedule_task(std::move(task)); |
| } |
| |
| void ControllerImpl::ReadDictionary(ReadDictionaryCallback callback) { |
| callback(AsyncSocketWrite(executor_, runner_->GetDictionaryAsInput())); |
| } |
| |
| void ControllerImpl::AddMonitor(fidl::InterfaceHandle<Monitor> monitor, |
| AddMonitorCallback callback) { |
| FX_DCHECK(runner_); |
| runner_->AddMonitor(std::move(monitor)); |
| callback(); |
| } |
| |
| // Invokes the callback provided for long-running workflows. |
| template <typename ResultType, typename WorkflowCallback> |
| static ResultType Acknowledge(ResultType&& result, WorkflowCallback callback) { |
| if (result.is_ok()) { |
| callback(fpromise::ok()); |
| } else { |
| callback(fpromise::error(result.error())); |
| } |
| return std::move(result); |
| } |
| |
| void ControllerImpl::Fuzz(FuzzCallback callback) { |
| auto task = ResetArtifact() |
| .then([callback = std::move(callback)](ZxResult<>& result) mutable { |
| return Acknowledge(std::move(result), std::move(callback)); |
| }) |
| .and_then(runner_->Fuzz()) |
| .then([this](ZxResult<Artifact>& result) { return Finish(std::move(result)); }) |
| .wrap_with(scope_); |
| executor_->schedule_task(std::move(task)); |
| } |
| |
| void ControllerImpl::TryOne(FidlInput fidl_input, TryOneCallback callback) { |
| auto task = ResetArtifact() |
| .and_then(AsyncSocketRead(executor_, std::move(fidl_input))) |
| .then([callback = std::move(callback)](ZxResult<Input>& result) mutable { |
| return Acknowledge(std::move(result), std::move(callback)); |
| }) |
| .and_then([this](Input& input) { return runner_->TryOne(std::move(input)); }) |
| .then([this](ZxResult<Artifact>& result) { return Finish(std::move(result)); }) |
| .wrap_with(scope_); |
| executor_->schedule_task(std::move(task)); |
| } |
| |
| void ControllerImpl::Minimize(FidlInput fidl_input, MinimizeCallback callback) { |
| auto task = |
| ResetArtifact() |
| .and_then(AsyncSocketRead(executor_, std::move(fidl_input))) |
| .and_then([this](Input& input) { return runner_->ValidateMinimize(std::move(input)); }) |
| .then([callback = std::move(callback)](ZxResult<Artifact>& result) mutable { |
| return Acknowledge(std::move(result), std::move(callback)); |
| }) |
| .and_then([this](Artifact& artifact) { return runner_->Minimize(std::move(artifact)); }) |
| .then([this](ZxResult<Artifact>& result) { return Finish(std::move(result)); }) |
| .wrap_with(scope_); |
| executor_->schedule_task(std::move(task)); |
| } |
| |
| void ControllerImpl::Cleanse(FidlInput fidl_input, CleanseCallback callback) { |
| auto task = ResetArtifact() |
| .and_then(AsyncSocketRead(executor_, std::move(fidl_input))) |
| .then([callback = std::move(callback)](ZxResult<Input>& result) mutable { |
| return Acknowledge(std::move(result), std::move(callback)); |
| }) |
| .and_then([this](Input& input) { return runner_->Cleanse(std::move(input)); }) |
| .then([this](ZxResult<Artifact>& result) { return Finish(std::move(result)); }) |
| .wrap_with(scope_); |
| executor_->schedule_task(std::move(task)); |
| } |
| |
| void ControllerImpl::Merge(MergeCallback callback) { |
| auto task = ResetArtifact() |
| .and_then([this]() { return runner_->ValidateMerge(); }) |
| .then([callback = std::move(callback)](ZxResult<>& result) mutable { |
| return Acknowledge(std::move(result), std::move(callback)); |
| }) |
| .and_then([this]() { return runner_->Merge(); }) |
| .then([this](ZxResult<Artifact>& result) { return Finish(std::move(result)); }) |
| .wrap_with(scope_); |
| executor_->schedule_task(std::move(task)); |
| } |
| |
| void ControllerImpl::GetStatus(GetStatusCallback callback) { callback(runner_->CollectStatus()); } |
| |
| void ControllerImpl::WatchArtifact(WatchArtifactCallback callback) { |
| auto task = |
| executor_->MakePromiseWaitHandle(zx::unowned_handle(changed_.get()), kSync) |
| .then([this](const ZxResult<zx_packet_signal_t>& result) -> ZxResult<FidlArtifact> { |
| if (result.is_error()) { |
| auto status = result.error(); |
| FX_LOGS(ERROR) << "Failed to watch for artifact: " << zx_status_get_string(status); |
| return fpromise::error(status); |
| } |
| if (auto status = changed_.signal(Signal::kSync, 0); status != ZX_OK) { |
| FX_LOGS(ERROR) << "Failed to clear artifact event: " << zx_status_get_string(status); |
| return fpromise::error(status); |
| } |
| if (artifact_.is_error()) { |
| return fpromise::error(artifact_.error()); |
| } |
| return fpromise::ok(AsyncSocketWrite(executor_, artifact_.value())); |
| }) |
| .then([callback = std::move(callback)](ZxResult<FidlArtifact>& result) { |
| if (result.is_error()) { |
| FidlArtifact fidl_artifact; |
| fidl_artifact.set_error(result.error()); |
| callback(std::move(fidl_artifact)); |
| } else { |
| callback(result.take_value()); |
| } |
| }) |
| .wrap_with(scope_); |
| executor_->schedule_task(std::move(task)); |
| } |
| |
| void ControllerImpl::Stop() { |
| if (runner_) { |
| executor_->schedule_task(runner_->Stop()); |
| } |
| } |
| |
| } // namespace fuzzing |