blob: f9668ea330f978de83c9df0d364217234ad7405e [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/framework/engine/process-proxy.h"
#include <lib/sync/completion.h>
#include <sstream>
#include <gtest/gtest.h>
#include "src/sys/fuzzing/common/options.h"
#include "src/sys/fuzzing/framework/engine/process-proxy-test.h"
#include "src/sys/fuzzing/framework/target/module.h"
#include "src/sys/fuzzing/framework/testing/module.h"
#include "src/sys/fuzzing/framework/testing/target.h"
namespace fuzzing {
namespace {
// Unit tests.
TEST_F(ProcessProxyTest, AddDefaults) {
Options options;
ProcessProxyImpl::AddDefaults(&options);
EXPECT_EQ(options.malloc_exitcode(), kDefaultMallocExitcode);
EXPECT_EQ(options.death_exitcode(), kDefaultDeathExitcode);
EXPECT_EQ(options.leak_exitcode(), kDefaultLeakExitcode);
EXPECT_EQ(options.oom_exitcode(), kDefaultOomExitcode);
}
TEST_F(ProcessProxyTest, Connect) {
ProcessProxyImpl impl(dispatcher(), pool());
uint32_t runs = 1000;
int64_t run_limit = 20;
auto options1 = ProcessProxyTest::DefaultOptions();
options1->set_runs(runs);
options1->set_run_limit(run_limit);
impl.Configure(options1);
impl.SetHandlers(IgnoreReceivedSignals, IgnoreErrors);
auto proxy = Bind(&impl);
Options options2;
TestTarget target;
EXPECT_EQ(proxy->Connect(IgnoreSentSignals(), IgnoreTarget(), &options2), ZX_OK);
EXPECT_EQ(options2.runs(), runs);
EXPECT_EQ(options2.run_limit(), run_limit);
}
TEST_F(ProcessProxyTest, AddFeedback) {
ProcessProxyImpl impl(dispatcher(), pool());
FakeModule fake;
fake[0] = 1;
fake[1] = 4;
fake[2] = 8;
Module module(fake.counters(), fake.pcs(), fake.num_pcs());
Feedback feedback;
feedback.set_id(module.id());
feedback.set_inline_8bit_counters(module.Share());
auto proxy = Bind(&impl);
proxy->AddFeedback(std::move(feedback));
auto* module_impl = pool()->Get(module.id(), fake.num_pcs());
EXPECT_EQ(module_impl->Measure(), 3U);
}
TEST_F(ProcessProxyTest, SignalPeer) {
ProcessProxyImpl impl(dispatcher(), pool());
impl.Configure(ProcessProxyTest::DefaultOptions());
impl.SetHandlers(IgnoreReceivedSignals, IgnoreErrors);
zx_signals_t observed;
sync_completion_t sync;
SignalCoordinator coordinator;
auto eventpair = coordinator.Create([&](zx_signals_t signals) {
observed = signals;
sync_completion_signal(&sync);
return true;
});
auto proxy = Bind(&impl);
EXPECT_EQ(proxy->Connect(std::move(eventpair), IgnoreTarget(), IgnoreOptions()), ZX_OK);
impl.Start(/* detect_leaks */ false);
sync_completion_wait(&sync, ZX_TIME_INFINITE);
sync_completion_reset(&sync);
EXPECT_EQ(observed, kStart);
impl.Start(/* detect_leaks */ true);
sync_completion_wait(&sync, ZX_TIME_INFINITE);
sync_completion_reset(&sync);
EXPECT_EQ(observed, kStartLeakCheck);
impl.Finish();
sync_completion_wait(&sync, ZX_TIME_INFINITE);
sync_completion_reset(&sync);
EXPECT_EQ(observed, kFinish);
}
TEST_F(ProcessProxyTest, AwaitSignals) {
ProcessProxyImpl impl(dispatcher(), pool());
impl.Configure(ProcessProxyTest::DefaultOptions());
sync_completion_t sync;
ProcessProxyImpl* failed_impl = nullptr;
impl.SetHandlers([&]() { sync_completion_signal(&sync); },
[&](ProcessProxyImpl* failed) {
failed_impl = failed;
sync_completion_signal(&sync);
});
auto proxy = Bind(&impl);
SignalCoordinator coordinator;
auto eventpair = coordinator.Create([](zx_signals_t signals) { return true; });
EXPECT_EQ(proxy->Connect(std::move(eventpair), IgnoreTarget(), IgnoreOptions()), ZX_OK);
sync_completion_reset(&sync);
coordinator.SignalPeer(kStart);
sync_completion_wait(&sync, ZX_TIME_INFINITE);
sync_completion_reset(&sync);
coordinator.SignalPeer(kFinish);
sync_completion_wait(&sync, ZX_TIME_INFINITE);
EXPECT_FALSE(impl.leak_suspected());
sync_completion_reset(&sync);
coordinator.SignalPeer(kFinishWithLeaks);
sync_completion_wait(&sync, ZX_TIME_INFINITE);
EXPECT_TRUE(impl.leak_suspected());
sync_completion_reset(&sync);
coordinator.Reset();
sync_completion_wait(&sync, ZX_TIME_INFINITE);
EXPECT_EQ(failed_impl, &impl);
}
TEST_F(ProcessProxyTest, GetStats) {
ProcessProxyImpl impl(dispatcher(), pool());
impl.Configure(ProcessProxyTest::DefaultOptions());
impl.SetHandlers(IgnoreReceivedSignals, IgnoreErrors);
auto proxy = Bind(&impl);
TestTarget target;
zx::process spawned = target.Launch();
zx_info_handle_basic_t basic_info;
zx_info_task_stats_t task_stats;
zx_info_task_runtime_t task_runtime;
EXPECT_EQ(
spawned.get_info(ZX_INFO_HANDLE_BASIC, &basic_info, sizeof(basic_info), nullptr, nullptr),
ZX_OK);
EXPECT_EQ(spawned.get_info(ZX_INFO_TASK_STATS, &task_stats, sizeof(task_stats), nullptr, nullptr),
ZX_OK);
EXPECT_EQ(
spawned.get_info(ZX_INFO_TASK_RUNTIME, &task_runtime, sizeof(task_runtime), nullptr, nullptr),
ZX_OK);
EXPECT_EQ(proxy->Connect(IgnoreSentSignals(), std::move(spawned), IgnoreOptions()), ZX_OK);
ProcessStats stats;
impl.GetStats(&stats);
EXPECT_EQ(stats.koid, basic_info.koid);
EXPECT_GE(stats.mem_mapped_bytes, task_stats.mem_mapped_bytes);
EXPECT_GE(stats.mem_private_bytes, task_stats.mem_private_bytes);
EXPECT_EQ(stats.mem_shared_bytes, task_stats.mem_shared_bytes);
EXPECT_EQ(stats.mem_scaled_shared_bytes, task_stats.mem_scaled_shared_bytes);
EXPECT_GE(stats.cpu_time, task_runtime.cpu_time);
EXPECT_GE(stats.page_fault_time, task_runtime.page_fault_time);
EXPECT_GE(stats.lock_contention_time, task_runtime.lock_contention_time);
}
TEST_F(ProcessProxyTest, DefaultBadMalloc) {
ProcessProxyImpl impl(dispatcher(), pool());
impl.Configure(ProcessProxyTest::DefaultOptions());
impl.SetHandlers(IgnoreReceivedSignals, IgnoreErrors);
auto proxy = Bind(&impl);
TestTarget target;
EXPECT_EQ(proxy->Connect(IgnoreSentSignals(), target.Launch(), IgnoreOptions()), ZX_OK);
target.Exit(kDefaultMallocExitcode);
EXPECT_EQ(impl.Join(), Result::BAD_MALLOC);
}
TEST_F(ProcessProxyTest, CustomBadMalloc) {
ProcessProxyImpl impl(dispatcher(), pool());
int32_t exitcode = 1234;
auto options = ProcessProxyTest::DefaultOptions();
options->set_malloc_exitcode(exitcode);
impl.Configure(options);
impl.SetHandlers(IgnoreReceivedSignals, IgnoreErrors);
auto proxy = Bind(&impl);
TestTarget target;
EXPECT_EQ(proxy->Connect(IgnoreSentSignals(), target.Launch(), IgnoreOptions()), ZX_OK);
target.Exit(exitcode);
EXPECT_EQ(impl.Join(), Result::BAD_MALLOC);
}
TEST_F(ProcessProxyTest, DefaultDeath) {
ProcessProxyImpl impl(dispatcher(), pool());
impl.Configure(ProcessProxyTest::DefaultOptions());
impl.SetHandlers(IgnoreReceivedSignals, IgnoreErrors);
auto proxy = Bind(&impl);
TestTarget target;
EXPECT_EQ(proxy->Connect(IgnoreSentSignals(), target.Launch(), IgnoreOptions()), ZX_OK);
target.Exit(kDefaultDeathExitcode);
EXPECT_EQ(impl.Join(), Result::DEATH);
}
TEST_F(ProcessProxyTest, CustomDeath) {
ProcessProxyImpl impl(dispatcher(), pool());
int32_t exitcode = 4321;
auto options = ProcessProxyTest::DefaultOptions();
options->set_death_exitcode(exitcode);
impl.Configure(options);
impl.SetHandlers(IgnoreReceivedSignals, IgnoreErrors);
auto proxy = Bind(&impl);
TestTarget target;
EXPECT_EQ(proxy->Connect(IgnoreSentSignals(), target.Launch(), IgnoreOptions()), ZX_OK);
target.Exit(exitcode);
EXPECT_EQ(impl.Join(), Result::DEATH);
}
TEST_F(ProcessProxyTest, Exit) {
ProcessProxyImpl impl(dispatcher(), pool());
impl.Configure(ProcessProxyTest::DefaultOptions());
impl.SetHandlers(IgnoreReceivedSignals, IgnoreErrors);
auto proxy = Bind(&impl);
TestTarget target;
EXPECT_EQ(proxy->Connect(IgnoreSentSignals(), target.Launch(), IgnoreOptions()), ZX_OK);
target.Exit(1);
EXPECT_EQ(impl.Join(), Result::EXIT);
}
TEST_F(ProcessProxyTest, DefaultLeak) {
ProcessProxyImpl impl(dispatcher(), pool());
impl.Configure(ProcessProxyTest::DefaultOptions());
impl.SetHandlers(IgnoreReceivedSignals, IgnoreErrors);
auto proxy = Bind(&impl);
TestTarget target;
EXPECT_EQ(proxy->Connect(IgnoreSentSignals(), target.Launch(), IgnoreOptions()), ZX_OK);
target.Exit(kDefaultLeakExitcode);
EXPECT_EQ(impl.Join(), Result::LEAK);
}
TEST_F(ProcessProxyTest, CustomLeak) {
ProcessProxyImpl impl(dispatcher(), pool());
int32_t exitcode = 5678309;
auto options = ProcessProxyTest::DefaultOptions();
options->set_leak_exitcode(exitcode);
impl.Configure(options);
impl.SetHandlers(IgnoreReceivedSignals, IgnoreErrors);
auto proxy = Bind(&impl);
TestTarget target;
EXPECT_EQ(proxy->Connect(IgnoreSentSignals(), target.Launch(), IgnoreOptions()), ZX_OK);
target.Exit(exitcode);
EXPECT_EQ(impl.Join(), Result::LEAK);
}
TEST_F(ProcessProxyTest, DefaultOom) {
ProcessProxyImpl impl(dispatcher(), pool());
impl.Configure(ProcessProxyTest::DefaultOptions());
impl.SetHandlers(IgnoreReceivedSignals, IgnoreErrors);
auto proxy = Bind(&impl);
TestTarget target;
EXPECT_EQ(proxy->Connect(IgnoreSentSignals(), target.Launch(), IgnoreOptions()), ZX_OK);
target.Exit(kDefaultOomExitcode);
EXPECT_EQ(impl.Join(), Result::OOM);
}
TEST_F(ProcessProxyTest, CustomOom) {
ProcessProxyImpl impl(dispatcher(), pool());
int32_t exitcode = 24601;
auto options = ProcessProxyTest::DefaultOptions();
options->set_oom_exitcode(exitcode);
impl.Configure(options);
impl.SetHandlers(IgnoreReceivedSignals, IgnoreErrors);
auto proxy = Bind(&impl);
TestTarget target;
EXPECT_EQ(proxy->Connect(IgnoreSentSignals(), target.Launch(), IgnoreOptions()), ZX_OK);
target.Exit(exitcode);
EXPECT_EQ(impl.Join(), Result::OOM);
}
TEST_F(ProcessProxyTest, Timeout) {
ProcessProxyImpl impl(dispatcher(), pool());
impl.Configure(ProcessProxyTest::DefaultOptions());
impl.SetHandlers(IgnoreReceivedSignals, IgnoreErrors);
auto proxy = Bind(&impl);
TestTarget target;
EXPECT_EQ(proxy->Connect(IgnoreSentSignals(), target.Launch(), IgnoreOptions()), ZX_OK);
constexpr size_t kBufSize = 1U << 20;
auto buf = std::make_unique<char[]>(kBufSize);
// On timeout, the runner invokes |ProcessProxyImpl::Dump|.
auto len = impl.Dump(buf.get(), kBufSize);
EXPECT_GT(len, 0U);
EXPECT_LT(len, kBufSize);
impl.Kill();
}
} // namespace
} // namespace fuzzing