| // 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 <fbl/string_piece.h> |
| |
| namespace fuzzing { |
| namespace { |
| |
| using ::fuchsia::fuzzer::Coverage; |
| using ::fuchsia::fuzzer::DataProvider; |
| using ::fuchsia::fuzzer::LlvmFuzzerPtr; |
| |
| // 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 Coverage 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 const char *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(fbl::constexpr_strlen(kLabel))); |
| } 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 |