From 98d592cc91728694defd32ec30b90004fcaeacd1 Mon Sep 17 00:00:00 2001 From: Kostya Serebryany Date: Fri, 20 Jan 2017 20:57:07 +0000 Subject: [PATCH] [libFuzzer] experimental support for 'equivalance fuzzing' llvm-svn: 292646 --- llvm/lib/Fuzzer/CMakeLists.txt | 1 + llvm/lib/Fuzzer/FuzzerDriver.cpp | 26 +++++++++ llvm/lib/Fuzzer/FuzzerFlags.def | 3 + llvm/lib/Fuzzer/FuzzerInternal.h | 1 + llvm/lib/Fuzzer/FuzzerLoop.cpp | 33 ++++++++++- llvm/lib/Fuzzer/FuzzerShmem.h | 69 ++++++++++++++++++++++ llvm/lib/Fuzzer/FuzzerShmemPosix.cpp | 97 +++++++++++++++++++++++++++++++ llvm/lib/Fuzzer/test/EquivalenceATest.cpp | 6 +- llvm/lib/Fuzzer/test/EquivalenceBTest.cpp | 9 +-- llvm/lib/Fuzzer/test/equivalence.test | 5 ++ 10 files changed, 243 insertions(+), 7 deletions(-) create mode 100644 llvm/lib/Fuzzer/FuzzerShmem.h create mode 100644 llvm/lib/Fuzzer/FuzzerShmemPosix.cpp create mode 100644 llvm/lib/Fuzzer/test/equivalence.test diff --git a/llvm/lib/Fuzzer/CMakeLists.txt b/llvm/lib/Fuzzer/CMakeLists.txt index 70bd017..7e41ef8 100644 --- a/llvm/lib/Fuzzer/CMakeLists.txt +++ b/llvm/lib/Fuzzer/CMakeLists.txt @@ -21,6 +21,7 @@ if( LLVM_USE_SANITIZE_COVERAGE ) FuzzerMerge.cpp FuzzerMutate.cpp FuzzerSHA1.cpp + FuzzerShmemPosix.cpp FuzzerTracePC.cpp FuzzerTraceState.cpp FuzzerUtil.cpp diff --git a/llvm/lib/Fuzzer/FuzzerDriver.cpp b/llvm/lib/Fuzzer/FuzzerDriver.cpp index 97dbf00..5d619e1 100644 --- a/llvm/lib/Fuzzer/FuzzerDriver.cpp +++ b/llvm/lib/Fuzzer/FuzzerDriver.cpp @@ -15,6 +15,7 @@ #include "FuzzerIO.h" #include "FuzzerMutate.h" #include "FuzzerRandom.h" +#include "FuzzerShmem.h" #include "FuzzerTracePC.h" #include #include @@ -474,6 +475,31 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { if (Flags.minimize_crash_internal_step) return MinimizeCrashInputInternalStep(F, Corpus); + if (auto Name = Flags.run_equivalence_server) { + SMR.Destroy(Name); + if (!SMR.Create(Name, 1 << 12)) { + Printf("ERROR: can't create shared memory region\n"); + return 1; + } + Printf("INFO: EQUIVALENCE SERVER UP\n"); + while (true) { + SMR.WaitClient(); + size_t Size = SMR.ReadByteArraySize(); + SMR.WriteByteArray(nullptr, 0); + F->RunOne(SMR.GetByteArray(), Size); + SMR.PostServer(); + } + return 0; + } + + if (auto Name = Flags.use_equivalence_server) { + if (!SMR.Open(Name)) { + Printf("ERROR: can't open shared memory region\n"); + return 1; + } + Printf("INFO: EQUIVALENCE CLIENT UP\n"); + } + if (DoPlainRun) { Options.SaveArtifacts = false; int Runs = std::max(1, Flags.runs); diff --git a/llvm/lib/Fuzzer/FuzzerFlags.def b/llvm/lib/Fuzzer/FuzzerFlags.def index 8014050..7b8d38b 100644 --- a/llvm/lib/Fuzzer/FuzzerFlags.def +++ b/llvm/lib/Fuzzer/FuzzerFlags.def @@ -106,6 +106,9 @@ FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum" " was added to the corpus. " "Used primarily for testing libFuzzer itself.") +FUZZER_FLAG_STRING(run_equivalence_server, "Experimental") +FUZZER_FLAG_STRING(use_equivalence_server, "Experimental") + FUZZER_DEPRECATED_FLAG(exit_on_first) FUZZER_DEPRECATED_FLAG(save_minimized_corpus) FUZZER_DEPRECATED_FLAG(sync_command) diff --git a/llvm/lib/Fuzzer/FuzzerInternal.h b/llvm/lib/Fuzzer/FuzzerInternal.h index 0d2c7a7..543a5cf 100644 --- a/llvm/lib/Fuzzer/FuzzerInternal.h +++ b/llvm/lib/Fuzzer/FuzzerInternal.h @@ -109,6 +109,7 @@ public: bool DuringInitialCorpusExecution); void HandleMalloc(size_t Size); + void AnnounceOutput(const uint8_t *Data, size_t Size); private: void AlarmCallback(); diff --git a/llvm/lib/Fuzzer/FuzzerLoop.cpp b/llvm/lib/Fuzzer/FuzzerLoop.cpp index f9822ce..63dd7315 100644 --- a/llvm/lib/Fuzzer/FuzzerLoop.cpp +++ b/llvm/lib/Fuzzer/FuzzerLoop.cpp @@ -14,6 +14,7 @@ #include "FuzzerIO.h" #include "FuzzerMutate.h" #include "FuzzerRandom.h" +#include "FuzzerShmem.h" #include "FuzzerTracePC.h" #include #include @@ -42,6 +43,8 @@ static const size_t kMaxUnitSizeToPrint = 256; thread_local bool Fuzzer::IsMyThread; +SharedMemoryRegion SMR; + static void MissingExternalApiFunction(const char *FnName) { Printf("ERROR: %s is not defined. Exiting.\n" "Did you use -fsanitize-coverage=... to build your code?\n", @@ -531,6 +534,8 @@ size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { assert(InFuzzingThread()); + if (SMR.IsClient()) + SMR.WriteByteArray(Data, Size); // We copy the contents of Unit into a separate heap buffer // so that we reliably find buffer overflows in it. uint8_t *DataCopy = new uint8_t[Size]; @@ -806,6 +811,29 @@ void Fuzzer::MinimizeCrashLoop(const Unit &U) { } } +void Fuzzer::AnnounceOutput(const uint8_t *Data, size_t Size) { + if (SMR.IsServer()) { + SMR.WriteByteArray(Data, Size); + } else if (SMR.IsClient()) { + SMR.PostClient(); + SMR.WaitServer(); + size_t OtherSize = SMR.ReadByteArraySize(); + uint8_t *OtherData = SMR.GetByteArray(); + if (Size != OtherSize || memcmp(Data, OtherData, Size) != 0) { + size_t i = 0; + for (i = 0; i < Min(Size, OtherSize); i++) + if (Data[i] != OtherData[i]) + break; + Printf("==%lu== ERROR: libFuzzer: equivalence-mismatch. Sizes: %zd %zd; " + "offset %zd\n", GetPid(), Size, OtherSize, i); + DumpCurrentUnit("mismatch-"); + Printf("SUMMARY: libFuzzer: equivalence-mismatch\n"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); + } + } +} + } // namespace fuzzer extern "C" { @@ -816,5 +844,8 @@ size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { } // Experimental -void LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size) {} +void LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size) { + assert(fuzzer::F); + fuzzer::F->AnnounceOutput(Data, Size); +} } // extern "C" diff --git a/llvm/lib/Fuzzer/FuzzerShmem.h b/llvm/lib/Fuzzer/FuzzerShmem.h new file mode 100644 index 0000000..88719c1 --- /dev/null +++ b/llvm/lib/Fuzzer/FuzzerShmem.h @@ -0,0 +1,69 @@ +//===- FuzzerShmem.h - shared memory interface ------------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// SharedMemoryRegion +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_SHMEM_H +#define LLVM_FUZZER_SHMEM_H + +#include +#include +#include + +#include "FuzzerDefs.h" + +namespace fuzzer { + +class SharedMemoryRegion { + public: + bool Create(const char *Name, size_t Size); + bool Open(const char *Name); + bool Destroy(const char *Name); + size_t GetSize() const { return Size; } + uint8_t *GetData() { return Data; } + void PostServer() {Post(0);} + void WaitServer() {Wait(0);} + void PostClient() {Post(1);} + void WaitClient() {Wait(1);} + + size_t WriteByteArray(const uint8_t *Bytes, size_t N) { + N = std::min(N, GetSize() - sizeof(N)); + memcpy(GetData(), &N, sizeof(N)); + memcpy(GetData() + sizeof(N), Bytes, N); + assert(N == ReadByteArraySize()); + return N; + } + size_t ReadByteArraySize() { + size_t Res; + memcpy(&Res, GetData(), sizeof(Res)); + return Res; + } + uint8_t *GetByteArray() { return GetData() + sizeof(size_t); } + + bool IsServer() const { return Data && IAmServer; } + bool IsClient() const { return Data && !IAmServer; } + +private: + bool IAmServer; + std::string Path(const char *Name); + std::string SemName(const char *Name, int Idx); + void Post(int Idx); + void Wait(int Idx); + + bool Map(int fd); + size_t Size = 0; + uint8_t *Data = nullptr; + void *Semaphore[2]; +}; + +extern SharedMemoryRegion SMR; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_SHMEM_H diff --git a/llvm/lib/Fuzzer/FuzzerShmemPosix.cpp b/llvm/lib/Fuzzer/FuzzerShmemPosix.cpp new file mode 100644 index 0000000..cc06f33 --- /dev/null +++ b/llvm/lib/Fuzzer/FuzzerShmemPosix.cpp @@ -0,0 +1,97 @@ +//===- FuzzerShmemPosix.cpp - Posix shared memory ---------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// SharedMemoryRegion +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#ifdef LIBFUZZER_POSIX + +#include "FuzzerIO.h" +#include "FuzzerShmem.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +std::string SharedMemoryRegion::Path(const char *Name) { + return DirPlusFile(TmpDir(), Name); +} + +std::string SharedMemoryRegion::SemName(const char *Name, int Idx) { + std::string Res(Name); + return Res + (char)('0' + Idx); +} + +bool SharedMemoryRegion::Map(int fd) { + Data = (uint8_t *)mmap(0, Size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); + if (Data == (uint8_t*)-1) + return false; + return true; +} + +bool SharedMemoryRegion::Create(const char *Name, size_t Size) { + int fd = open(Path(Name).c_str(), O_CREAT | O_RDWR, 0777); + if (fd < 0) return false; + if (ftruncate(fd, Size) < 0) return false; + this->Size = Size; + if (!Map(fd)) + return false; + for (int i = 0; i < 2; i++) { + sem_unlink(SemName(Name, i).c_str()); + Semaphore[i] = sem_open(SemName(Name, i).c_str(), O_CREAT, 0644, 0); + if (Semaphore[i] == (void *)-1) + return false; + } + IAmServer = true; + return true; +} + +bool SharedMemoryRegion::Open(const char *Name) { + int fd = open(Path(Name).c_str(), O_RDWR); + if (fd < 0) return false; + struct stat stat_res; + if (0 != fstat(fd, &stat_res)) + return false; + Size = stat_res.st_size; + if (!Map(fd)) + return false; + for (int i = 0; i < 2; i++) { + Semaphore[i] = sem_open(SemName(Name, i).c_str(), 0); + if (Semaphore[i] == (void *)-1) + return false; + } + IAmServer = false; + return true; +} + +bool SharedMemoryRegion::Destroy(const char *Name) { + return 0 == unlink(Path(Name).c_str()); +} + +void SharedMemoryRegion::Post(int Idx) { + assert(Idx == 0 || Idx == 1); + sem_post((sem_t*)Semaphore[Idx]); +} + +void SharedMemoryRegion::Wait(int Idx) { + assert(Idx == 0 || Idx == 1); + if (sem_wait((sem_t*)Semaphore[Idx])) { + Printf("ERROR: sem_wait failed\n"); + exit(1); + } +} + +} // namespace fuzzer + +#endif // LIBFUZZER_POSIX diff --git a/llvm/lib/Fuzzer/test/EquivalenceATest.cpp b/llvm/lib/Fuzzer/test/EquivalenceATest.cpp index 101fe12..7d1ebb0 100644 --- a/llvm/lib/Fuzzer/test/EquivalenceATest.cpp +++ b/llvm/lib/Fuzzer/test/EquivalenceATest.cpp @@ -2,12 +2,14 @@ // License. See LICENSE.TXT for details. #include #include +#include // Test for libFuzzer's "equivalence" fuzzing, part A. extern "C" void LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size); extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size > 100) return 0; - uint8_t Result[100]; + // fprintf(stderr, "A %zd\n", Size); + uint8_t Result[50]; + if (Size > 50) Size = 50; for (size_t i = 0; i < Size; i++) Result[Size - i - 1] = Data[i]; LLVMFuzzerAnnounceOutput(Result, Size); diff --git a/llvm/lib/Fuzzer/test/EquivalenceBTest.cpp b/llvm/lib/Fuzzer/test/EquivalenceBTest.cpp index 175eed1..b1de208 100644 --- a/llvm/lib/Fuzzer/test/EquivalenceBTest.cpp +++ b/llvm/lib/Fuzzer/test/EquivalenceBTest.cpp @@ -7,18 +7,19 @@ // Test for libFuzzer's "equivalence" fuzzing, part B. extern "C" void LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size); extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size > 100) return 0; - uint8_t Result[100]; + // fprintf(stderr, "B %zd\n", Size); + uint8_t Result[50]; + if (Size > 50) Size = 50; for (size_t i = 0; i < Size; i++) Result[Size - i - 1] = Data[i]; // Be a bit different from EquivalenceATest - if (Size > 42 && Data[10] == 'B') { + if (Size > 10 && Data[5] == 'B' && Data[6] == 'C' && Data[7] == 'D') { static int c; if (!c) fprintf(stderr, "ZZZZZZZ\n"); c = 1; - Result[42]++; + Result[2]++; } LLVMFuzzerAnnounceOutput(Result, Size); diff --git a/llvm/lib/Fuzzer/test/equivalence.test b/llvm/lib/Fuzzer/test/equivalence.test new file mode 100644 index 0000000..26d75a3 --- /dev/null +++ b/llvm/lib/Fuzzer/test/equivalence.test @@ -0,0 +1,5 @@ +RUN: LLVMFuzzer-EquivalenceATest -run_equivalence_server=EQUIV_TEST & export APID=$! +RUN: not LLVMFuzzer-EquivalenceBTest -use_equivalence_server=EQUIV_TEST 2>&1 | FileCheck %s +CHECK: ERROR: libFuzzer: equivalence-mismatch. Sizes: {{.*}}; offset 2 +CHECK: SUMMARY: libFuzzer: equivalence-mismatch +RUN: kill -9 $APID -- 2.7.4