Index: compiler-rt/lib/fuzzer/build.sh =================================================================== --- compiler-rt/lib/fuzzer/build.sh +++ compiler-rt/lib/fuzzer/build.sh @@ -79,4 +79,10 @@ IPCProxyLinux.o \ IPCQuickExit.o \ +rm -f libFuzzerIPCRemote.a +ar r libFuzzerIPCRemote.a \ + IPCLinux.o \ + IPCQuickExit.o \ + IPCRemoteLinux.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 @@ -12,6 +12,10 @@ SOURCES IPCProxyLinux.cpp HEADERS IPCProxyLinux.h IPCRemoteLinux.h) +add_libfuzzer_objects(RTfuzzer_ipc_remote + SOURCES IPCRemoteLinux.cpp + HEADERS IPCRemoteLinux.h) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") function(add_partially_linked_library name arch) cmake_parse_arguments(LOCAL "" "" "OBJECT_LIBS" "" ${ARGN}) @@ -32,5 +36,12 @@ RTfuzzer_ipc_base RTfuzzer_ipc RTfuzzer_ipc_proxy) + + add_partially_linked_library(RTFuzzerIPCRemote ${arch} + OBJECT_LIBS RTfuzzer_base + RTfuzzer_remote + RTfuzzer_ipc_base + RTfuzzer_ipc + RTfuzzer_ipc_remote) endforeach() endif() Index: compiler-rt/lib/fuzzer/ipc/IPCRemoteLinux.cpp =================================================================== --- /dev/null +++ compiler-rt/lib/fuzzer/ipc/IPCRemoteLinux.cpp @@ -0,0 +1,289 @@ +//===- IPCRemoteLinux.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 FuzzerRemote on Linux. +//===----------------------------------------------------------------------===// + +#include "FuzzerPlatform.h" +#if LIBFUZZER_LINUX + +#include "FuzzerUtil.h" + +#include "FuzzerExtFunctions.h" +#include "FuzzerRemoteInterface.h" +#include "IPCLinux.h" +#include "IPCProxyLinux.h" +#include "IPCRemoteLinux.h" +#include +#include +#include + +namespace fuzzer { +namespace ipc { +namespace { + +// This class represents a single FuzzerProxy-side endpoint for a connection to +// a FuzzerRemote process. +class RemoteConnection final : public Connection { +public: + static RemoteConnection &GetInstance() { + static RemoteConnection Singleton; + return Singleton; + } + + ~RemoteConnection() override { Reset(); } + + void Connect(void *Options, size_t OptionsLen) { + PID = getpid(); + ReceivedOptions = Options; + ReceivedOptionsLen = OptionsLen; + if (!Connection::Connect(fuzzer::ipc::kProxyAddressEnvVar)) { + Printf("ERROR: Failed to connect to proxy.\n"); + exit(1); + } + } + + bool DoHandshake() override { + Connection::DoHandshake(); + std::lock_guard Lock(GetRxMutex()); + ProxyOrdinal O; + if (!ReceiveLocked(&O) || O != kOptions || + !ReceiveLocked(ReceivedOptions, ReceivedOptionsLen)) + return false; + ReceivedOptions = nullptr; + ReceivedOptionsLen = 0; + return true; + } + + void AddCoverage(uint8_t *CountersBegin, uint8_t *CountersEnd, + const uintptr_t *PCsBegin, const uintptr_t *PCsEnd) { + if (CountersEnd <= CountersBegin || PCsEnd <= PCsBegin) + return; + { + std::lock_guard TxLock(GetTxMutex()); + SendLocked(kAddCoverage); + size_t Len = (CountersEnd - CountersBegin) * sizeof(uint8_t); + SendMemoryLocked(CountersBegin, Len, /* Updatable= */ true); + Len = (PCsEnd - PCsBegin) * sizeof(uintptr_t); + SendMemoryLocked(PCsBegin, Len, /* Updatable= */ false); + } + WaitForAcknowledgement(); + } + + void ExecutionStarted() { + std::lock_guard TxLock(GetTxMutex()); + SendLocked(kExecutionStarted); + } + + void ExecutionFinished(int HasMoreMallocsThanFrees) { + UpdateSharedMemory(); + std::lock_guard TxLock(GetTxMutex()); + SendLocked(kExecutionFinished); + SendLocked(HasMoreMallocsThanFrees); + } + + void CrashCallback() { + DispatchOnCurrentThread( + [this]() REQUIRES(GetTxMutex()) { SendLocked(kCrashCallback); }); + } + + void DeathCallback() { + DispatchOnCurrentThread( + [this]() REQUIRES(GetTxMutex()) { SendLocked(kDeathCallback); }); + } + + void ExitCallback() { + DispatchOnCurrentThread( + [this]() REQUIRES(GetTxMutex()) { SendLocked(kExitCallback); }); + } + + void LeakCallback() { + // LeakCallback is only ever invoked from the ReceiverThread in response to + // either a kFinishExecution or kDetectLeaksAtExit message. Additionally, + // it doesn't have an associated stack trace, so this can exit immediately. + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + { + std::lock_guard TxLock(GetTxMutex()); + SendLocked(kLeakCallback); + } + } + + void MallocLimitCallback(size_t Size) { + DispatchOnCurrentThread([this, Size]() REQUIRES(GetTxMutex()) { + SendLocked(kMallocLimitCallback); + SendLocked(Size); + }); + } + + void RssLimitCallback() { + DispatchOnCurrentThread( + [this]() REQUIRES(GetTxMutex()) { SendLocked(kRssLimitCallback); }); + } + +private: + bool DispatchOneLocked() override REQUIRES(GetRxMutex()) { + ProxyOrdinal O; + if (!ReceiveLocked(&O)) + return false; + switch (O) { + case kProxyAck: { + HandleAcknowledgement(); + break; + } + case kStartExecution: { + uint32_t Options; + if (!ReceiveLocked(&Options)) + return false; + FuzzerRemoteStartExecution(PID, Options); + break; + } + case kFinishExecution: { + FuzzerRemoteFinishExecution(PID); + break; + } + case kPrintPC: { + char SymbolizedFMT[kMaxDescLen]; + char FallbackFMT[kMaxDescLen]; + uintptr_t PC; + if (!ReceiveStringLocked(SymbolizedFMT, sizeof(SymbolizedFMT)) || + !ReceiveStringLocked(FallbackFMT, sizeof(FallbackFMT)) || + !ReceiveLocked(&PC)) { + return false; + } + FuzzerRemotePrintPC(PID, SymbolizedFMT, FallbackFMT, PC); + Acknowledge(); + break; + } + case kDescribePC: { + char SymbolizedFMT[kMaxDescLen]; + uintptr_t PC; + char Desc[kMaxDescLen]; + size_t DescLen; + if (!ReceiveStringLocked(SymbolizedFMT, sizeof(SymbolizedFMT)) || + !ReceiveLocked(&PC) || !ReceiveLocked(&DescLen)) + return false; + assert(DescLen <= kMaxDescLen); + FuzzerRemoteDescribePC(PID, SymbolizedFMT, PC, Desc, DescLen); + UniqueLock TxLock(GetTxMutex()); + SendLocked(kDescribePCResponse); + SendStringLocked(Desc); + break; + } + case kPrintStackTrace: { + FuzzerRemotePrintStackTrace(PID); + Acknowledge(); + break; + } + case kPrintMemoryProfile: { + FuzzerRemotePrintMemoryProfile(PID); + Acknowledge(); + break; + } + case kDetectLeaksAtExit: { + FuzzerRemoteDetectLeaksAtExit(PID); + Acknowledge(); + break; + } + default: + Printf("ERROR: protocol: unknown request: %u\n", O); + Close(); + exit(1); + } + return true; + } + + template void DispatchOnCurrentThread(TxCallback CB) { + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + { + UniqueLock TxLock(GetTxMutex()); + CB(); + } + StopReceiving(); + DispatchLoop(); + } + + pid_t PID; + void *ReceivedOptions; + size_t ReceivedOptionsLen; + std::condition_variable OnCloseCV; +}; + +} // namespace + +} // namespace ipc +} // namespace fuzzer + +// FuzzerRemoteInterface and FuzzerMonitor implementations. The IPC layer looks +// like FuzzerProxy to the FuzzerRemotes. + +extern "C" { + +ATTRIBUTE_INTERFACE void FuzzerProxyConnect(unsigned long PID, void *Options, + size_t OptionsLen) { + auto &RC = fuzzer::ipc::RemoteConnection::GetInstance(); + RC.Connect(Options, OptionsLen); +} + +ATTRIBUTE_INTERFACE uintptr_t FuzzerProxyAddCoverage(unsigned long PID, + uint8_t *CountersBegin, + uint8_t *CountersEnd, + const uintptr_t *PCsBegin, + const uintptr_t *PCsEnd) { + auto &RC = fuzzer::ipc::RemoteConnection::GetInstance(); + RC.AddCoverage(CountersBegin, CountersEnd, PCsBegin, PCsEnd); + return fuzzer::kInvalidIdx; // Ignored by FuzzerRemote. +} + +ATTRIBUTE_INTERFACE void FuzzerProxyExecutionStarted(unsigned long PID) { + auto &RC = fuzzer::ipc::RemoteConnection::GetInstance(); + RC.ExecutionStarted(); +} + +ATTRIBUTE_INTERFACE void +FuzzerProxyExecutionFinished(unsigned long PID, int HasMoreMallocsThanFrees) { + auto &RC = fuzzer::ipc::RemoteConnection::GetInstance(); + RC.ExecutionFinished(HasMoreMallocsThanFrees); +} + +ATTRIBUTE_INTERFACE void FuzzerCrashSignalCallback(unsigned long PID) { + auto &RC = fuzzer::ipc::RemoteConnection::GetInstance(); + RC.CrashCallback(); +} + +ATTRIBUTE_INTERFACE void FuzzerDeathCallback() { + auto &RC = fuzzer::ipc::RemoteConnection::GetInstance(); + RC.DeathCallback(); +} + +ATTRIBUTE_INTERFACE void FuzzerExitCallback(unsigned long PID) { + auto &RC = fuzzer::ipc::RemoteConnection::GetInstance(); + RC.ExitCallback(); +} + +ATTRIBUTE_INTERFACE void FuzzerLeakCallback(unsigned long PID) { + auto &RC = fuzzer::ipc::RemoteConnection::GetInstance(); + RC.LeakCallback(); +} + +ATTRIBUTE_INTERFACE void FuzzerMallocLimitCallback(unsigned long PID, + size_t Size) { + auto &RC = fuzzer::ipc::RemoteConnection::GetInstance(); + RC.MallocLimitCallback(Size); +} + +ATTRIBUTE_INTERFACE void FuzzerRssLimitCallback(unsigned long PID) { + auto &RC = fuzzer::ipc::RemoteConnection::GetInstance(); + RC.RssLimitCallback(); +} + +} // extern "C" + +#endif // LIBFUZZER_LINUX 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 @@ -25,4 +25,11 @@ RTfuzzer_ipc_base RTfuzzer_ipc_test_util RTfuzzer_ipc_proxy) + + generate_libfuzzer_unittests(FuzzerIPCRemoteUnitTests ${arch} + SOURCES IPCRemoteLinuxUnittest.cpp + OBJECT_LIBS RTfuzzer_base + RTfuzzer_ipc_base + RTfuzzer_ipc_test_util + RTfuzzer_ipc_remote) endif() Index: compiler-rt/lib/fuzzer/tests/ipc/IPCRemoteLinuxUnittest.cpp =================================================================== --- /dev/null +++ compiler-rt/lib/fuzzer/tests/ipc/IPCRemoteLinuxUnittest.cpp @@ -0,0 +1,430 @@ +//===- 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 IPCRemoteLinux. +// +// This is kept separate from other unit tests as it must link against the +// ipc_remote library, which has symbols that conflict with the fuzzer and +// fuzzer_remote runtimes and with the fuzzer_ipc_proxy library, e.g. the +// FuzzerRemoteInterface symbols. +//===----------------------------------------------------------------------===// + +#include "FuzzerPlatform.h" +#if LIBFUZZER_LINUX + +#include "FuzzerMonitor.h" +#include "FuzzerRemoteInterface.h" +#include "ipc/IPCProxyLinux.h" +#include "ipc/IPCRemoteLinux.h" +#include "tests/FuzzerTestUtil.h" +#include "tests/ipc/IPCTestUtilLinux.h" +#include "gtest/gtest.h" +#include +#include + +namespace fuzzer { +namespace ipc { +namespace { + +// Test fixtures + +// Shadows sanitizer_common's crash state to allow reuse across tests. +std::atomic gCrashState = {}; + +class IPCRemoteLinuxTest : public TestBase { +public: + template static void OnRemoteReceive(Responder R) { + ReceiveAtNear(R); + } + +protected: + void SetUp() override { + TestBase::SetUp(); + Proxy.Start(kProxyAddressEnvVar); + gCrashState = 0; + EF->__sanitizer_acquire_crash_state = []() -> int { + return !gCrashState.exchange(1); + }; + } + + void Connect() { + uint8_t Ignored; + DoRemoteSend([&]() { FuzzerProxyConnect(PID, &Ignored, sizeof(Ignored)); }); + Proxy.WaitForConnection(); + OnProxyReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Send(kOptions)); + EXPECT_TRUE(TC->Send(Ignored)); + }); + } + + // Rename the base names for clarity. + template void DoRemoteSend(Requester R) { + SendFromNear(R); + } + + template void OnProxyReceive(Responder R) { + ReceiveAtFar(R); + } + + template void DoProxySend(Requester R) { + SendFromFar(R); + } + + std::shared_ptr GetFarConnection() override { + return Proxy.GetTestConnection(PID); + } + + TestServer Proxy; + + void TearDown() override { + Proxy.Stop(); + EF->__sanitizer_acquire_crash_state = + DefaultEF.__sanitizer_acquire_crash_state; + TestBase::TearDown(); + } + +private: + ExternalFunctions DefaultEF; +}; + +// Unit tests + +TEST_F(IPCRemoteLinuxTest, Connect) { + constexpr size_t kOptionsSize = 0x2020; + uint8_t OptionsOut[kOptionsSize]; + + DoRemoteSend( + [&]() { FuzzerProxyConnect(PID, &OptionsOut, sizeof(OptionsOut)); }); + Proxy.WaitForConnection(); + + uint8_t OptionsIn[kOptionsSize]; + for (size_t i = 0; i < kOptionsSize; ++i) + OptionsIn[i] = Pick(); + + OnProxyReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Send(kOptions)); + EXPECT_TRUE(TC->Send(OptionsIn, sizeof(OptionsIn))); + }); + + EXPECT_EQ(memcmp(OptionsIn, OptionsOut, sizeof(OptionsIn)), 0); +} + +TEST_F(IPCRemoteLinuxTest, AddCoverage) { + Connect(); + + // Initialize coverage. + FakeDSO<0x1000> DSO; + DoRemoteSend([&]() { + FuzzerProxyAddCoverage(PID, DSO.Counters, DSO.CountersEnd(), DSO.PCs, + DSO.PCsEnd()); + }); + + void *Addr1, *Addr2; + size_t Len1, Len2; + RemoteOrdinal Ordinal; + OnProxyReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + EXPECT_TRUE(TC->ReceiveMemory(&Addr1, &Len1)); + EXPECT_TRUE(TC->ReceiveMemory(&Addr2, &Len2)); + EXPECT_TRUE(TC->Send(Connection::kAcknowledgement)); + }); + EXPECT_EQ(Ordinal, kAddCoverage); + + uint8_t *CountersBegin = reinterpret_cast(Addr1); + ASSERT_EQ(Len1, sizeof(DSO.Counters)); + EXPECT_EQ(memcmp(CountersBegin, DSO.Counters, Len1), 0); + ASSERT_NE(munmap(Addr1, Len1), -1); + + uintptr_t *PCsBegin = reinterpret_cast(Addr2); + ASSERT_EQ(Len2, sizeof(DSO.PCs)); + EXPECT_EQ(memcmp(PCsBegin, DSO.PCs, Len2), 0); + ASSERT_NE(munmap(Addr2, Len2), -1); +} + +static struct : public StaticTestMessage { + uint32_t ExecOptions; +} StartExecutionReq; + +extern "C" void FuzzerRemoteStartExecution(unsigned long PID, + uint32_t ExecOptions) { + IPCRemoteLinuxTest::OnRemoteReceive([&]() { + StartExecutionReq.PID = PID; + StartExecutionReq.ExecOptions = ExecOptions; + }); +} + +TEST_F(IPCRemoteLinuxTest, StartExecution) { + Connect(); + auto ExecOptions = Pick(); + DoProxySend([&](TestConnection *TC) { + EXPECT_TRUE(TC->Send(kStartExecution)); + EXPECT_TRUE(TC->Send(ExecOptions)); + }); + EXPECT_EQ(StartExecutionReq.PID, PID); + EXPECT_EQ(StartExecutionReq.ExecOptions, ExecOptions); +} + +static StaticTestMessage FinishExecutionReq; + +extern "C" void FuzzerRemoteFinishExecution(unsigned long PID) { + IPCRemoteLinuxTest::OnRemoteReceive([&]() { FinishExecutionReq.PID = PID; }); +} + +TEST_F(IPCRemoteLinuxTest, FinishExecution) { + Connect(); + DoProxySend( + [&](TestConnection *TC) { EXPECT_TRUE(TC->Send(kFinishExecution)); }); + EXPECT_EQ(FinishExecutionReq.PID, PID); +} + +TEST_F(IPCRemoteLinuxTest, ExecutionFinished) { + Connect(); + + // Initialize coverage. + FakeDSO<0x1000> DSO; + DoRemoteSend([&]() { + FuzzerProxyAddCoverage(PID, DSO.Counters, DSO.CountersEnd(), DSO.PCs, + DSO.PCsEnd()); + }); + + RemoteOrdinal Ordinal; + void *Addr1, *Addr2; + size_t Len1, Len2; + OnProxyReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + EXPECT_TRUE(TC->ReceiveMemory(&Addr1, &Len1)); + EXPECT_TRUE(TC->ReceiveMemory(&Addr2, &Len2)); + EXPECT_TRUE(TC->Send(Connection::kAcknowledgement)); + }); + EXPECT_EQ(Ordinal, kAddCoverage); + + uint8_t *CountersBegin = reinterpret_cast(Addr1); + ASSERT_EQ(Len1, sizeof(DSO.Counters)); + + // Simulate finishing an iteration. Counters should be updated. + for (size_t i = 0; i < DSO.NumPCs; ++i) + DSO.Counters[i] = Pick(); + int HasMoreMallocsThanFreesIn = 8; + DoRemoteSend( + [&]() { FuzzerProxyExecutionFinished(PID, HasMoreMallocsThanFreesIn); }); + + int HasMoreMallocsThanFreesOut; + OnProxyReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + EXPECT_TRUE(TC->Receive(&HasMoreMallocsThanFreesOut)); + }); + EXPECT_EQ(Ordinal, kExecutionFinished); + EXPECT_EQ(HasMoreMallocsThanFreesIn, HasMoreMallocsThanFreesOut); + EXPECT_EQ(memcmp(CountersBegin, DSO.Counters, Len1), 0); + ASSERT_NE(munmap(Addr1, Len1), -1); + ASSERT_NE(munmap(Addr2, Len2), -1); +} + +TEST_F(IPCRemoteLinuxTest, CrashCallback) { + Connect(); + DoRemoteSend([&]() { FuzzerCrashSignalCallback(PID); }); + + RemoteOrdinal Ordinal; + OnProxyReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + TC->Close(); + }); + EXPECT_EQ(Ordinal, kCrashCallback); +} + +TEST_F(IPCRemoteLinuxTest, DeathCallback) { + Connect(); + DoRemoteSend([&]() { FuzzerDeathCallback(); }); + + RemoteOrdinal Ordinal; + OnProxyReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + TC->Close(); + }); + EXPECT_EQ(Ordinal, kDeathCallback); +} + +TEST_F(IPCRemoteLinuxTest, ExitCallback) { + Connect(); + DoRemoteSend([&]() { FuzzerExitCallback(PID); }); + + RemoteOrdinal Ordinal; + OnProxyReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + TC->Close(); + }); + EXPECT_EQ(Ordinal, kExitCallback); +} + +TEST_F(IPCRemoteLinuxTest, LeakCallback) { + Connect(); + DoRemoteSend([&]() { FuzzerLeakCallback(PID); }); + + RemoteOrdinal Ordinal; + OnProxyReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + TC->Close(); + }); + EXPECT_EQ(Ordinal, kLeakCallback); +} + +TEST_F(IPCRemoteLinuxTest, MallocLimitCallback) { + Connect(); + size_t SizeIn = std::numeric_limits::max(); + DoRemoteSend([&]() { FuzzerMallocLimitCallback(PID, SizeIn); }); + + RemoteOrdinal Ordinal; + size_t SizeOut; + OnProxyReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + EXPECT_TRUE(TC->Receive(&SizeOut)); + TC->Close(); + }); + EXPECT_EQ(Ordinal, kMallocLimitCallback); + EXPECT_EQ(SizeIn, SizeOut); +} + +TEST_F(IPCRemoteLinuxTest, RssLimitCallback) { + Connect(); + DoRemoteSend([&]() { FuzzerRssLimitCallback(PID); }); + + RemoteOrdinal Ordinal; + OnProxyReceive([&](TestConnection *TC) { + EXPECT_TRUE(TC->Receive(&Ordinal)); + TC->Close(); + }); + EXPECT_EQ(Ordinal, kRssLimitCallback); +} + +static struct : public StaticTestMessage { + char SymbolizedFMT[kMaxDescLen]; + char FallbackFMT[kMaxDescLen]; + uintptr_t PC; +} PrintPCReq; + +extern "C" void FuzzerRemotePrintPC(unsigned long PID, + const char *SymbolizedFMT, + const char *FallbackFMT, uintptr_t PC) { + IPCRemoteLinuxTest::OnRemoteReceive([&]() { + PrintPCReq.PID = PID; + strncpy(PrintPCReq.SymbolizedFMT, SymbolizedFMT, + sizeof(PrintPCReq.SymbolizedFMT)); + strncpy(PrintPCReq.FallbackFMT, FallbackFMT, + sizeof(PrintPCReq.FallbackFMT)); + PrintPCReq.PC = PC; + }); +} + +TEST_F(IPCRemoteLinuxTest, PrintPC) { + Connect(); + const char *SymbolizedFMT = "symbolized format"; + const char *FallbackFMT = "fallback format"; + auto PC = Pick(); + DoProxySend([&](TestConnection *TC) { + EXPECT_TRUE(TC->Send(kPrintPC)); + EXPECT_TRUE(TC->SendString(SymbolizedFMT)); + EXPECT_TRUE(TC->SendString(FallbackFMT)); + EXPECT_TRUE(TC->Send(PC)); + }); + + EXPECT_EQ(PrintPCReq.PID, PID); + EXPECT_STREQ(PrintPCReq.SymbolizedFMT, SymbolizedFMT); + EXPECT_STREQ(PrintPCReq.FallbackFMT, FallbackFMT); + EXPECT_EQ(PrintPCReq.PC, PC); +} + +static struct : public StaticTestMessage { + char SymbolizedFMT[kMaxDescLen]; + uintptr_t PC; +} DescribePCReq; + +extern "C" void FuzzerRemoteDescribePC(unsigned long PID, + const char *SymbolizedFMT, uintptr_t PC, + char *Desc, size_t DescLen) { + IPCRemoteLinuxTest::OnRemoteReceive([&]() { + DescribePCReq.PID = PID; + strncpy(DescribePCReq.SymbolizedFMT, SymbolizedFMT, + sizeof(DescribePCReq.SymbolizedFMT)); + DescribePCReq.PC = PC; + snprintf(Desc, DescLen, SymbolizedFMT, PC); + }); +} + +TEST_F(IPCRemoteLinuxTest, DescribePC) { + Connect(); + const char *SymbolizedFMT = "symbolized format: %lu"; + auto PC = Pick(); + char Desc[kMaxDescLen]; + DoProxySend([&](TestConnection *TC) { + EXPECT_TRUE(TC->Send(kDescribePC)); + EXPECT_TRUE(TC->SendString(SymbolizedFMT)); + EXPECT_TRUE(TC->Send(PC)); + EXPECT_TRUE(TC->Send(sizeof(Desc))); + }); + + EXPECT_EQ(DescribePCReq.PID, PID); + EXPECT_STREQ(DescribePCReq.SymbolizedFMT, SymbolizedFMT); + EXPECT_EQ(DescribePCReq.PC, PC); + + auto TC = GetFarConnection(); + RemoteOrdinal Ordinal; + EXPECT_TRUE(TC->Receive(&Ordinal)); + EXPECT_TRUE(TC->ReceiveString(Desc, sizeof(Desc))); + + char Expected[sizeof(Desc)]; + snprintf(Expected, sizeof(Expected), SymbolizedFMT, PC); + EXPECT_EQ(Ordinal, kDescribePCResponse); + EXPECT_STREQ(Desc, Expected); +} + +static StaticTestMessage PrintStackTraceReq; + +extern "C" void FuzzerRemotePrintStackTrace(unsigned long PID) { + IPCRemoteLinuxTest::OnRemoteReceive([&]() { PrintStackTraceReq.PID = PID; }); +} + +TEST_F(IPCRemoteLinuxTest, PrintStackTrace) { + Connect(); + DoProxySend( + [&](TestConnection *TC) { EXPECT_TRUE(TC->Send(kPrintStackTrace)); }); + EXPECT_EQ(PrintStackTraceReq.PID, PID); +} + +static StaticTestMessage PrintMemoryProfileReq; + +extern "C" void FuzzerRemotePrintMemoryProfile(unsigned long PID) { + IPCRemoteLinuxTest::OnRemoteReceive( + [&]() { PrintMemoryProfileReq.PID = PID; }); +} + +TEST_F(IPCRemoteLinuxTest, PrintMemoryProfile) { + Connect(); + DoProxySend( + [&](TestConnection *TC) { EXPECT_TRUE(TC->Send(kPrintMemoryProfile)); }); + EXPECT_EQ(PrintMemoryProfileReq.PID, PID); +} + +static StaticTestMessage DetectLeaksAtExitReq; + +extern "C" void FuzzerRemoteDetectLeaksAtExit(unsigned long PID) { + IPCRemoteLinuxTest::OnRemoteReceive( + [&]() { DetectLeaksAtExitReq.PID = PID; }); +} + +TEST_F(IPCRemoteLinuxTest, DetectLeaksAtExit) { + Connect(); + DoProxySend( + [&](TestConnection *TC) { EXPECT_TRUE(TC->Send(kDetectLeaksAtExit)); }); + + EXPECT_EQ(DetectLeaksAtExitReq.PID, PID); +} + +} // 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 @@ -23,6 +23,7 @@ list(APPEND LIBFUZZER_TEST_DEPS FuzzerRemoteUnitTests) list(APPEND LIBFUZZER_TEST_DEPS FuzzerIPCUnitTests) list(APPEND LIBFUZZER_TEST_DEPS FuzzerIPCProxyUnitTests) + list(APPEND LIBFUZZER_TEST_DEPS FuzzerIPCRemoteUnitTests) endif() add_custom_target(check-fuzzer)