blob: 18737cfc6563276b6e9879b9463cef3bc84259c4 [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.
#include "src/sys/fuzzing/common/controller.h"
#include <lib/syslog/cpp/macros.h>
#include <zircon/sanitizer.h>
#include <zircon/status.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 {
ControllerImpl::ControllerImpl(ExecutorPtr executor)
: binding_(this), executor_(std::move(executor)) {
options_ = MakeOptions();
}
void ControllerImpl::Bind(fidl::InterfaceRequest<Controller> request) {
FX_DCHECK(runner_);
binding_.Bind(std::move(request));
}
void ControllerImpl::SetRunner(RunnerPtr runner) {
runner_ = std::move(runner);
AddDefaults();
initialized_ = false;
}
void ControllerImpl::AddDefaults() {
if (!options_->has_seed()) {
options_->set_seed(static_cast<uint32_t>(zx::ticks::now().get()));
}
runner_->AddDefaults(options_.get());
}
ZxPromise<> ControllerImpl::Initialize() {
FX_CHECK(runner_);
artifact_ = Artifact();
return fpromise::make_promise(
[this, configure = ZxFuture<>()](Context& context) mutable -> ZxResult<> {
if (initialized_) {
return fpromise::ok();
}
if (!configure) {
configure = runner_->Configure(options_);
}
if (!configure(context)) {
return fpromise::pending();
}
initialized_ = true;
return configure.result();
})
.wrap_with(scope_);
}
///////////////////////////////////////////////////////////////
// FIDL methods.
void ControllerImpl::Configure(Options options, ConfigureCallback callback) {
*options_ = std::move(options);
AddDefaults();
auto task = runner_->Configure(options_)
.then([this, callback = std::move(callback)](const ZxResult<>& result) {
callback(result.is_ok() ? ZX_OK : result.error());
initialized_ = true;
})
.wrap_with(scope_);
executor_->schedule_task(std::move(task));
}
void ControllerImpl::GetOptions(GetOptionsCallback callback) { callback(CopyOptions(*options_)); }
void ControllerImpl::AddToCorpus(CorpusType corpus_type, FidlInput fidl_input,
AddToCorpusCallback callback) {
auto task = Initialize()
.and_then([this, fidl_input = std::move(fidl_input)]() mutable {
return AsyncSocketRead(executor_, std::move(fidl_input));
})
.and_then([this, corpus_type](Input& received) -> ZxResult<> {
runner_->AddToCorpus(corpus_type, std::move(received));
return fpromise::ok();
})
.then([callback = std::move(callback)](const ZxResult<>& result) {
callback(result.is_ok() ? ZX_OK : result.error());
})
.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));
auto task = Initialize()
.and_then([this, corpus_type, client = std::move(client),
inputs = std::vector<Input>(), callback = std::move(callback),
sending = ZxFuture<>()](Context& context) mutable -> ZxResult<> {
if (!sending) {
for (size_t offset = 1;; ++offset) {
auto input = runner_->ReadFromCorpus(corpus_type, offset);
if (input.size() == 0) {
break;
}
inputs.emplace_back(std::move(input));
}
sending = client->Send(std::move(inputs));
}
if (!sending(context)) {
return fpromise::pending();
}
callback();
return sending.result();
})
.wrap_with(scope_);
executor_->schedule_task(std::move(task));
}
void ControllerImpl::WriteDictionary(FidlInput dictionary, WriteDictionaryCallback callback) {
auto task = Initialize()
.and_then([this, dictionary = std::move(dictionary)]() mutable {
return AsyncSocketRead(executor_, std::move(dictionary));
})
.and_then([this](Input& received) -> ZxResult<> {
return AsZxResult(runner_->ParseDictionary(std::move(received)));
})
.then([callback = std::move(callback)](const ZxResult<>& result) {
callback(result.is_ok() ? ZX_OK : result.error());
})
.wrap_with(scope_);
executor_->schedule_task(std::move(task));
}
void ControllerImpl::ReadDictionary(ReadDictionaryCallback callback) {
callback(AsyncSocketWrite(executor_, runner_->GetDictionaryAsInput()));
}
void ControllerImpl::GetStatus(GetStatusCallback callback) { callback(runner_->CollectStatus()); }
void ControllerImpl::AddMonitor(fidl::InterfaceHandle<Monitor> monitor,
AddMonitorCallback callback) {
FX_DCHECK(runner_);
runner_->AddMonitor(std::move(monitor));
callback();
}
void ControllerImpl::GetResults(GetResultsCallback callback) {
const auto& input = artifact_.input();
callback(artifact_.fuzz_result(), AsyncSocketWrite(executor_, input.Duplicate()));
}
void ControllerImpl::Execute(FidlInput fidl_input, ExecuteCallback callback) {
auto task = Initialize()
.and_then(AsyncSocketRead(executor_, std::move(fidl_input)))
.and_then([this](Input& received) {
artifact_ = Artifact(FuzzResult::NO_ERRORS, received.Duplicate());
return runner_->Execute(std::move(received));
})
.then([this, callback = std::move(callback)](ZxResult<FuzzResult>& result) {
if (result.is_ok()) {
auto input = artifact_.take_input();
artifact_ = Artifact(result.value(), std::move(input));
}
callback(std::move(result));
})
.wrap_with(scope_);
executor_->schedule_task(std::move(task));
}
void ControllerImpl::Minimize(FidlInput fidl_input, MinimizeCallback callback) {
auto task =
Initialize()
.and_then(AsyncSocketRead(executor_, std::move(fidl_input)))
.and_then([this](Input& received) { return runner_->Minimize(std::move(received)); })
.and_then([this](Input& input) {
artifact_ = Artifact(FuzzResult::NO_ERRORS, input.Duplicate());
return fpromise::ok(AsyncSocketWrite(executor_, std::move(input)));
})
.then([callback = std::move(callback)](ZxResult<FidlInput>& result) {
callback(std::move(result));
})
.wrap_with(scope_);
executor_->schedule_task(std::move(task));
}
void ControllerImpl::Cleanse(FidlInput fidl_input, CleanseCallback callback) {
auto task =
Initialize()
.and_then(AsyncSocketRead(executor_, std::move(fidl_input)))
.and_then([this](Input& received) { return runner_->Cleanse(std::move(received)); })
.and_then([this](Input& input) {
artifact_ = Artifact(FuzzResult::NO_ERRORS, input.Duplicate());
return fpromise::ok(AsyncSocketWrite(executor_, std::move(input)));
})
.then([callback = std::move(callback)](ZxResult<FidlInput>& result) {
callback(std::move(result));
})
.wrap_with(scope_);
executor_->schedule_task(std::move(task));
}
void ControllerImpl::Fuzz(FuzzCallback callback) {
auto task = Initialize()
.and_then(runner_->Fuzz())
.and_then([this](Artifact& artifact) {
artifact_ = artifact.Duplicate();
return fpromise::ok(AsyncSocketWrite(executor_, std::move(artifact)));
})
.then([callback = std::move(callback)](ZxResult<FidlArtifact>& result) {
callback(std::move(result));
})
.wrap_with(scope_);
executor_->schedule_task(std::move(task));
}
void ControllerImpl::Merge(MergeCallback callback) {
auto task = Initialize()
.and_then(runner_->Merge())
.then([callback = std::move(callback)](ZxResult<>& result) {
callback(result.is_ok() ? ZX_OK : result.error());
});
executor_->schedule_task(std::move(task));
}
void ControllerImpl::Stop() {
if (runner_) {
executor_->schedule_task(runner_->Stop());
}
}
} // namespace fuzzing