Index: compiler-rt/lib/fuzzer/build.sh =================================================================== --- compiler-rt/lib/fuzzer/build.sh +++ compiler-rt/lib/fuzzer/build.sh @@ -14,8 +14,10 @@ CXX="${CXX:-clang}" CFLAGS="${CFLAGS:-${DEFAULT_CFLAGS}}" -for f in $LIBFUZZER_SRC_DIR/*.cpp; do - $CXX $CFLAGS $f -c & +for d in . ipc; do + for f in $LIBFUZZER_SRC_DIR/$d/*.cpp; do + $CXX $CFLAGS $f -c & + done done wait @@ -71,4 +73,10 @@ FuzzerUtilWindows.o \ FuzzerRemote.o -rm -f Fuzzer*.o +rm -f libFuzzerIPCProxy.a +ar r libFuzzerIPCProxy.a \ + IPCLinux.o \ + IPCProxyLinux.o \ + IPCQuickExit.o \ + +rm -f Fuzzer*.o IPC*.o Index: compiler-rt/lib/fuzzer/ipc/CMakeLists.txt =================================================================== --- compiler-rt/lib/fuzzer/ipc/CMakeLists.txt +++ compiler-rt/lib/fuzzer/ipc/CMakeLists.txt @@ -7,3 +7,30 @@ add_libfuzzer_objects(RTfuzzer_ipc SOURCES IPCQuickExit.cpp HEADERS IPCQuickExit.h) + +add_libfuzzer_objects(RTfuzzer_ipc_proxy + SOURCES IPCProxyLinux.cpp + HEADERS IPCProxyLinux.h IPCRemoteLinux.h) + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + function(add_partially_linked_library name arch) + cmake_parse_arguments(LOCAL "" "" "OBJECT_LIBS" "" ${ARGN}) + foreach(object_lib ${LOCAL_OBJECT_LIBS}) + list(APPEND LOCAL_OBJECTS $) + endforeach() + add_library(${name}-${arch} STATIC ${LOCAL_OBJECTS}) + set_target_properties(${name}-${arch} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + partially_link_libcxx(${name} ${LIBCXX_${arch}_PREFIX} ${arch}) + endfunction() + + foreach(arch ${FUZZER_SUPPORTED_ARCH}) + add_partially_linked_library(RTFuzzerIPCProxy ${arch} + OBJECT_LIBS RTfuzzer_base + RTfuzzer + RTfuzzer_main + RTfuzzer_ipc_base + RTfuzzer_ipc + RTfuzzer_ipc_proxy) + endforeach() +endif() Index: compiler-rt/lib/fuzzer/ipc/IPCProxyLinux.h =================================================================== --- /dev/null +++ compiler-rt/lib/fuzzer/ipc/IPCProxyLinux.h @@ -0,0 +1,101 @@ +//===- IPCProxyLinux.h ------------------------------------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Defines the proxy-related constants shared between both sides of the IPC +// layer, and the server interface used by FuzzerProxy. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_IPC_PROXY_LINUX_H +#define LLVM_FUZZER_IPC_PROXY_LINUX_H + +#include "FuzzerPlatform.h" +#if LIBFUZZER_LINUX + +#include "FuzzerLock.h" +#include "IPCLinux.h" +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { +namespace ipc { + +// The server address should be easily configurable, yet easy to discover by or +// pass to the client. An environment variable with a fixed name works. +constexpr const char *kProxyAddressEnvVar = "FUZZER_IPC_ADDRESS"; + +// Message types for messages from the proxy to a remote. +enum ProxyOrdinal : uint8_t { + kProxyAck = Connection::kAcknowledgement, + kOptions, + kStartExecution, + kFinishExecution, + kPrintPC, + kDescribePC, + kPrintStackTrace, + kPrintMemoryProfile, + kDetectLeaksAtExit, +}; + +// Maximum length for a PC description. +constexpr size_t kMaxDescLen = 256; + +// This class provides the IPC implementation of the FuzzerRemoteInterface for +// FuzzerProxy. +class ProxyConnection; +class Proxy final : public ConnectionServer { +public: + static Proxy *GetInstance(); + static std::shared_ptr GetProxyConnection(unsigned long PID); + + ~Proxy() override; + + void Start(); + void Stop() override; + + // Coverage received by remote processes is added to the static TracePC, and + // must remain valid until it is replaced or the program exits, at which point + // it should be unmapped. Since it can outlive an individual connection, this + // memory region needs to be tracked by the Proxy. Calling AddMapped twice + // with the same Idx will cause the first mapping to be replaced. + void AddMapped(uintptr_t Idx, void *Addr, size_t Len); + + // When the FuzzerRemote asks the FuzzerProxy to invoke one of the callbacks + // defined by the FuzzerMonitor interface, it needs to be executed on a + // separate thread to allow the connection to continue receiving messages, + // e.g. acknowledgements to PrintStackTrace requests. Only one such error + // context is needed, so the thread is owned by the Proxy rather than + // individual connections. This method tells the thread to invoke a callback. + void InvokeCallback(uint8_t CB, unsigned long RemotePID = 0, size_t Size = 0); + +protected: + std::unique_ptr CreateConnection() override; + +private: + std::mutex Mutex; + std::unordered_map> + MappedByIdx GUARDED_BY(Mutex); + + // See InvokeCallback, above. These variables are used to communicate details + // of a fatal callback from a remote process to the per-proxy CallbackThread. + uint8_t Callback GUARDED_BY(Mutex); + unsigned long CallbackPID GUARDED_BY(Mutex); + size_t CallbackSize GUARDED_BY(Mutex); + + std::thread CallbackThread; + std::condition_variable CallbackCV; +}; + +} // namespace ipc +} // namespace fuzzer + +#endif // LIBFUZZER_LINUX +#endif // LLVM_FUZZER_IPC_PROXY_LINUX_H Index: compiler-rt/lib/fuzzer/ipc/IPCProxyLinux.cpp =================================================================== --- /dev/null +++ compiler-rt/lib/fuzzer/ipc/IPCProxyLinux.cpp @@ -0,0 +1,386 @@ +//===- IPCProxyLinux.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// IPC routines used by the FuzzerProxy on Linux. +//===----------------------------------------------------------------------===// + +#include "FuzzerPlatform.h" + +#if LIBFUZZER_LINUX + +#include "FuzzerExtFunctions.h" +#include "FuzzerMonitor.h" +#include "FuzzerProxy.h" +#include "FuzzerRemoteInterface.h" +#include "IPCLinux.h" +#include "IPCProxyLinux.h" +#include "IPCRemoteLinux.h" +#include +#include + +namespace fuzzer { +namespace ipc { + +// This class represents a single FuzzerProxy-side endpoint for a connection to +// a FuzzerRemote process. +class ProxyConnection final : public Connection { +public: + explicit ProxyConnection(ConnectionServer *S) + : Connection(S), Proxy(Proxy::GetInstance()) { + assert(Proxy == S); + } + + ~ProxyConnection() override { Reset(); } + + bool DoHandshake() override EXCLUDES(GetTxMutex()) { + Connection::DoHandshake(); + ProxiedOptions Options; + FuzzerProxyConnect(this->GetPid(), &Options, sizeof(Options)); + { + std::lock_guard TxLock(GetTxMutex()); + return SendLocked(kOptions) && SendLocked(&Options, sizeof(Options)); + } + } + + void StartExecution(uint32_t Options) EXCLUDES(GetTxMutex()) { + std::lock_guard TxLock(GetTxMutex()); + SendLocked(kStartExecution); + SendLocked(Options); + } + + void FinishExecution() EXCLUDES(GetTxMutex()) { + std::lock_guard TxLock(GetTxMutex()); + SendLocked(kFinishExecution); + } + + void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) + EXCLUDES(GetTxMutex()) { + { + std::lock_guard TxLock(GetTxMutex()); + SendLocked(kPrintPC); + SendStringLocked(SymbolizedFMT); + SendStringLocked(FallbackFMT); + SendLocked(PC); + } + WaitForAcknowledgement(); + } + + void DescribePC(const char *SymbolizedFMT, uintptr_t PC, char *Desc, + size_t DescLen) EXCLUDES(Mutex, GetTxMutex()) { + assert(Desc && DescLen); + { + std::lock_guard Lock(Mutex); + Desc[0] = '\0'; + PCDescription = Desc; + PCDescriptionLen = std::min(DescLen, kMaxDescLen); + } + { + std::lock_guard TxLock(GetTxMutex()); + SendLocked(kDescribePC); + SendStringLocked(SymbolizedFMT); + SendLocked(PC); + SendLocked(DescLen); + } + WaitForAcknowledgement(); + } + + void PrintStackTrace() EXCLUDES(GetTxMutex()) { + { + std::lock_guard TxLock(GetTxMutex()); + SendLocked(kPrintStackTrace); + } + WaitForAcknowledgement(); + } + + void PrintMemoryProfile() EXCLUDES(GetTxMutex()) { + { + std::lock_guard TxLock(GetTxMutex()); + SendLocked(kPrintMemoryProfile); + } + WaitForAcknowledgement(); + } + + void DetectLeaksAtExit() EXCLUDES(GetTxMutex()) { + { + std::lock_guard TxLock(GetTxMutex()); + SendLocked(kDetectLeaksAtExit); + } + WaitForAcknowledgement(); + } + +private: + bool DispatchOneLocked() override REQUIRES(GetRxMutex()) { + unsigned long PID = this->GetPid(); + RemoteOrdinal O; + if (!ReceiveLocked(&O)) + return false; + size_t Size = 0; + switch (O) { + case kRemoteAck: + HandleAcknowledgement(); + break; + case kAddCoverage: { + void *Addr1, *Addr2; + size_t Len1, Len2; + if (!ReceiveMemoryLocked(&Addr1, &Len1) || + !ReceiveMemoryLocked(&Addr2, &Len2)) + return false; + auto *Start = reinterpret_cast(Addr1); + auto *Stop = Start + (Len1 / sizeof(Start[0])); + auto *Begin = reinterpret_cast(Addr2); + auto *End = Begin + (Len2 / sizeof(Begin[0])); + uintptr_t Idx = FuzzerProxyAddCoverage(PID, Start, Stop, Begin, End); + // Counters are added to TracePC and must be kept mapped, even beyond + // this connection. + if (Idx == kInvalidIdx) + munmap(Addr1, Len1); + else + Proxy->AddMapped(Idx, Addr1, Len1); + // PCs have been copied and can be unmapped. + munmap(Addr2, Len2); + Acknowledge(); + break; + } + case kExecutionStarted: + FuzzerProxyExecutionStarted(PID); + break; + case kExecutionFinished: { + int HasMoreMallocsThanFrees; + if (!ReceiveLocked(&HasMoreMallocsThanFrees)) + return false; + FuzzerProxyExecutionFinished(PID, HasMoreMallocsThanFrees); + break; + } + case kCrashCallback: + case kExitCallback: + case kLeakCallback: + case kRssLimitCallback: + Proxy->InvokeCallback(O, PID, Size); + break; + case kDeathCallback: + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + break; + Proxy->InvokeCallback(O, PID, Size); + break; + case kMallocLimitCallback: + if (!ReceiveLocked(&Size)) + return false; + Proxy->InvokeCallback(O, PID, Size); + break; + case kDescribePCResponse: { + std::lock_guard Lock(Mutex); + if (!ReceiveStringLocked(PCDescription, PCDescriptionLen)) + return false; + } + HandleAcknowledgement(); + break; + } + return true; + } + + void OnClose() override { + unsigned long PID = this->GetPid(); + FuzzerProxyDisconnect(PID); + Connection::OnClose(); + { + std::lock_guard Lock(Mutex); + AwaitingResponse = false; + } + DescriptionCV.notify_one(); + } + + fuzzer::ipc::Proxy *const Proxy; + + std::mutex Mutex; + char *PCDescription GUARDED_BY(Mutex) = nullptr; + size_t PCDescriptionLen GUARDED_BY(Mutex) = 0; + bool AwaitingResponse GUARDED_BY(Mutex) = false; + std::condition_variable DescriptionCV; +}; + +// fuzzer::ipc::Proxy methods + +Proxy *Proxy::GetInstance() { + static Proxy Singleton; + return &Singleton; +} + +Proxy::~Proxy() { + Stop(); + std::lock_guard Lock(Mutex); + for (auto &I : MappedByIdx) + if (munmap(I.second.first, I.second.second) == -1) + Printf("ERROR: munmap: %s\n", strerror(errno)); +} + +void Proxy::Start() { + { + std::lock_guard Lock(Mutex); + assert(Callback != kExecutionStarted); + Callback = kExecutionStarted; + } + CallbackThread = std::thread([this]() EXCLUDES(Mutex) { + uint8_t CB; + unsigned long RemotePID; + size_t Size; + { + UniqueLock Lock(Mutex); + while (Callback == kExecutionStarted) + CallbackCV.wait(Lock.get()); + CB = Callback; + RemotePID = CallbackPID; + Size = CallbackSize; + } + switch (CB) { + case kCrashCallback: + FuzzerCrashSignalCallback(RemotePID); + break; + case kDeathCallback: + FuzzerDeathCallback(); + break; + case kExitCallback: + FuzzerExitCallback(RemotePID); + break; + case kLeakCallback: + FuzzerLeakCallback(RemotePID); + break; + case kMallocLimitCallback: + FuzzerMallocLimitCallback(RemotePID, Size); + break; + case kRssLimitCallback: + FuzzerRssLimitCallback(RemotePID); + break; + case kExecutionFinished: + // Normal exit + break; + default: + Printf("ERROR: Unknown callback: %u.\n", CB); + break; + } + }); + ConnectionServer::Start(kProxyAddressEnvVar); +} + +void Proxy::Stop() { + ConnectionServer::Stop(); + { + std::lock_guard Lock(Mutex); + Callback = kExecutionFinished; + } + CallbackCV.notify_one(); + if (CallbackThread.joinable()) + CallbackThread.join(); +} + +std::unique_ptr Proxy::CreateConnection() { + return std::unique_ptr(new ProxyConnection(this)); +} + +std::shared_ptr Proxy::GetProxyConnection(unsigned long PID) { + auto C = GetInstance()->GetConnection(PID); + return std::static_pointer_cast(C); +} + +void Proxy::AddMapped(uintptr_t Idx, void *Addr, size_t Len) { + assert(Idx != kInvalidIdx); + assert(Addr != nullptr); + assert(Len != 0); + std::lock_guard Lock(Mutex); + auto I = MappedByIdx.find(Idx); + if (I != MappedByIdx.end()) + if (munmap(I->second.first, I->second.second) == -1) + Printf("ERROR: munmap: %s\n", strerror(errno)); + MappedByIdx[Idx] = {Addr, Len}; +} + +void Proxy::InvokeCallback(uint8_t CB, unsigned long RemotePID, size_t Size) { + { + std::lock_guard Lock(Mutex); + Callback = CB; + CallbackPID = RemotePID; + CallbackSize = Size; + } + CallbackCV.notify_one(); +} + +} // namespace ipc +} // namespace fuzzer + +// FuzzerRemoteInterface implementations. The IPC layer looks like FuzzerRemote +// to the FuzzerProxy. + +extern "C" { + +ATTRIBUTE_INTERFACE +void FuzzerAcceptRemotes() { + auto *Proxy = fuzzer::ipc::Proxy::GetInstance(); + Proxy->Start(); +} + +ATTRIBUTE_INTERFACE +void FuzzerShutdownRemotes() { + auto *Proxy = fuzzer::ipc::Proxy::GetInstance(); + Proxy->Stop(); +} + +ATTRIBUTE_INTERFACE +void FuzzerRemoteStartExecution(unsigned long PID, uint32_t Options) { + auto C = fuzzer::ipc::Proxy::GetProxyConnection(PID); + if (C) + C->StartExecution(Options); +} + +ATTRIBUTE_INTERFACE +void FuzzerRemoteFinishExecution(unsigned long PID) { + auto C = fuzzer::ipc::Proxy::GetProxyConnection(PID); + if (C) + C->FinishExecution(); +} + +ATTRIBUTE_INTERFACE +void FuzzerRemotePrintPC(unsigned long PID, const char *SymbolizedFMT, + const char *FallbackFMT, uintptr_t PC) { + auto C = fuzzer::ipc::Proxy::GetProxyConnection(PID); + if (C) + C->PrintPC(SymbolizedFMT, FallbackFMT, PC); +} + +ATTRIBUTE_INTERFACE +void FuzzerRemoteDescribePC(unsigned long PID, const char *SymbolizedFMT, + uintptr_t PC, char *Desc, size_t DescLen) { + auto C = fuzzer::ipc::Proxy::GetProxyConnection(PID); + if (C) + C->DescribePC(SymbolizedFMT, PC, Desc, DescLen); +} + +ATTRIBUTE_INTERFACE +void FuzzerRemotePrintStackTrace(unsigned long PID) { + auto C = fuzzer::ipc::Proxy::GetProxyConnection(PID); + if (C) + C->PrintStackTrace(); +} + +ATTRIBUTE_INTERFACE +void FuzzerRemotePrintMemoryProfile(unsigned long PID) { + auto C = fuzzer::ipc::Proxy::GetProxyConnection(PID); + if (C) + C->PrintMemoryProfile(); +} + +ATTRIBUTE_INTERFACE +void FuzzerRemoteDetectLeaksAtExit(unsigned long PID) { + auto C = fuzzer::ipc::Proxy::GetProxyConnection(PID); + if (C) + C->DetectLeaksAtExit(); +} + +int my_hack; + +} // extern "C" + +#endif // LIBFUZZER_LINUX Index: compiler-rt/lib/fuzzer/ipc/IPCRemoteLinux.h =================================================================== --- /dev/null +++ compiler-rt/lib/fuzzer/ipc/IPCRemoteLinux.h @@ -0,0 +1,42 @@ +//===- IPCRemoteLinux.h -----------------------------------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Defines the remote-related constants shared between both sides of the IPC +// layer. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_IPC_REMOTE_LINUX_H +#define LLVM_FUZZER_IPC_REMOTE_LINUX_H + +#include "FuzzerPlatform.h" +#if LIBFUZZER_LINUX + +#include + +namespace fuzzer { +namespace ipc { + +// Message types for messages from a remote to the proxy. +enum RemoteOrdinal : uint8_t { + kRemoteAck = Connection::kAcknowledgement, + kAddCoverage, + kExecutionStarted, + kExecutionFinished, + kCrashCallback, + kDeathCallback, + kExitCallback, + kLeakCallback, + kMallocLimitCallback, + kRssLimitCallback, + kDescribePCResponse, +}; + +} // namespace ipc +} // namespace fuzzer + +#endif // LIBFUZZER_LINUX +#endif // LLVM_FUZZER_IPC_REMOTE_LINUX_H Index: compiler-rt/lib/fuzzer/tests/ipc/CMakeLists.txt =================================================================== --- compiler-rt/lib/fuzzer/tests/ipc/CMakeLists.txt +++ compiler-rt/lib/fuzzer/tests/ipc/CMakeLists.txt @@ -4,34 +4,25 @@ IPCTestUtilGeneric.cpp IPCTestUtilLinux.cpp) -set(LIBFUZZER_IPC_TEST_HEADERS - IPCTestUtil.h) - -add_compiler_rt_object_libraries(RTfuzzer_ipc_test_util - OS ${FUZZER_SUPPORTED_OS} - ARCHS ${FUZZER_SUPPORTED_ARCH} +add_libfuzzer_objects(RTfuzzer_ipc_test_util SOURCES ${LIBFUZZER_IPC_TEST_SOURCES} - ADDITIONAL_HEADERS ${LIBFUZZER_IPC_TEST_HEADERS} - CFLAGS ${LIBFUZZER_UNITTEST_CFLAGS} - DEPS ${LIBFUZZER_DEPS}) - -if(OS_NAME MATCHES "Linux" AND - COMPILER_RT_LIBCXX_PATH AND - COMPILER_RT_LIBCXXABI_PATH) - foreach(arch ${FUZZER_SUPPORTED_ARCH}) - target_compile_options(RTfuzzer_ipc_test_util.${arch} PRIVATE -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1) - add_dependencies(RTfuzzer_ipc_test_util.${arch} libcxx_fuzzer_${arch}-build) - endforeach() -endif() + HEADERS IPCTestUtil.h + CFLAGS ${LIBFUZZER_UNITTEST_CFLAGS}) if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST FUZZER_SUPPORTED_ARCH) # libFuzzer unit tests are only run on the host machine. set(arch ${COMPILER_RT_DEFAULT_TARGET_ARCH}) - generate_libfuzzer_test_runtime(RTFuzzerIPCTest ${arch} - OBJECT_LIBS RTfuzzer_base RTfuzzer_ipc_base RTfuzzer_ipc_test_util) - - generate_libfuzzer_unittests(FuzzerIPC ${arch} + generate_libfuzzer_unittests(FuzzerIPCUnitTests ${arch} SOURCES IPCLinuxUnittest.cpp - RUNTIME RTFuzzerIPCTest) + OBJECT_LIBS RTfuzzer_base + RTfuzzer_ipc_base + RTfuzzer_ipc_test_util) + + generate_libfuzzer_unittests(FuzzerIPCProxyUnitTests ${arch} + SOURCES IPCProxyLinuxUnittest.cpp + OBJECT_LIBS RTfuzzer_base + RTfuzzer_ipc_base + RTfuzzer_ipc_test_util + RTfuzzer_ipc_proxy) endif() Index: compiler-rt/lib/fuzzer/tests/ipc/IPCProxyLinuxUnittest.cpp =================================================================== --- /dev/null +++ compiler-rt/lib/fuzzer/tests/ipc/IPCProxyLinuxUnittest.cpp @@ -0,0 +1,407 @@ +//===- IPCRemoteLinuxUnittest.cpp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Unit test of IPCProxyLinux. +// +// This is kept separate from other unit tests as it must link against the +// ipc_proxy library, which has symbols that conflict with the fuzzer and +// fuzzer_remote runtimes and with the ipc_remote library, e.g. the +// FuzzerRemoteInterface symbols. +//===----------------------------------------------------------------------===// + +#include "FuzzerPlatform.h" +#if LIBFUZZER_LINUX + +#include "FuzzerRemoteInterface.h" +#include "ipc/IPCProxyLinux.h" +#include "ipc/IPCRemoteLinux.h" +#include "tests/FuzzerTestUtil.h" +#include "tests/ipc/IPCTestUtilLinux.h" +#include "gtest/gtest.h" + +namespace fuzzer { +namespace ipc { +namespace { + +// Test fixtures + +class IPCProxyLinuxTest : public TestBase { +public: + template static void OnProxyReceive(Responder R) { + ReceiveAtNear(R); + } + +protected: + void SetUp() override { + TestBase::SetUp(); + FarConnection.reset(new TestConnection()); + } + + void Connect() { + FuzzerAcceptRemotes(); + DoRemoteSend([&](TestConnection *TC) { + if (TC->Connect(kProxyAddressEnvVar)) + TC->StopReceiving(); + }); + auto TC = GetFarConnection(); + ProxyOrdinal Ordinal; + ProxiedOptions Options; + EXPECT_TRUE(TC->Receive(&Ordinal)); + EXPECT_TRUE(TC->Receive(&Options, sizeof(Options))); + } + + std::shared_ptr GetFarConnection() override { + return FarConnection; + } + + // Rename the base names for clarity. + template void DoProxySend(Requester R) { + SendFromNear(R); + } + + template void OnRemoteReceive(Responder R) { + ReceiveAtFar(R); + } + + template void DoRemoteSend(Requester R) { + SendFromFar(R); + } + + void TearDown() override { + FuzzerShutdownRemotes(); + FarConnection.reset(); + TestBase::TearDown(); + } + + std::shared_ptr FarConnection; +}; + +using IPCProxyLinuxDeathTest = IPCProxyLinuxTest; + +// Unit tests + +TEST_F(IPCProxyLinuxDeathTest, ProxyNotListening) { + auto TC = GetFarConnection(); + EXPECT_DEATH(TC->Connect("kProxyAddressEnvVar"), + "environment variable is not set"); +} + +static StaticTestMessage ConnectReq; + +extern "C" void FuzzerProxyConnect(unsigned long PID, void *Options, + size_t OptionsLen) { + IPCProxyLinuxTest::OnProxyReceive([&]() { + ConnectReq.PID = PID; + memset(Options, 0x41, OptionsLen); + }); +} + +TEST_F(IPCProxyLinuxTest, Connect) { + FuzzerAcceptRemotes(); + DoRemoteSend([&](TestConnection *TC) { TC->Connect(kProxyAddressEnvVar); }); + + std::vector Expected(sizeof(ProxiedOptions), 0x41); + auto TC = GetFarConnection(); + ProxyOrdinal Ordinal; + ProxiedOptions Options; + EXPECT_TRUE(TC->Receive(&Ordinal)); + EXPECT_TRUE(TC->Receive(&Options, sizeof(Options))); + EXPECT_EQ(Ordinal, kOptions); + EXPECT_EQ(memcmp(&Options, &Expected[0], sizeof(Options)), 0); +} + +static struct : public StaticTestMessage { + size_t NumCounters; + uint8_t *Counters; + size_t NumPCs; + std::unique_ptr PCs; +} AddCoverageReq; + +extern "C" uintptr_t FuzzerProxyAddCoverage(unsigned long PID, + uint8_t *CountersBegin, + uint8_t *CountersEnd, + const uintptr_t *PCsBegin, + const uintptr_t *PCsEnd) { + IPCProxyLinuxTest::OnProxyReceive([&]() { + AddCoverageReq.PID = PID; + AddCoverageReq.NumCounters = CountersEnd - CountersBegin; + AddCoverageReq.Counters = CountersBegin; + size_t N = PCsEnd - PCsBegin; + AddCoverageReq.NumPCs = N; + AddCoverageReq.PCs.reset(new uintptr_t[N]); + memcpy(AddCoverageReq.PCs.get(), PCsBegin, N * sizeof(uintptr_t)); + }); + return 0; // Fake index. +} + +TEST_F(IPCProxyLinuxTest, AddCoverage) { + Connect(); + + FakeDSO<0xcafe> DSO; + DoRemoteSend([&](TestConnection *TC) { + EXPECT_TRUE(TC->Send(kAddCoverage)); + EXPECT_TRUE(TC->SendMemory(DSO.Counters, sizeof(DSO.Counters), true)); + EXPECT_TRUE(TC->SendMemory(DSO.PCs, sizeof(DSO.PCs), false)); + }); + EXPECT_EQ(AddCoverageReq.PID, PID); + ASSERT_EQ(AddCoverageReq.NumCounters, sizeof(DSO.Counters)); + ASSERT_EQ(AddCoverageReq.NumPCs, sizeof(DSO.PCs) / sizeof(DSO.PCs[0])); + EXPECT_EQ(memcmp(AddCoverageReq.Counters, DSO.Counters, sizeof(DSO.Counters)), + 0); + EXPECT_EQ(memcmp(AddCoverageReq.PCs.get(), DSO.PCs, sizeof(DSO.PCs)), 0); +} + +TEST_F(IPCProxyLinuxTest, StartExecution) { + Connect(); + auto ExecOptionsIn = Pick(); + DoProxySend([&]() { FuzzerRemoteStartExecution(PID, ExecOptionsIn); }); + + ProxyOrdinal Ordinal; + uint32_t ExecOptionsOut; + OnRemoteReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + EXPECT_TRUE(TC->Receive(&ExecOptionsOut)); + }); + EXPECT_EQ(Ordinal, kStartExecution); + EXPECT_EQ(ExecOptionsIn, ExecOptionsOut); +} + +static StaticTestMessage ExecutionStartedReq; + +extern "C" void FuzzerProxyExecutionStarted(unsigned long PID) { + IPCProxyLinuxTest::OnProxyReceive([&]() { ExecutionStartedReq.PID = PID; }); +} + +TEST_F(IPCProxyLinuxTest, ExecutionStarted) { + Connect(); + DoRemoteSend( + [&](TestConnection *TC) { EXPECT_TRUE(TC->Send(kExecutionStarted)); }); + EXPECT_EQ(ExecutionStartedReq.PID, PID); +} + +TEST_F(IPCProxyLinuxTest, FinishExecution) { + Connect(); + DoProxySend([&]() { FuzzerRemoteFinishExecution(PID); }); + ProxyOrdinal Ordinal; + OnRemoteReceive( + [&](TestConnection *TC) { EXPECT_TRUE(TC->Receive(&Ordinal)); }); + EXPECT_EQ(Ordinal, kFinishExecution); +} + +static struct : public StaticTestMessage { + int HasMoreMallocsThanFrees; +} ExecutionFinishedReq; + +extern "C" void FuzzerProxyExecutionFinished(unsigned long PID, + int HasMoreMallocsThanFrees) { + IPCProxyLinuxTest::OnProxyReceive([&]() { + ExecutionFinishedReq.PID = PID; + ExecutionFinishedReq.HasMoreMallocsThanFrees = HasMoreMallocsThanFrees; + }); +} + +TEST_F(IPCProxyLinuxTest, ExecutionFinished) { + Connect(); + int HasMoreMallocsThanFrees = Pick(); + DoRemoteSend([&](TestConnection *TC) { + EXPECT_TRUE(TC->Send(kExecutionFinished)); + EXPECT_TRUE(TC->Send(HasMoreMallocsThanFrees)); + }); + EXPECT_EQ(ExecutionFinishedReq.PID, PID); + EXPECT_EQ(ExecutionFinishedReq.HasMoreMallocsThanFrees, + HasMoreMallocsThanFrees); +} + +static StaticTestMessage CrashCallbackReq; + +extern "C" void FuzzerCrashSignalCallback(unsigned long PID) { + IPCProxyLinuxTest::OnProxyReceive([&]() { CrashCallbackReq.PID = PID; }); +} + +TEST_F(IPCProxyLinuxTest, CrashCallback) { + Connect(); + DoRemoteSend( + [&](TestConnection *TC) { EXPECT_TRUE(TC->Send(kCrashCallback)); }); + EXPECT_EQ(CrashCallbackReq.PID, PID); +} + +static StaticTestMessage DeathCallbackReq; + +extern "C" void FuzzerDeathCallback() { + IPCProxyLinuxTest::OnProxyReceive([&]() { DeathCallbackReq.PID = GetPid(); }); +} + +TEST_F(IPCProxyLinuxTest, DeathCallback) { + Connect(); + DoRemoteSend( + [&](TestConnection *TC) { EXPECT_TRUE(TC->Send(kDeathCallback)); }); + EXPECT_EQ(DeathCallbackReq.PID, GetPid()); +} + +static struct : public StaticTestMessage { int ExitCode; } ExitCallbackReq; + +extern "C" void FuzzerExitCallback(unsigned long PID) { + IPCProxyLinuxTest::OnProxyReceive([&]() { ExitCallbackReq.PID = PID; }); +} + +TEST_F(IPCProxyLinuxTest, ExitCallback) { + Connect(); + DoRemoteSend( + [&](TestConnection *TC) { EXPECT_TRUE(TC->Send(kExitCallback)); }); + EXPECT_EQ(ExitCallbackReq.PID, PID); +} + +static StaticTestMessage LeakCallbackReq; + +extern "C" void FuzzerLeakCallback(unsigned long PID) { + IPCProxyLinuxTest::OnProxyReceive([&]() { LeakCallbackReq.PID = PID; }); +} + +TEST_F(IPCProxyLinuxTest, LeakCallback) { + Connect(); + DoRemoteSend( + [&](TestConnection *TC) { EXPECT_TRUE(TC->Send(kLeakCallback)); }); + EXPECT_EQ(LeakCallbackReq.PID, PID); +} + +static struct : public StaticTestMessage { + size_t MallocSize; +} MallocLimitCallbackReq; + +extern "C" void FuzzerMallocLimitCallback(unsigned long PID, + size_t MallocSize) { + IPCProxyLinuxTest::OnProxyReceive([&]() { + MallocLimitCallbackReq.PID = PID; + MallocLimitCallbackReq.MallocSize = MallocSize; + }); +} + +TEST_F(IPCProxyLinuxTest, MallocLimitCallback) { + Connect(); + auto MallocSize = Pick(); + DoRemoteSend([&](TestConnection *TC) { + EXPECT_TRUE(TC->Send(kMallocLimitCallback)); + EXPECT_TRUE(TC->Send(MallocSize)); + }); + EXPECT_EQ(MallocLimitCallbackReq.PID, PID); + EXPECT_EQ(MallocLimitCallbackReq.MallocSize, MallocSize); +} + +static StaticTestMessage RssLimitCallbackReq; + +extern "C" void FuzzerRssLimitCallback(unsigned long PID) { + IPCProxyLinuxTest::OnProxyReceive([&]() { RssLimitCallbackReq.PID = PID; }); +} + +TEST_F(IPCProxyLinuxTest, RssLimitCallback) { + Connect(); + DoRemoteSend( + [&](TestConnection *TC) { EXPECT_TRUE(TC->Send(kRssLimitCallback)); }); + EXPECT_EQ(RssLimitCallbackReq.PID, PID); +} + +TEST_F(IPCProxyLinuxTest, PrintPC) { + Connect(); + const char *SymbolizedFMTIn = "symbolized format: %lu"; + const char *FallbackFMTIn = "fallback format: %lu"; + auto PCIn = Pick(); + DoProxySend([&]() { + FuzzerRemotePrintPC(PID, SymbolizedFMTIn, FallbackFMTIn, PCIn); + }); + + ProxyOrdinal Ordinal; + char SymbolizedFMTOut[kMaxDescLen]; + char FallbackFMTOut[kMaxDescLen]; + uintptr_t PCOut; + OnRemoteReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + EXPECT_TRUE(TC->ReceiveString(SymbolizedFMTOut, sizeof(SymbolizedFMTOut))); + EXPECT_TRUE(TC->ReceiveString(FallbackFMTOut, sizeof(FallbackFMTOut))); + EXPECT_TRUE(TC->Receive(&PCOut)); + EXPECT_TRUE(TC->Send(Connection::kAcknowledgement)); + }); + EXPECT_EQ(Ordinal, kPrintPC); + EXPECT_STREQ(SymbolizedFMTIn, SymbolizedFMTOut); + EXPECT_STREQ(FallbackFMTIn, FallbackFMTOut); + EXPECT_EQ(PCIn, PCOut); +} + +TEST_F(IPCProxyLinuxTest, DescribePC) { + Connect(); + const char *SymbolizedFMTIn = "symbolized format: %lu"; + auto PCIn = Pick(); + char Expected[kMaxDescLen]; + snprintf(Expected, sizeof(Expected), SymbolizedFMTIn, PCIn); + char Actual[kMaxDescLen]; + DoProxySend([&]() { + FuzzerRemoteDescribePC(PID, SymbolizedFMTIn, PCIn, Actual, sizeof(Actual)); + }); + + ProxyOrdinal Ordinal; + char SymbolizedFMTOut[kMaxDescLen]; + uintptr_t PCOut; + char Desc[kMaxDescLen]; + size_t DescLen; + OnRemoteReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + EXPECT_TRUE(TC->ReceiveString(SymbolizedFMTOut, sizeof(SymbolizedFMTOut))); + EXPECT_TRUE(TC->Receive(&PCOut)); + EXPECT_TRUE(TC->Receive(&DescLen)); + ASSERT_LE(DescLen, sizeof(Desc)); + snprintf(Desc, DescLen, SymbolizedFMTOut, PCOut); + EXPECT_TRUE(TC->Send(kDescribePCResponse)); + EXPECT_TRUE(TC->SendString(Desc)); + }); + + EXPECT_EQ(Ordinal, kDescribePC); + EXPECT_STREQ(SymbolizedFMTIn, SymbolizedFMTOut); + EXPECT_EQ(PCIn, PCOut); + EXPECT_EQ(DescLen, sizeof(Actual)); + EXPECT_STREQ(Expected, Actual); +} + +TEST_F(IPCProxyLinuxTest, PrintStackTrace) { + Connect(); + DoProxySend([&]() { FuzzerRemotePrintStackTrace(PID); }); + + ProxyOrdinal Ordinal; + OnRemoteReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + EXPECT_TRUE(TC->Send(Connection::kAcknowledgement)); + }); + EXPECT_EQ(Ordinal, kPrintStackTrace); +} + +TEST_F(IPCProxyLinuxTest, PrintMemoryProfile) { + Connect(); + DoProxySend([&]() { FuzzerRemotePrintMemoryProfile(PID); }); + + ProxyOrdinal Ordinal; + OnRemoteReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + EXPECT_TRUE(TC->Send(Connection::kAcknowledgement)); + }); + EXPECT_EQ(Ordinal, kPrintMemoryProfile); +} + +TEST_F(IPCProxyLinuxTest, DetectLeaksAtExit) { + Connect(); + DoProxySend([&]() { FuzzerRemoteDetectLeaksAtExit(PID); }); + + ProxyOrdinal Ordinal; + OnRemoteReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + EXPECT_TRUE(TC->Send(Connection::kAcknowledgement)); + }); + EXPECT_EQ(Ordinal, kDetectLeaksAtExit); +} + +} // namespace +} // namespace ipc +} // namespace fuzzer + +#endif // LIBFUZZER_LINUX Index: compiler-rt/test/fuzzer/CMakeLists.txt =================================================================== --- compiler-rt/test/fuzzer/CMakeLists.txt +++ compiler-rt/test/fuzzer/CMakeLists.txt @@ -22,6 +22,7 @@ list(APPEND LIBFUZZER_TEST_DEPS FuzzedDataProviderUnitTests) list(APPEND LIBFUZZER_TEST_DEPS FuzzerRemoteUnitTests) list(APPEND LIBFUZZER_TEST_DEPS FuzzerIPCUnitTests) + list(APPEND LIBFUZZER_TEST_DEPS FuzzerIPCProxyUnitTests) endif() add_custom_target(check-fuzzer)