blob: a2ae493166a64eb18ff0a66945f7e073ace786fa [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 <zircon/status.h>
#include <memory>
#include <gtest/gtest.h>
#include "src/sys/fuzzing/common/async-socket.h"
#include "src/sys/fuzzing/common/status.h"
#include "src/sys/fuzzing/common/testing/async-test.h"
#include "src/sys/fuzzing/common/testing/corpus-reader.h"
#include "src/sys/fuzzing/common/testing/monitor.h"
#include "src/sys/fuzzing/common/testing/runner.h"
namespace fuzzing {
namespace {
using fuchsia::fuzzer::ControllerPtr;
using fuchsia::fuzzer::ControllerSyncPtr;
using fuchsia::fuzzer::UpdateReason;
// Test fixtures.
// Base class for |Controller| unit tests.
class ControllerTest : public AsyncTest {
public:
// Implicitly tests |Controller::SetRunner| and |Controller::Bind|.
void Bind(fidl::InterfaceRequest<Controller> request) {
runner_ = FakeRunner::MakePtr(executor());
controller_ = std::make_unique<ControllerImpl>(executor());
controller_->SetRunner(runner_);
controller_->Bind(std::move(request));
}
auto runner() const { return std::static_pointer_cast<FakeRunner>(runner_); }
private:
std::unique_ptr<ControllerImpl> controller_;
RunnerPtr runner_;
};
// Unit tests.
TEST_F(ControllerTest, ConfigureAndGetOptions) {
ControllerPtr controller;
Bind(controller.NewRequest(dispatcher()));
// GetOptions without Configure.
Options options1;
Bridge<Options> bridge1;
controller->GetOptions(bridge1.completer.bind());
FUZZING_EXPECT_OK(bridge1.consumer.promise_or(fpromise::error()), &options1);
RunUntilIdle();
EXPECT_NE(options1.seed(), 0U);
// Configure.
uint32_t runs = 1000;
zx::duration max_total_time = zx::sec(300);
uint32_t seed = 42;
uint32_t max_input_size = 1ULL << 10;
uint16_t mutation_depth = 8;
bool detect_exits = true;
bool detect_leaks = false;
zx::duration run_limit = zx::sec(20);
options1.set_runs(runs);
options1.set_max_total_time(max_total_time.get());
options1.set_seed(seed);
options1.set_max_input_size(max_input_size);
options1.set_mutation_depth(mutation_depth);
options1.set_detect_exits(detect_exits);
options1.set_detect_leaks(detect_leaks);
options1.set_run_limit(run_limit.get());
auto options2 = CopyOptions(options1);
Bridge<zx_status_t> bridge2;
controller->Configure(std::move(options1), bridge2.completer.bind());
FUZZING_EXPECT_OK(bridge2.consumer.promise_or(fpromise::ok(ZX_ERR_CANCELED)), ZX_OK);
RunUntilIdle();
// Can Configure again.
uint64_t malloc_limit = 64ULL << 10;
uint64_t oom_limit = 1ULL << 20;
zx::duration purge_interval = zx::sec(10);
int32_t malloc_exitcode = 1000;
int32_t death_exitcode = 1001;
int32_t leak_exitcode = 1002;
int32_t oom_exitcode = 1003;
zx::duration pulse_interval = zx::sec(3);
options2.set_malloc_limit(malloc_limit);
options2.set_oom_limit(oom_limit);
options2.set_purge_interval(purge_interval.get());
options2.set_malloc_exitcode(malloc_exitcode);
options2.set_death_exitcode(death_exitcode);
options2.set_leak_exitcode(leak_exitcode);
options2.set_oom_exitcode(oom_exitcode);
options2.set_pulse_interval(pulse_interval.get());
Bridge<zx_status_t> bridge3;
controller->Configure(std::move(options2), bridge3.completer.bind());
FUZZING_EXPECT_OK(bridge3.consumer.promise_or(fpromise::ok(ZX_ERR_CANCELED)), ZX_OK);
RunUntilIdle();
// Changes are reflected.
Options options3;
Bridge<Options> bridge4;
controller->GetOptions(bridge4.completer.bind());
FUZZING_EXPECT_OK(bridge4.consumer.promise_or(fpromise::error()), &options3);
RunUntilIdle();
EXPECT_EQ(options3.runs(), runs);
EXPECT_EQ(options3.max_total_time(), max_total_time.get());
EXPECT_EQ(options3.seed(), seed);
EXPECT_EQ(options3.max_input_size(), max_input_size);
EXPECT_EQ(options3.mutation_depth(), mutation_depth);
EXPECT_EQ(options3.detect_exits(), detect_exits);
EXPECT_EQ(options3.detect_leaks(), detect_leaks);
EXPECT_EQ(options3.run_limit(), run_limit.get());
EXPECT_EQ(options3.malloc_limit(), malloc_limit);
EXPECT_EQ(options3.oom_limit(), oom_limit);
EXPECT_EQ(options3.purge_interval(), purge_interval.get());
EXPECT_EQ(options3.malloc_exitcode(), malloc_exitcode);
EXPECT_EQ(options3.death_exitcode(), death_exitcode);
EXPECT_EQ(options3.leak_exitcode(), leak_exitcode);
EXPECT_EQ(options3.oom_exitcode(), oom_exitcode);
EXPECT_EQ(options3.pulse_interval(), pulse_interval.get());
}
TEST_F(ControllerTest, AddToCorpus) {
ControllerPtr controller;
Bind(controller.NewRequest(dispatcher()));
Input input0;
Input seed_input1({0xde, 0xad});
Input seed_input2({0xbe, 0xef});
Input live_input3({0xfe, 0xed});
Input live_input4({0xfa, 0xce});
// Interleave the calls.
Bridge<zx_status_t> bridge1, bridge2, bridge3, bridge4;
controller->AddToCorpus(CorpusType::LIVE, AsyncSocketWrite(executor(), live_input3.Duplicate()),
bridge3.completer.bind());
controller->AddToCorpus(CorpusType::SEED, AsyncSocketWrite(executor(), seed_input1.Duplicate()),
bridge1.completer.bind());
controller->AddToCorpus(CorpusType::SEED, AsyncSocketWrite(executor(), seed_input2.Duplicate()),
bridge2.completer.bind());
controller->AddToCorpus(CorpusType::LIVE, AsyncSocketWrite(executor(), live_input4.Duplicate()),
bridge4.completer.bind());
FUZZING_EXPECT_OK(bridge1.consumer.promise_or(fpromise::error()));
FUZZING_EXPECT_OK(bridge2.consumer.promise_or(fpromise::error()));
FUZZING_EXPECT_OK(bridge3.consumer.promise_or(fpromise::error()));
FUZZING_EXPECT_OK(bridge4.consumer.promise_or(fpromise::error()));
RunUntilIdle();
EXPECT_EQ(runner()->ReadFromCorpus(CorpusType::SEED, 0).ToHex(), input0.ToHex());
EXPECT_EQ(runner()->ReadFromCorpus(CorpusType::SEED, 1).ToHex(), seed_input1.ToHex());
EXPECT_EQ(runner()->ReadFromCorpus(CorpusType::SEED, 2).ToHex(), seed_input2.ToHex());
EXPECT_EQ(runner()->ReadFromCorpus(CorpusType::SEED, 3).ToHex(), input0.ToHex());
EXPECT_EQ(runner()->ReadFromCorpus(CorpusType::LIVE, 0).ToHex(), input0.ToHex());
EXPECT_EQ(runner()->ReadFromCorpus(CorpusType::LIVE, 1).ToHex(), live_input3.ToHex());
EXPECT_EQ(runner()->ReadFromCorpus(CorpusType::LIVE, 2).ToHex(), live_input4.ToHex());
EXPECT_EQ(runner()->ReadFromCorpus(CorpusType::LIVE, 3).ToHex(), input0.ToHex());
}
TEST_F(ControllerTest, ReadCorpus) {
ControllerPtr controller;
Bind(controller.NewRequest(dispatcher()));
Input input0;
Input input1({0xde, 0xad});
Input input2({0xbe, 0xef});
Input input3({0xfe, 0xed});
Input input4({0xfa, 0xce});
EXPECT_EQ(runner()->AddToCorpus(CorpusType::SEED, input1.Duplicate()), ZX_OK);
EXPECT_EQ(runner()->AddToCorpus(CorpusType::SEED, input2.Duplicate()), ZX_OK);
EXPECT_EQ(runner()->AddToCorpus(CorpusType::LIVE, input3.Duplicate()), ZX_OK);
EXPECT_EQ(runner()->AddToCorpus(CorpusType::LIVE, input4.Duplicate()), ZX_OK);
FakeCorpusReader seed_reader(executor());
FakeCorpusReader live_reader(executor());
Bridge<> seed_bridge;
Bridge<> live_bridge;
controller->ReadCorpus(CorpusType::SEED, seed_reader.NewBinding(), seed_bridge.completer.bind());
controller->ReadCorpus(CorpusType::LIVE, live_reader.NewBinding(), live_bridge.completer.bind());
FUZZING_EXPECT_OK(seed_bridge.consumer.promise_or(fpromise::error()));
FUZZING_EXPECT_OK(live_bridge.consumer.promise_or(fpromise::error()));
RunUntilIdle();
const auto& seed_corpus = seed_reader.corpus();
ASSERT_EQ(seed_corpus.size(), 3U);
EXPECT_EQ(seed_corpus[0], input1);
EXPECT_EQ(seed_corpus[1], input2);
EXPECT_EQ(seed_corpus[2], input0);
const auto& live_corpus = live_reader.corpus();
ASSERT_EQ(live_corpus.size(), 3U);
EXPECT_EQ(live_corpus[0], input3);
EXPECT_EQ(live_corpus[1], input4);
EXPECT_EQ(live_corpus[2], input0);
}
TEST_F(ControllerTest, WriteDictionary) {
ControllerPtr controller;
Bind(controller.NewRequest(dispatcher()));
Bridge<zx_status_t> bridge1;
controller->WriteDictionary(AsyncSocketWrite(executor(), FakeRunner::invalid_dictionary()),
bridge1.completer.bind());
FUZZING_EXPECT_OK(bridge1.consumer.promise_or(fpromise::error()), ZX_ERR_INVALID_ARGS);
Bridge<zx_status_t> bridge2;
controller->WriteDictionary(AsyncSocketWrite(executor(), FakeRunner::valid_dictionary()),
bridge2.completer.bind());
FUZZING_EXPECT_OK(bridge2.consumer.promise_or(fpromise::error()), ZX_OK);
RunUntilIdle();
}
TEST_F(ControllerTest, ReadDictionary) {
ControllerPtr controller;
Bind(controller.NewRequest());
auto dict = FakeRunner::valid_dictionary();
EXPECT_EQ(runner()->ParseDictionary(dict), ZX_OK);
Bridge<FidlInput> bridge;
controller->ReadDictionary(bridge.completer.bind());
FUZZING_EXPECT_OK(bridge.consumer.promise_or(fpromise::error())
.or_else([] { return fpromise::error(ZX_ERR_CANCELED); })
.and_then([&](FidlInput& fidl_input) {
return AsyncSocketRead(executor(), std::move(fidl_input));
}),
std::move(dict));
RunUntilIdle();
}
TEST_F(ControllerTest, GetStatus) {
ControllerPtr controller;
Bind(controller.NewRequest(dispatcher()));
Status result;
Status status;
status.set_running(true);
status.set_runs(42);
status.set_elapsed(zx::sec(15).get());
status.set_covered_pcs(5);
status.set_covered_features(10);
status.set_corpus_num_inputs(15);
status.set_corpus_total_size(25);
auto expected = CopyStatus(status);
runner()->set_status(std::move(status));
Bridge<Status> bridge;
controller->GetStatus(bridge.completer.bind());
FUZZING_EXPECT_OK(bridge.consumer.promise_or(fpromise::error()), &result);
RunUntilIdle();
EXPECT_EQ(result.running(), expected.running());
EXPECT_EQ(result.runs(), expected.runs());
EXPECT_EQ(result.elapsed(), expected.elapsed());
EXPECT_EQ(result.covered_pcs(), expected.covered_pcs());
EXPECT_EQ(result.covered_features(), expected.covered_features());
EXPECT_EQ(result.corpus_num_inputs(), expected.corpus_num_inputs());
EXPECT_EQ(result.corpus_total_size(), expected.corpus_total_size());
}
TEST_F(ControllerTest, AddMonitor) {
ControllerPtr controller;
Bind(controller.NewRequest());
FakeMonitor monitor(executor());
Status status;
status.set_runs(13);
auto expected = CopyStatus(status);
runner()->set_status(std::move(status));
Bridge<> bridge;
controller->AddMonitor(monitor.NewBinding(), bridge.completer.bind());
FUZZING_EXPECT_OK(bridge.consumer.promise_or(fpromise::error()));
RunUntilIdle();
runner()->UpdateMonitors(UpdateReason::PULSE);
FUZZING_EXPECT_OK(monitor.AwaitUpdate());
RunUntilIdle();
ASSERT_FALSE(monitor.empty());
EXPECT_EQ(monitor.status().runs(), expected.runs());
EXPECT_EQ(monitor.reason(), UpdateReason::PULSE);
}
TEST_F(ControllerTest, ExecuteAndGetResults) {
ControllerPtr controller;
Bind(controller.NewRequest());
Input input({0xde, 0xad, 0xbe, 0xef});
runner()->set_error(ZX_ERR_WRONG_TYPE);
ZxBridge<FuzzResult> bridge1;
controller->Execute(AsyncSocketWrite(executor(), input.Duplicate()),
ZxBind<FuzzResult>(std::move(bridge1.completer)));
FUZZING_EXPECT_ERROR(bridge1.consumer.promise_or(fpromise::error(ZX_ERR_CANCELED)),
ZX_ERR_WRONG_TYPE);
RunUntilIdle();
runner()->set_error(ZX_OK);
runner()->set_result(FuzzResult::OOM);
ZxBridge<FuzzResult> bridge2;
controller->Execute(AsyncSocketWrite(executor(), input.Duplicate()),
ZxBind<FuzzResult>(std::move(bridge2.completer)));
FUZZING_EXPECT_OK(bridge2.consumer.promise(), FuzzResult::OOM);
RunUntilIdle();
Bridge<FidlArtifact> bridge3;
controller->GetResults([completer = std::move(bridge3.completer)](FuzzResult fuzz_result,
FidlInput fidl_input) mutable {
completer.complete_ok(MakeFidlArtifact(fuzz_result, std::move(fidl_input)));
});
Artifact artifact(FuzzResult::OOM, input.Duplicate());
FUZZING_EXPECT_OK(bridge3.consumer.promise_or(fpromise::error())
.or_else([] { return fpromise::error(ZX_ERR_CANCELED); })
.and_then([&](FidlArtifact& fidl_artifact) {
return AsyncSocketRead(executor(), std::move(fidl_artifact));
}),
std::move(artifact));
RunUntilIdle();
}
TEST_F(ControllerTest, Minimize) {
ControllerPtr controller;
Bind(controller.NewRequest());
Input input({0xde, 0xad, 0xbe, 0xef});
Input minimized({0xde, 0xbe});
runner()->set_error(ZX_ERR_WRONG_TYPE);
ZxBridge<FidlInput> bridge1;
controller->Minimize(AsyncSocketWrite(executor(), input.Duplicate()),
ZxBind<FidlInput>(std::move(bridge1.completer)));
FUZZING_EXPECT_ERROR(bridge1.consumer.promise_or(fpromise::error(ZX_ERR_CANCELED)),
ZX_ERR_WRONG_TYPE);
RunUntilIdle();
runner()->set_error(ZX_OK);
runner()->set_result_input(minimized);
ZxBridge<FidlInput> bridge2;
controller->Minimize(AsyncSocketWrite(executor(), input.Duplicate()),
ZxBind<FidlInput>(std::move(bridge2.completer)));
FUZZING_EXPECT_OK(bridge2.consumer.promise_or(fpromise::error(ZX_ERR_CANCELED))
.and_then([&](FidlInput& fidl_input) {
return AsyncSocketRead(executor(), std::move(fidl_input));
}),
std::move(minimized));
RunUntilIdle();
}
TEST_F(ControllerTest, Cleanse) {
ControllerPtr controller;
Bind(controller.NewRequest());
Input input({0xde, 0xad, 0xbe, 0xef});
Input cleansed({0x20, 0x20, 0xbe, 0xff});
runner()->set_error(ZX_ERR_WRONG_TYPE);
ZxBridge<FidlInput> bridge1;
controller->Cleanse(AsyncSocketWrite(executor(), input.Duplicate()),
ZxBind<FidlInput>(std::move(bridge1.completer)));
FUZZING_EXPECT_ERROR(bridge1.consumer.promise_or(fpromise::error(ZX_ERR_CANCELED)),
ZX_ERR_WRONG_TYPE);
RunUntilIdle();
runner()->set_error(ZX_OK);
runner()->set_result_input(cleansed);
ZxBridge<FidlInput> bridge2;
controller->Cleanse(AsyncSocketWrite(executor(), input.Duplicate()),
ZxBind<FidlInput>(std::move(bridge2.completer)));
FUZZING_EXPECT_OK(bridge2.consumer.promise_or(fpromise::error(ZX_ERR_CANCELED))
.and_then([&](FidlInput& fidl_input) {
return AsyncSocketRead(executor(), std::move(fidl_input));
}),
std::move(cleansed));
RunUntilIdle();
}
TEST_F(ControllerTest, Fuzz) {
ControllerPtr controller;
Bind(controller.NewRequest());
Artifact artifact(FuzzResult::CRASH, {0xde, 0xad, 0xbe, 0xef});
runner()->set_error(ZX_ERR_WRONG_TYPE);
ZxBridge<FidlArtifact> bridge1;
controller->Fuzz(ZxBind<FidlArtifact>(std::move(bridge1.completer)));
FUZZING_EXPECT_ERROR(bridge1.consumer.promise_or(fpromise::error(ZX_ERR_CANCELED)),
ZX_ERR_WRONG_TYPE);
RunUntilIdle();
runner()->set_error(ZX_OK);
runner()->set_result(artifact.fuzz_result());
runner()->set_result_input(artifact.input());
ZxBridge<FidlArtifact> bridge2;
controller->Fuzz(ZxBind<FidlArtifact>(std::move(bridge2.completer)));
FUZZING_EXPECT_OK(bridge2.consumer.promise_or(fpromise::error(ZX_ERR_CANCELED))
.and_then([&](FidlArtifact& fidl_artifact) {
return AsyncSocketRead(executor(), std::move(fidl_artifact));
}),
std::move(artifact));
RunUntilIdle();
}
TEST_F(ControllerTest, Merge) {
ControllerPtr controller;
Bind(controller.NewRequest(dispatcher()));
runner()->set_error(ZX_ERR_WRONG_TYPE);
Bridge<zx_status_t> bridge1;
controller->Merge(bridge1.completer.bind());
FUZZING_EXPECT_OK(bridge1.consumer.promise_or(fpromise::error()), ZX_ERR_WRONG_TYPE);
RunUntilIdle();
runner()->set_error(ZX_OK);
Bridge<zx_status_t> bridge2;
controller->Merge(bridge2.completer.bind());
FUZZING_EXPECT_OK(bridge2.consumer.promise_or(fpromise::error()), ZX_OK);
RunUntilIdle();
}
} // namespace
} // namespace fuzzing