blob: afa1ac1d496e6e8cd63752810df29278805b4a5b [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/status.h>
#include <gtest/gtest.h>
#include "src/sys/fuzzing/common/binding.h"
#include "src/sys/fuzzing/common/dispatcher.h"
#include "src/sys/fuzzing/common/status.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"
#include "src/sys/fuzzing/common/testing/transceiver.h"
namespace fuzzing {
namespace {
using fuchsia::fuzzer::ControllerSyncPtr;
using fuchsia::fuzzer::ProcessProxySyncPtr;
using fuchsia::fuzzer::UpdateReason;
// Test fixtures.
// Base class for |Controller| unit tests.
class ControllerTest : public ::testing::Test {
public:
// Implicitly tests |Controller::SetRunner| and |Controller::Bind|.
ControllerSyncPtr Bind() {
// The shared_ptr will be responsible for deleting the FakeRunner memory.
auto runner = std::make_shared<FakeRunner>();
runner_ = runner.get();
controller_.SetRunner(runner);
ControllerSyncPtr controller;
controller_.Bind(controller.NewRequest());
return controller;
}
void AddToCorpus(CorpusType corpus_type, Input input) {
runner_->AddToCorpus(corpus_type, std::move(input));
}
Input ReadFromCorpus(CorpusType corpus_type, size_t offset) {
return runner_->ReadFromCorpus(corpus_type, offset);
}
zx_status_t ParseDictionary(const Input& input) { return runner_->ParseDictionary(input); }
void SetError(zx_status_t error) { runner_->set_error(error); }
void SetResult(Result result) { runner_->set_result(result); }
void SetResultInput(const Input& input) { runner_->set_result_input(input); }
void SetStatus(Status status) { runner_->set_status(std::move(status)); }
void UpdateMonitors(UpdateReason reason) { runner_->UpdateMonitors(reason); }
FidlInput Transmit(const Input& input) { return transceiver_.Transmit(input.Duplicate()); }
// Synchronously receives and returns an |Input| from a provided |FidlInput|.
Input Receive(FidlInput fidl_input) { return transceiver_.Receive(std::move(fidl_input)); }
private:
ControllerImpl controller_;
FakeRunner* runner_;
FakeTransceiver transceiver_;
};
// Unit tests.
TEST_F(ControllerTest, ConfigureAndGetOptions) {
auto controller = Bind();
// GetOptions without Configure.
Options options1;
EXPECT_EQ(controller->GetOptions(&options1), ZX_OK);
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());
zx_status_t status;
auto options2 = CopyOptions(options1);
EXPECT_EQ(controller->Configure(std::move(options1), &status), ZX_OK);
EXPECT_EQ(status, ZX_OK);
// 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());
EXPECT_EQ(controller->Configure(std::move(options2), &status), ZX_OK);
EXPECT_EQ(status, ZX_OK);
// Changes are reflected.
Options options3;
EXPECT_EQ(controller->GetOptions(&options3), ZX_OK);
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) {
auto controller = Bind();
Input input0;
Input seed_input1({0xde, 0xad});
Input seed_input2({0xbe, 0xef});
Input live_input3({0xfe, 0xed});
Input live_input4({0xfa, 0xce});
zx_status_t result;
// Interleave the calls.
EXPECT_EQ(controller->AddToCorpus(CorpusType::LIVE, Transmit(live_input3), &result), ZX_OK);
EXPECT_EQ(result, ZX_OK);
EXPECT_EQ(controller->AddToCorpus(CorpusType::SEED, Transmit(seed_input1), &result), ZX_OK);
EXPECT_EQ(result, ZX_OK);
EXPECT_EQ(controller->AddToCorpus(CorpusType::SEED, Transmit(seed_input2), &result), ZX_OK);
EXPECT_EQ(result, ZX_OK);
EXPECT_EQ(controller->AddToCorpus(CorpusType::LIVE, Transmit(live_input4), &result), ZX_OK);
EXPECT_EQ(result, ZX_OK);
EXPECT_EQ(ReadFromCorpus(CorpusType::SEED, 0).ToHex(), input0.ToHex());
EXPECT_EQ(ReadFromCorpus(CorpusType::SEED, 1).ToHex(), seed_input1.ToHex());
EXPECT_EQ(ReadFromCorpus(CorpusType::SEED, 2).ToHex(), seed_input2.ToHex());
EXPECT_EQ(ReadFromCorpus(CorpusType::SEED, 3).ToHex(), input0.ToHex());
EXPECT_EQ(ReadFromCorpus(CorpusType::LIVE, 0).ToHex(), input0.ToHex());
EXPECT_EQ(ReadFromCorpus(CorpusType::LIVE, 1).ToHex(), live_input3.ToHex());
EXPECT_EQ(ReadFromCorpus(CorpusType::LIVE, 2).ToHex(), live_input4.ToHex());
EXPECT_EQ(ReadFromCorpus(CorpusType::LIVE, 3).ToHex(), input0.ToHex());
}
TEST_F(ControllerTest, ReadCorpus) {
auto controller = Bind();
Input input0;
Input input1({0xde, 0xad});
Input input2({0xbe, 0xef});
Input input3({0xfe, 0xed});
Input input4({0xfa, 0xce});
AddToCorpus(CorpusType::SEED, input1.Duplicate());
AddToCorpus(CorpusType::SEED, input2.Duplicate());
AddToCorpus(CorpusType::LIVE, input3.Duplicate());
AddToCorpus(CorpusType::LIVE, input4.Duplicate());
auto dispatcher = std::make_shared<Dispatcher>();
FakeCorpusReader seed_reader(dispatcher);
FakeCorpusReader live_reader(dispatcher);
EXPECT_EQ(controller->ReadCorpus(CorpusType::SEED, seed_reader.NewBinding()), ZX_OK);
EXPECT_EQ(controller->ReadCorpus(CorpusType::LIVE, live_reader.NewBinding()), ZX_OK);
// Interleave the calls.
ASSERT_TRUE(live_reader.AwaitNext());
EXPECT_EQ(live_reader.GetNext().ToHex(), input3.ToHex());
ASSERT_TRUE(seed_reader.AwaitNext());
EXPECT_EQ(seed_reader.GetNext().ToHex(), input1.ToHex());
ASSERT_TRUE(live_reader.AwaitNext());
EXPECT_EQ(live_reader.GetNext().ToHex(), input4.ToHex());
ASSERT_TRUE(seed_reader.AwaitNext());
EXPECT_EQ(seed_reader.GetNext().ToHex(), input2.ToHex());
// All inputs have been sent.
EXPECT_FALSE(live_reader.AwaitNext());
EXPECT_FALSE(live_reader.AwaitNext());
}
TEST_F(ControllerTest, WriteDictionary) {
auto controller = Bind();
auto invalid = FakeRunner::invalid_dictionary();
auto valid = FakeRunner::valid_dictionary();
zx_status_t result;
EXPECT_EQ(controller->WriteDictionary(Transmit(invalid), &result), ZX_OK);
EXPECT_EQ(result, ZX_ERR_INVALID_ARGS);
EXPECT_EQ(controller->WriteDictionary(Transmit(valid), &result), ZX_OK);
EXPECT_EQ(result, ZX_OK);
}
TEST_F(ControllerTest, ReadDictionary) {
auto controller = Bind();
FidlInput result;
auto dict = FakeRunner::valid_dictionary();
EXPECT_EQ(ParseDictionary(dict), ZX_OK);
EXPECT_EQ(controller->ReadDictionary(&result), ZX_OK);
auto received = Receive(std::move(result));
EXPECT_EQ(received.ToHex(), dict.ToHex());
}
TEST_F(ControllerTest, GetStatus) {
auto controller = Bind();
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);
SetStatus(std::move(status));
EXPECT_EQ(controller->GetStatus(&result), ZX_OK);
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) {
auto controller = Bind();
FakeMonitor monitor;
Status status;
status.set_runs(13);
auto expected = CopyStatus(status);
SetStatus(std::move(status));
EXPECT_EQ(controller->AddMonitor(monitor.NewBinding()), ZX_OK);
UpdateMonitors(UpdateReason::PULSE);
UpdateReason reason;
auto updated = monitor.NextStatus(&reason);
EXPECT_EQ(updated.runs(), expected.runs());
EXPECT_EQ(reason, UpdateReason::PULSE);
}
TEST_F(ControllerTest, GetResults) {
auto controller = Bind();
Result result;
FidlInput fidl_input;
Input result_input({0xde, 0xad, 0xbe, 0xef});
SetResult(Result::DEATH);
SetResultInput(result_input);
EXPECT_EQ(controller->GetResults(&result, &fidl_input), ZX_OK);
EXPECT_EQ(result, Result::DEATH);
auto received = Receive(std::move(fidl_input));
EXPECT_EQ(received.ToHex(), result_input.ToHex());
}
TEST_F(ControllerTest, Execute) {
auto controller = Bind();
::fuchsia::fuzzer::Controller_Execute_Result result;
Input input({0xde, 0xad, 0xbe, 0xef});
SetError(ZX_ERR_WRONG_TYPE);
EXPECT_EQ(controller->Execute(Transmit(input), &result), ZX_OK);
ASSERT_TRUE(result.is_err());
EXPECT_EQ(result.err(), ZX_ERR_WRONG_TYPE);
SetError(ZX_OK);
SetResult(Result::OOM);
EXPECT_EQ(controller->Execute(Transmit(input), &result), ZX_OK);
ASSERT_TRUE(result.is_response());
auto& response = result.response();
EXPECT_EQ(response.result, Result::OOM);
}
TEST_F(ControllerTest, Minimize) {
auto controller = Bind();
::fuchsia::fuzzer::Controller_Minimize_Result result;
Input input({0xde, 0xad, 0xbe, 0xef});
Input minimized({0xde, 0xbe});
SetError(ZX_ERR_WRONG_TYPE);
EXPECT_EQ(controller->Minimize(Transmit(input), &result), ZX_OK);
ASSERT_TRUE(result.is_err());
EXPECT_EQ(result.err(), ZX_ERR_WRONG_TYPE);
SetError(ZX_OK);
SetResultInput(minimized);
EXPECT_EQ(controller->Minimize(Transmit(input), &result), ZX_OK);
ASSERT_TRUE(result.is_response());
auto& response = result.response();
auto received = Receive(std::move(response.minimized));
EXPECT_EQ(received.ToHex(), minimized.ToHex());
}
TEST_F(ControllerTest, Cleanse) {
auto controller = Bind();
::fuchsia::fuzzer::Controller_Cleanse_Result result;
Input input({0xde, 0xad, 0xbe, 0xef});
Input cleansed({0x20, 0x20, 0xbe, 0xff});
SetError(ZX_ERR_WRONG_TYPE);
EXPECT_EQ(controller->Cleanse(Transmit(input), &result), ZX_OK);
ASSERT_TRUE(result.is_err());
EXPECT_EQ(result.err(), ZX_ERR_WRONG_TYPE);
SetError(ZX_OK);
SetResultInput(cleansed);
EXPECT_EQ(controller->Cleanse(Transmit(input), &result), ZX_OK);
ASSERT_TRUE(result.is_response());
auto& response = result.response();
auto received = Receive(std::move(response.cleansed));
EXPECT_EQ(received.ToHex(), cleansed.ToHex());
}
TEST_F(ControllerTest, Fuzz) {
auto controller = Bind();
::fuchsia::fuzzer::Controller_Fuzz_Result result;
Input fuzzed({0xde, 0xad, 0xbe, 0xef});
SetError(ZX_ERR_WRONG_TYPE);
EXPECT_EQ(controller->Fuzz(&result), ZX_OK);
ASSERT_TRUE(result.is_err());
EXPECT_EQ(result.err(), ZX_ERR_WRONG_TYPE);
SetError(ZX_OK);
SetResult(Result::CRASH);
SetResultInput(fuzzed);
EXPECT_EQ(controller->Fuzz(&result), ZX_OK);
ASSERT_TRUE(result.is_response());
auto& response = result.response();
EXPECT_EQ(response.result, Result::CRASH);
auto received = Receive(std::move(response.error_input));
EXPECT_EQ(received.ToHex(), fuzzed.ToHex());
}
TEST_F(ControllerTest, Merge) {
auto controller = Bind();
zx_status_t result;
SetError(ZX_ERR_WRONG_TYPE);
EXPECT_EQ(controller->Merge(&result), ZX_OK);
EXPECT_EQ(result, ZX_ERR_WRONG_TYPE);
SetError(ZX_OK);
EXPECT_EQ(controller->Merge(&result), ZX_OK);
EXPECT_EQ(result, ZX_OK);
}
} // namespace
} // namespace fuzzing