blob: ef1ff77876f346d42f02e343215d2d36393644fc [file] [log] [blame] [edit]
// 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/options.h"
namespace fuzzing {
ControllerImpl::ControllerImpl() : binding_(this, std::make_shared<Dispatcher>()) {
dispatcher_ = binding_.dispatcher();
options_ = std::make_shared<Options>();
transceiver_ = std::make_shared<Transceiver>();
reader_ = std::thread([this]() { ReadCorpusLoop(); });
}
ControllerImpl::~ControllerImpl() {
// No new requests.
dispatcher_->Shutdown();
// Finish sending the corpus.
{
std::lock_guard<std::mutex> lock(mutex_);
reading_ = false;
sync_completion_signal(&pending_readers_);
}
reader_.join();
// Finish up data transfers.
transceiver_->Shutdown();
}
void ControllerImpl::SetRunner(const std::shared_ptr<Runner>& runner) {
runner_ = runner;
AddDefaults();
runner_->Configure(options_);
}
void ControllerImpl::AddDefaults() {
if (!options_->has_seed()) {
options_->set_seed(static_cast<uint32_t>(zx::ticks::now().get()));
}
runner_->AddDefaults(options_.get());
}
void ControllerImpl::ReceiveAndThen(FidlInput fidl_input, Response response,
fit::function<void(Input, Response)> callback) {
transceiver_->Receive(std::move(fidl_input),
[response = std::move(response), callback = std::move(callback)](
zx_status_t status, Input received) mutable {
if (status != ZX_OK) {
response.Send(status);
} else {
callback(std::move(received), std::move(response));
}
});
}
///////////////////////////////////////////////////////////////
// FIDL methods.
void ControllerImpl::Bind(fidl::InterfaceRequest<Controller> request) {
FX_DCHECK(runner_);
binding_.Bind(std::move(request));
}
void ControllerImpl::Configure(Options options, ConfigureCallback callback) {
*options_ = std::move(options);
AddDefaults();
callback(runner_->Configure(options_));
}
void ControllerImpl::GetOptions(GetOptionsCallback callback) {
auto options = CopyOptions(*options_);
callback(std::move(options));
}
void ControllerImpl::AddToCorpus(CorpusType corpus_type, FidlInput fidl_input,
AddToCorpusCallback callback) {
ReceiveAndThen(std::move(fidl_input), NewResponse(std::move(callback)),
[this, corpus_type](Input received, Response response) {
response.Send(runner_->AddToCorpus(corpus_type, std::move(received)));
});
}
void ControllerImpl::ReadCorpus(CorpusType corpus_type, fidl::InterfaceHandle<CorpusReader> reader,
ReadCorpusCallback callback) {
CorpusReaderSyncPtr ptr;
ptr.Bind(std::move(reader));
{
std::lock_guard<std::mutex> lock(mutex_);
if (reading_) {
readers_.emplace_back(corpus_type, std::move(ptr));
sync_completion_signal(&pending_readers_);
}
}
// If |!reading|, the |ptr| will be dropped, signalling the controller is shutting down..
callback();
}
void ControllerImpl::ReadCorpusLoop() {
while (true) {
// |pending_readers_| will be signalled on object destruction.
sync_completion_wait(&pending_readers_, ZX_TIME_INFINITE);
CorpusType corpus_type;
CorpusReaderSyncPtr reader;
{
std::lock_guard<std::mutex> lock(mutex_);
if (readers_.empty()) {
return;
}
auto& request = readers_.front();
corpus_type = request.first;
reader = std::move(request.second);
readers_.pop_front();
if (reading_ && readers_.empty()) {
sync_completion_reset(&pending_readers_);
}
}
size_t offset = 1;
for (bool has_more = true; has_more;) {
auto input = runner_->ReadFromCorpus(corpus_type, offset++);
has_more = input.size() != 0;
FidlInput next;
// Only error from the transceiver is |ZX_ERR_BAD_STATE| if it is shutting down.
if (transceiver_->Transmit(std::move(input), &next) != ZX_OK) {
break;
}
zx_status_t result;
auto status = reader->Next(std::move(next), &result);
status = status == ZX_OK ? result : status;
if (status != ZX_OK) {
FX_LOGS(WARNING) << "Failed to send next input from corpus: "
<< zx_status_get_string(status);
break;
}
}
}
}
void ControllerImpl::WriteDictionary(FidlInput dictionary, WriteDictionaryCallback callback) {
ReceiveAndThen(std::move(dictionary), NewResponse(std::move(callback)),
[this](Input received, Response response) {
response.Send(runner_->ParseDictionary(received));
});
}
void ControllerImpl::ReadDictionary(ReadDictionaryCallback callback) {
auto response = NewResponse(std::move(callback));
response.Send(ZX_OK, Result::NO_ERRORS, runner_->GetDictionaryAsInput());
}
void ControllerImpl::GetStatus(GetStatusCallback callback) { callback(runner_->CollectStatus()); }
void ControllerImpl::AddMonitor(fidl::InterfaceHandle<Monitor> monitor,
AddMonitorCallback callback) {
MonitorPtr ptr;
ptr.Bind(std::move(monitor), dispatcher_->get());
runner_->AddMonitor(std::move(ptr));
callback();
}
void ControllerImpl::GetResults(GetResultsCallback callback) {
auto response = NewResponse(std::move(callback));
response.Send(ZX_OK, runner_->result(), runner_->result_input());
}
void ControllerImpl::Execute(FidlInput fidl_input, ExecuteCallback callback) {
ReceiveAndThen(std::move(fidl_input), NewResponse(std::move(callback)),
[this](Input received, Response response) {
runner_->Execute(std::move(received), [this, response = std::move(response)](
zx_status_t status) mutable {
response.Send(status, runner_->result(), runner_->result_input());
});
});
}
void ControllerImpl::Minimize(FidlInput fidl_input, MinimizeCallback callback) {
ReceiveAndThen(std::move(fidl_input), NewResponse(std::move(callback)),
[this](Input received, Response response) {
runner_->Minimize(std::move(received), [this, response = std::move(response)](
zx_status_t status) mutable {
response.Send(status, runner_->result(), runner_->result_input());
});
});
}
void ControllerImpl::Cleanse(FidlInput fidl_input, CleanseCallback callback) {
ReceiveAndThen(std::move(fidl_input), NewResponse(std::move(callback)),
[this](Input received, Response response) {
runner_->Cleanse(std::move(received), [this, response = std::move(response)](
zx_status_t status) mutable {
response.Send(status, runner_->result(), runner_->result_input());
});
});
}
void ControllerImpl::Fuzz(FuzzCallback callback) {
runner_->Fuzz([this, response = NewResponse(std::move(callback))](zx_status_t status) mutable {
response.Send(status, runner_->result(), runner_->result_input());
});
}
void ControllerImpl::Merge(MergeCallback callback) {
runner_->Merge([response = NewResponse(std::move(callback))](zx_status_t status) mutable {
response.Send(status);
});
}
} // namespace fuzzing