blob: 394c9d63f9f1df7fd29bc50eb671bbae80947441 [file] [log] [blame]
// Copyright 2020 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 "engine.h"
#include <lib/async-loop/default.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include <string_view>
namespace fuzzing {
namespace {
using ::fuchsia::fuzzer::DataProvider;
using ::fuchsia::fuzzer::LlvmFuzzerPtr;
using ::fuchsia::fuzzer::Proxy;
// Usage message for direct invocation.
void Usage(const char *argv0) {
std::cout << "usage: " << argv0 << " [options] [libFuzzer-options...]" << std::endl;
std::cout << std::endl;
std::cout << "options:" << std::endl;
std::cout << " -h|--help: Print this message and exit." << std::endl;
std::cout << " --label=<label>: Partition test input on data consumer label." << std::endl;
std::cout << std::endl;
std::cout << "Unrecognized items are passed to libFuzzer." << std::endl;
exit(0);
}
// Stop the singleton when exiting.
void AtExit() { EngineImpl::GetInstance(false /* autoconnect */)->Stop(ZX_OK); }
} // namespace
// Public methods
/* static */ EngineImpl *EngineImpl::GetInstance(bool autostart) {
static EngineImpl engine(autostart);
return &engine;
}
EngineImpl::~EngineImpl() {
// Called on exit.
Stop(ZX_OK);
}
zx_status_t EngineImpl::SetLlvmFuzzer(LlvmFuzzerPtr llvm_fuzzer) {
// Set the pointer to the Proxy service.
if (!llvm_fuzzer.is_bound()) {
return ZX_ERR_INVALID_ARGS;
}
Stop(ZX_ERR_PEER_CLOSED);
llvm_fuzzer_ = std::move(llvm_fuzzer);
return ZX_OK;
}
void EngineImpl::Start(std::vector<std::string> options, StartCallback callback) {
if (callback_) {
FX_LOGS(ERROR) << "Already started.";
callback(ZX_ERR_BAD_STATE);
return;
}
if (!llvm_fuzzer_.is_bound()) {
FX_LOGS(ERROR) << "LlvmFuzzer not set for engine.";
callback(ZX_ERR_BAD_STATE);
return;
}
callback_ = std::move(callback);
zx::vmo vmo;
data_provider_.Initialize(&vmo);
llvm_fuzzer_->Initialize(std::move(vmo), std::move(options),
[this](int result, std::vector<std::string> modified) {
options_ = std::move(modified);
if (result != 0) {
Stop(result);
}
sync_completion_signal(&sync_);
});
// libFuzzer's own std::atexit calls have run, so this will be the first exit callback invoked.
std::atexit(::fuzzing::AtExit);
}
int EngineImpl::Initialize(int *argc, char ***argv) {
if (!argv_.empty()) {
FX_LOGS(ERROR) << "Already initialized.";
return ZX_ERR_BAD_STATE;
}
// Extract the consumer labels.
static constexpr std::string_view kLabel = "--label=";
for (int i = 0; i < *argc; ++i) {
std::string arg((*argv)[i]);
if (arg == "-h" || arg == "--help") {
Usage((*argv)[0]);
} else if (arg.rfind(kLabel, 0) == 0) {
data_provider_.AddConsumerLabel(arg.substr(kLabel.size()));
} else {
argv_.push_back((*argv)[i]);
}
}
// Wait until the engine is started, then append the libFuzzer options.
sync_completion_wait(&sync_, ZX_TIME_INFINITE);
std::transform(options_.begin(), options_.end(), std::back_inserter(argv_),
[](const std::string &s) { return const_cast<char *>(s.c_str()); });
*argc = argv_.size();
*argv = &argv_[0];
return ZX_OK;
}
int EngineImpl::TestOneInput(const uint8_t *data, size_t size) {
if (!sync_completion_signaled(&sync_)) {
FX_LOGS(ERROR) << "Not initialized.";
return ZX_ERR_BAD_STATE;
}
zx_status_t status = data_provider_.PartitionTestInput(data, size);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "failed to start iteration: " << zx_status_get_string(status);
return status;
}
sync_completion_t sync;
llvm_fuzzer_->TestOneInput([&status, &sync](int result) {
status = result;
sync_completion_signal(&sync);
});
sync_completion_wait(&sync, ZX_TIME_INFINITE);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "fuzz target function returned non-zero status: " << status;
return status;
}
if ((status = data_provider_.CompleteIteration()) != ZX_OK ||
(status = coverage_.CompleteIteration()) != ZX_OK) {
FX_LOGS(ERROR) << "failed to complete iteration: " << zx_status_get_string(status);
return status;
}
return ZX_OK;
}
void EngineImpl::Stop(zx_status_t status) {
coverage_.Reset();
data_provider_.Reset();
llvm_fuzzer_.Unbind();
if (callback_) {
callback_(status);
callback_ = nullptr;
}
sync_completion_reset(&sync_);
}
// Private methods
EngineImpl::EngineImpl(bool autoconnect) {
if (autoconnect) {
loop_ = std::make_unique<async::Loop>(&kAsyncLoopConfigNoAttachToCurrentThread);
FX_CHECK(context_->outgoing()->ServeFromStartupInfo(dispatcher_) == ZX_OK);
context_->outgoing()->AddPublicService(coverage_.GetHandler());
context_->outgoing()->AddPublicService(data_provider_.GetHandler());
FX_CHECK(loop_->StartThread() == ZX_OK);
auto svc = sys::ServiceDirectory::CreateFromNamespace();
LlvmFuzzerPtr llvm_fuzzer;
svc->Connect(llvm_fuzzer.NewRequest(loop_->dispatcher()));
FX_CHECK(SetLlvmFuzzer(std::move(llvm_fuzzer)) == ZX_OK);
}
}
void EngineImpl::UseContextImpl(std::unique_ptr<sys::ComponentContext> context) {
Stop(ZX_OK);
argv_.clear();
context_ = std::move(context);
context_->outgoing()->AddPublicService(coverage_.GetHandler());
context_->outgoing()->AddPublicService(data_provider_.GetHandler());
}
} // namespace fuzzing