blob: 44f5dddbe1e361a097c236a8910ff6e65d505307 [file] [log] [blame]
// Copyright 2022 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/realmfuzzer/testing/coverage.h"
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
namespace fuzzing {
using fuchsia::fuzzer::Data;
using fuchsia::fuzzer::InstrumentedProcess;
FakeCoverage::FakeCoverage(ExecutorPtr executor)
: collector_(this),
provider_(this),
executor_(executor),
options_(MakeOptions()),
receiver_(&sender_) {
auto self = zx::process::self();
zx_info_handle_basic_t info;
auto status = self->get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
FX_CHECK(status == ZX_OK) << zx_status_get_string(status);
target_id_ = info.koid;
}
fidl::InterfaceRequestHandler<CoverageDataCollector> FakeCoverage::GetCollectorHandler() {
return [this](fidl::InterfaceRequest<CoverageDataCollector> request) {
if (auto status = collector_.Bind(request.TakeChannel(), executor_->dispatcher());
status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to bind fuchsia.fuzzer.CoverageDataCollector request: "
<< zx_status_get_string(status);
}
};
}
fidl::InterfaceRequestHandler<CoverageDataProvider> FakeCoverage::GetProviderHandler() {
return [this](fidl::InterfaceRequest<CoverageDataProvider> request) {
if (auto status = provider_.Bind(std::move(request), executor_->dispatcher());
status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to bind fuchsia.fuzzer.CoverageDataProvider request: "
<< zx_status_get_string(status);
}
};
}
void FakeCoverage::Initialize(zx::eventpair eventpair, zx::process process,
InitializeCallback callback) {
CoverageData coverage_data = {
.target_id = target_id_,
.data = Data::WithInstrumented({
.eventpair = std::move(eventpair),
.process = std::move(process),
}),
};
if (auto status = sender_.Send(std::move(coverage_data)); status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to send instrumented process to provider: "
<< zx_status_get_string(status);
return;
}
callback(CopyOptions(*options_));
}
void FakeCoverage::AddInline8bitCounters(zx::vmo inline_8bit_counters,
AddInline8bitCountersCallback callback) {
CoverageData coverage_data = {
.target_id = target_id_,
.data = Data::WithInline8bitCounters(std::move(inline_8bit_counters)),
};
if (auto status = sender_.Send(std::move(coverage_data)); status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to send inline 8-bit counters to provider: "
<< zx_status_get_string(status);
return;
}
callback();
}
void FakeCoverage::SetOptions(Options options) { ::fuzzing::SetOptions(options_.get(), options); }
void FakeCoverage::WatchCoverageData(WatchCoverageDataCallback callback) {
auto task =
fpromise::make_promise([this, callback = std::move(callback),
coverage_data = std::vector<CoverageData>(),
fut = Future<CoverageData>()](Context& context) mutable -> Result<> {
while (true) {
if (!fut) {
while (true) {
auto result = receiver_.TryReceive();
if (result.is_error()) {
break;
}
coverage_data.emplace_back(result.take_value());
}
if (first_ || !coverage_data.empty()) {
callback(std::move(coverage_data));
first_ = false;
return fpromise::ok();
}
fut = receiver_.Receive();
}
if (!fut(context)) {
return fpromise::pending();
}
if (fut.is_error()) {
FX_LOGS(WARNING) << "Failed to receive coverage data";
return fpromise::error();
}
coverage_data.emplace_back(fut.take_value());
}
}).wrap_with(scope_);
executor_->schedule_task(std::move(task));
}
void FakeCoverage::Send(CoverageData coverage_data) {
auto status = sender_.Send(std::move(coverage_data));
FX_CHECK(status == ZX_OK) << zx_status_get_string(status);
}
Result<CoverageData> FakeCoverage::TryReceive() { return receiver_.TryReceive(); }
Promise<CoverageData> FakeCoverage::Receive() { return receiver_.Receive(); }
} // namespace fuzzing