blob: 6b7541b2813ee0e908d7cbaeae6e4560d26470bc [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 "remote.h"
#include <lib/gtest/test_loop_fixture.h>
#include <lib/sys/cpp/testing/component_context_provider.h>
#include <lib/syslog/cpp/macros.h>
#include <stddef.h>
#include <string.h>
#include <thread>
#include "test/fake-proxy.h"
namespace fuzzing {
using ::fuchsia::fuzzer::Proxy;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Test fixture
class SanitizerCovProxyTest : public gtest::TestLoopFixture {
public:
void SetUp() override {
TestLoopFixture::SetUp();
context_ = provider_.TakeContext();
context_->outgoing()->AddPublicService(coverage_.GetHandler());
ProxyPtr coverage;
provider_.ConnectToPublicService(coverage.NewRequest());
auto proxy = SanitizerCovProxy::GetInstance(false /* autoconnect */);
ASSERT_EQ(proxy->SetProxy(std::move(coverage)), ZX_OK);
coverage_.Configure();
RunLoopUntilIdle();
}
protected:
FakeProxy coverage_;
private:
sys::testing::ComponentContextProvider provider_;
std::unique_ptr<sys::ComponentContext> context_;
};
} // namespace fuzzing
////////////////////////////////////////////////////////////////////////////////////////////////////
// Unit tests
namespace fuzzing {
TEST_F(SanitizerCovProxyTest, AddInline8BitCounters) {
const size_t kBufferSize1 = 0x1000;
uint8_t buffer1[kBufferSize1];
// Invalid arguments are ignored
__sanitizer_cov_8bit_counters_init(nullptr, buffer1 + kBufferSize1);
__sanitizer_cov_8bit_counters_init(buffer1, nullptr);
__sanitizer_cov_8bit_counters_init(buffer1 + kBufferSize1, buffer1);
SharedMemory shmem;
EXPECT_FALSE(coverage_.MapPending(&shmem));
// Valid
// This needs a separate thread so the test can still drive the loop.
sync_completion_t sync;
std::thread t1([&buffer1, &sync]() {
__sanitizer_cov_8bit_counters_init(buffer1, buffer1 + kBufferSize1);
sync_completion_signal(&sync);
});
// Keep prodding the test loop until the thread above is done making FIDL requests.
zx_status_t status;
while ((status = sync_completion_wait(&sync, ZX_MSEC(10))) == ZX_ERR_TIMED_OUT) {
RunLoopUntilIdle();
}
ASSERT_EQ(status, ZX_OK);
t1.join();
EXPECT_TRUE(coverage_.MapPending(&shmem));
EXPECT_EQ(shmem.len(), kBufferSize1 * sizeof(uint8_t));
// Should be able to do another, different region
// This needs a separate thread so the test can still drive the loop.
const size_t kBufferSize2 = 0x2000;
uint8_t buffer2[kBufferSize2];
sync_completion_reset(&sync);
std::thread t2([&buffer2, &sync]() {
__sanitizer_cov_8bit_counters_init(buffer2, buffer2 + kBufferSize2);
sync_completion_signal(&sync);
});
// Keep prodding the test loop until the thread above is done making FIDL requests.
while ((status = sync_completion_wait(&sync, ZX_MSEC(10))) == ZX_ERR_TIMED_OUT) {
RunLoopUntilIdle();
}
ASSERT_EQ(status, ZX_OK);
t2.join();
EXPECT_TRUE(coverage_.MapPending(&shmem));
EXPECT_EQ(shmem.len(), kBufferSize2 * sizeof(uint8_t));
}
TEST_F(SanitizerCovProxyTest, AddPcTable) {
const size_t kBufferSize1 = 0x1000;
uintptr_t buffer1[kBufferSize1];
// Invalid arguments are ignored
__sanitizer_cov_pcs_init(nullptr, buffer1 + kBufferSize1);
__sanitizer_cov_pcs_init(buffer1, nullptr);
__sanitizer_cov_pcs_init(buffer1 + kBufferSize1, buffer1);
SharedMemory shmem;
EXPECT_FALSE(coverage_.MapPending(&shmem));
// Valid
// This needs a separate thread so the test can still drive the loop.
sync_completion_t sync;
std::thread t1([&buffer1, &sync]() {
__sanitizer_cov_pcs_init(buffer1, buffer1 + kBufferSize1);
sync_completion_signal(&sync);
});
// Keep prodding the test loop until the thread above is done making FIDL requests.
zx_status_t status;
while ((status = sync_completion_wait(&sync, ZX_MSEC(10))) == ZX_ERR_TIMED_OUT) {
RunLoopUntilIdle();
}
ASSERT_EQ(status, ZX_OK);
t1.join();
EXPECT_TRUE(coverage_.MapPending(&shmem));
EXPECT_EQ(shmem.len(), kBufferSize1 * sizeof(uintptr_t));
// Should be able to do another, different region
// This needs a separate thread so the test can still drive the loop.
const size_t kBufferSize2 = 0x2000;
uintptr_t buffer2[kBufferSize2];
sync_completion_reset(&sync);
std::thread t2([&buffer2, &sync]() {
__sanitizer_cov_pcs_init(buffer2, buffer2 + kBufferSize2);
sync_completion_signal(&sync);
});
// Keep prodding the test loop until the thread above is done making FIDL requests.
while ((status = sync_completion_wait(&sync, ZX_MSEC(10))) == ZX_ERR_TIMED_OUT) {
RunLoopUntilIdle();
}
ASSERT_EQ(status, ZX_OK);
t2.join();
EXPECT_TRUE(coverage_.MapPending(&shmem));
EXPECT_EQ(shmem.len(), kBufferSize2 * sizeof(uintptr_t));
}
TEST_F(SanitizerCovProxyTest, AddTrace) {
uintptr_t pc = 0x1000;
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_pc_indir(~pc);
Instruction *trace = coverage_.traces();
EXPECT_EQ(trace->type, Instruction::kPcIndir);
EXPECT_EQ(trace->pc, pc);
EXPECT_EQ(trace->args[0], ~pc);
EXPECT_EQ(trace->args[1], 0u);
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_cmp8((uint64_t)1, (uint64_t)-1);
trace++;
EXPECT_EQ(trace->type, Instruction::kCmp8);
EXPECT_EQ(trace->pc, pc);
EXPECT_EQ(trace->args[0], 1u);
EXPECT_EQ(trace->args[1], 0xffffffffffffffff);
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_const_cmp8((uint64_t)2, (uint64_t)-2);
trace++;
EXPECT_EQ(trace->type, Instruction::kConstCmp8);
EXPECT_EQ(trace->pc, pc);
EXPECT_EQ(trace->args[0], 2u);
EXPECT_EQ(trace->args[1], 0xfffffffffffffffe);
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_cmp4((uint32_t)3, (uint32_t)-3);
trace++;
EXPECT_EQ(trace->type, Instruction::kCmp4);
EXPECT_EQ(trace->pc, pc);
EXPECT_EQ(trace->args[0], 3u);
EXPECT_EQ(trace->args[1], 0xfffffffd);
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_const_cmp4((uint32_t)4, (uint32_t)-4);
trace++;
EXPECT_EQ(trace->type, Instruction::kConstCmp4);
EXPECT_EQ(trace->pc, pc);
EXPECT_EQ(trace->args[0], 4u);
EXPECT_EQ(trace->args[1], 0xfffffffc);
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_cmp2((uint16_t)5, (uint16_t)-5);
trace++;
EXPECT_EQ(trace->type, Instruction::kCmp2);
EXPECT_EQ(trace->pc, pc);
EXPECT_EQ(trace->args[0], 5u);
EXPECT_EQ(trace->args[1], 0xfffbu);
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_const_cmp2((uint16_t)6, (uint16_t)-6);
trace++;
EXPECT_EQ(trace->type, Instruction::kConstCmp2);
EXPECT_EQ(trace->pc, pc);
EXPECT_EQ(trace->args[0], 6u);
EXPECT_EQ(trace->args[1], 0xfffau);
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_cmp1((uint8_t)7, (uint8_t)-7);
trace++;
EXPECT_EQ(trace->type, Instruction::kCmp1);
EXPECT_EQ(trace->pc, pc);
EXPECT_EQ(trace->args[0], 7u);
EXPECT_EQ(trace->args[1], 0xf9u);
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_const_cmp1((uint8_t)8, (uint8_t)-8);
trace++;
EXPECT_EQ(trace->type, Instruction::kConstCmp1);
EXPECT_EQ(trace->pc, pc);
EXPECT_EQ(trace->args[0], 8u);
EXPECT_EQ(trace->args[1], 0xf8u);
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_div4((uint32_t)-9);
trace++;
EXPECT_EQ(trace->type, Instruction::kDiv4);
EXPECT_EQ(trace->pc, pc);
EXPECT_EQ(trace->args[0], 0xfffffff7);
EXPECT_EQ(trace->args[1], 0u);
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_div8((uint64_t)-10);
trace++;
EXPECT_EQ(trace->type, Instruction::kDiv8);
EXPECT_EQ(trace->pc, pc);
EXPECT_EQ(trace->args[0], 0xfffffffffffffff6);
EXPECT_EQ(trace->args[1], 0u);
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_gep((uintptr_t)11);
trace++;
EXPECT_EQ(trace->type, Instruction::kGep);
EXPECT_EQ(trace->pc, pc);
EXPECT_EQ(trace->args[0], 11u);
EXPECT_EQ(trace->args[1], 0u);
}
TEST_F(SanitizerCovProxyTest, AddTraceSwitch) {
uintptr_t pc = 0x2000;
std::vector<uint64_t> cases;
Instruction *trace = coverage_.traces();
// Invalid number of cases. This shouldn't add a trace.
cases = std::vector<uint64_t>{0, 64};
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_switch(257, &cases[0]);
EXPECT_EQ(trace->type, Instruction::kInvalid);
// Invalid number of bits. This shouldn't add a trace.
cases = std::vector<uint64_t>{2, 63, 0, 258};
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_switch(257, &cases[0]);
EXPECT_EQ(trace->type, Instruction::kInvalid);
// Small values (<256) don't get recorded
cases = std::vector<uint64_t>{2, 64, 0, 258};
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_switch(255, &cases[0]);
EXPECT_EQ(trace->type, Instruction::kInvalid);
// Small cases (all < 256) don't get recorded
cases = std::vector<uint64_t>{2, 64, 0, 255};
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_switch(257, &cases[0]);
EXPECT_EQ(trace->type, Instruction::kInvalid);
// Should be able to trace switch with a single case. Try less than, equal to, and greater then.
cases = std::vector<uint64_t>{1, 32, 258};
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_switch(257, &cases[0]);
EXPECT_EQ(trace->type, Instruction::kCmp4);
EXPECT_EQ(trace->args[0], 257u);
EXPECT_EQ(trace->args[1], 0u);
trace++;
EXPECT_EQ(trace->type, Instruction::kCmp4);
EXPECT_EQ(trace->args[0], 257u);
EXPECT_EQ(trace->args[1], 258u);
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_switch(258, &cases[0]);
trace++;
EXPECT_EQ(trace->type, Instruction::kCmp4);
EXPECT_EQ(trace->args[0], 258u);
EXPECT_EQ(trace->args[1], 0u);
trace++;
EXPECT_EQ(trace->type, Instruction::kCmp4);
EXPECT_EQ(trace->args[0], 258u);
EXPECT_EQ(trace->args[1], 0xffffffff);
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_switch(259, &cases[0]);
trace++;
EXPECT_EQ(trace->type, Instruction::kCmp4);
EXPECT_EQ(trace->args[0], 259u);
EXPECT_EQ(trace->args[1], 258u);
trace++;
EXPECT_EQ(trace->type, Instruction::kCmp4);
EXPECT_EQ(trace->args[0], 259u);
EXPECT_EQ(trace->args[1], 0xffffffff);
// Should able to handle multiple cases. Make sure correct adjacent cases are selected.
cases = std::vector<uint64_t>{4, 16, 0x1011, 0x1012, 0x1013, 0x1014};
LLVMFuzzerSetRemoteCallerPC(++pc);
__sanitizer_cov_trace_switch(0x1012, &cases[0]);
trace++;
EXPECT_EQ(trace->type, Instruction::kCmp2);
EXPECT_EQ(trace->args[0], 0x1012u);
EXPECT_EQ(trace->args[1], 0x1011u);
trace++;
EXPECT_EQ(trace->type, Instruction::kCmp2);
EXPECT_EQ(trace->args[0], 0x1012u);
EXPECT_EQ(trace->args[1], 0x1013u);
}
TEST_F(SanitizerCovProxyTest, AddTrace_MultiThreaded) {
Instruction *trace = coverage_.traces();
std::thread t1([]() {
for (size_t i = 0; i < kInstructionBufferLen / 2; i++) {
__sanitizer_cov_trace_pc_indir(0x1000);
zx::nanosleep(zx::deadline_after(zx::nsec(1)));
}
});
std::thread t2([]() {
for (size_t i = 0; i < kInstructionBufferLen / 4; i++) {
__sanitizer_cov_trace_gep(0x7fff);
zx::nanosleep(zx::deadline_after(zx::nsec(1)));
}
});
// Neither thread should block.
t1.join();
t2.join();
// The order of traces should be unpredictable, but the totla number of each should be consistent.
size_t num_pc_indirs = 0;
size_t num_geps = 0;
while (true) {
switch (trace->type) {
case Instruction::kPcIndir:
EXPECT_EQ(trace->args[0], 0x1000u);
num_pc_indirs++;
break;
case Instruction::kGep:
EXPECT_EQ(trace->args[0], 0x7fffu);
num_geps++;
break;
case Instruction::kInvalid:
EXPECT_EQ(num_pc_indirs, kInstructionBufferLen / 2);
EXPECT_EQ(num_geps, kInstructionBufferLen / 4);
return;
default:
FX_NOTREACHED();
}
trace++;
}
}
TEST_F(SanitizerCovProxyTest, AddTrace_ExceedThreshold) {
// Trace more than |kInstructionBufferLen| instructions without resolution. This should force an
// update request to be sent.
for (size_t i = 0; i < kInstructionBufferLen + 1; i++) {
__sanitizer_cov_trace_pc_indir(0x1000);
}
// ...but it should result in an update request.
coverage_.Resolve();
EXPECT_EQ(coverage_.Count(Instruction::kPcIndir), kInstructionBufferLen);
}
TEST_F(SanitizerCovProxyTest, AddTrace_ExhaustUnresolved) {
// Trace more than |kMaxInstructions| instructions without resolution. This should cause some
// calls to block until the Proxy service can free up some of the trace array.
sync_completion_t sync;
std::thread t1([&sync]() {
for (size_t i = 0; i < kMaxInstructions + 1; i++) {
__sanitizer_cov_trace_pc_indir(0x1000);
__sanitizer_cov_trace_gep(0x7fff);
}
sync_completion_signal(&sync);
});
// Keep prodding the test loop until the thread above is done making FIDL requests.
zx_status_t status;
while ((status = sync_completion_wait(&sync, ZX_MSEC(10))) == ZX_ERR_TIMED_OUT) {
coverage_.Resolve();
}
ASSERT_EQ(status, ZX_OK);
t1.join();
coverage_.Resolve();
EXPECT_EQ(coverage_.Count(Instruction::kPcIndir), kMaxInstructions);
EXPECT_EQ(coverage_.Count(Instruction::kGep), kMaxInstructions);
}
TEST_F(SanitizerCovProxyTest, OnIterationComplete) {
// Initialize the shared memory regions
constexpr size_t N = 8;
uint8_t inline_8bit_counters[N];
uintptr_t pcs[N];
// This needs a separate thread so the test can still drive the loop.
sync_completion_t sync;
std::thread t1([&inline_8bit_counters, &pcs, &sync]() {
__sanitizer_cov_8bit_counters_init(inline_8bit_counters, inline_8bit_counters + N);
__sanitizer_cov_pcs_init(pcs, pcs + N);
sync_completion_signal(&sync);
});
// Keep prodding the test loop until the thread above is done making FIDL requests.
zx_status_t status;
while ((status = sync_completion_wait(&sync, ZX_MSEC(10))) == ZX_ERR_TIMED_OUT) {
RunLoopUntilIdle();
}
ASSERT_EQ(status, ZX_OK);
t1.join();
// Add some data as if a fuzzing iteration were performed.
for (uint8_t i = 0; i < N; ++i) {
inline_8bit_counters[i] = i;
pcs[i] = 0x1000 + i;
__sanitizer_cov_trace_pc_indir(0x2000 + i);
}
coverage_.SendIterationComplete();
// Shared memory regions should have been updated.
SharedMemory i8bc_shmem;
EXPECT_TRUE(coverage_.MapPending(&i8bc_shmem));
EXPECT_EQ(i8bc_shmem.len(), sizeof(inline_8bit_counters));
uint8_t *i8bc_actual = reinterpret_cast<uint8_t *>(i8bc_shmem.addr());
for (size_t i = 0; i < N; ++i) {
EXPECT_EQ(inline_8bit_counters[i], i8bc_actual[i]);
}
SharedMemory pcs_shmem;
EXPECT_TRUE(coverage_.MapPending(&pcs_shmem));
EXPECT_EQ(pcs_shmem.len(), sizeof(pcs));
uintptr_t *pcs_actual = reinterpret_cast<uintptr_t *>(pcs_shmem.addr());
for (size_t i = 0; i < N; ++i) {
EXPECT_EQ(pcs[i], pcs_actual[i]);
}
// The proxy should have indicated completion.
EXPECT_TRUE(coverage_.HasCompleted());
}
} // namespace fuzzing