Skip to content

Commit

Permalink
[libFuzzer] experimental support for 'equivalance fuzzing'
Browse files Browse the repository at this point in the history
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@292646 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
kcc committed Jan 20, 2017
1 parent 0d4e33d commit 7404114
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 7 deletions.
1 change: 1 addition & 0 deletions lib/Fuzzer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ if( LLVM_USE_SANITIZE_COVERAGE )
FuzzerMerge.cpp
FuzzerMutate.cpp
FuzzerSHA1.cpp
FuzzerShmemPosix.cpp
FuzzerTracePC.cpp
FuzzerTraceState.cpp
FuzzerUtil.cpp
Expand Down
26 changes: 26 additions & 0 deletions lib/Fuzzer/FuzzerDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "FuzzerIO.h"
#include "FuzzerMutate.h"
#include "FuzzerRandom.h"
#include "FuzzerShmem.h"
#include "FuzzerTracePC.h"
#include <algorithm>
#include <atomic>
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions lib/Fuzzer/FuzzerFlags.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions lib/Fuzzer/FuzzerInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class Fuzzer {
bool DuringInitialCorpusExecution);

void HandleMalloc(size_t Size);
void AnnounceOutput(const uint8_t *Data, size_t Size);

private:
void AlarmCallback();
Expand Down
33 changes: 32 additions & 1 deletion lib/Fuzzer/FuzzerLoop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "FuzzerIO.h"
#include "FuzzerMutate.h"
#include "FuzzerRandom.h"
#include "FuzzerShmem.h"
#include "FuzzerTracePC.h"
#include <algorithm>
#include <cstring>
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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" {
Expand All @@ -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"
69 changes: 69 additions & 0 deletions lib/Fuzzer/FuzzerShmem.h
Original file line number Diff line number Diff line change
@@ -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 <algorithm>
#include <cstring>
#include <string>

#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
97 changes: 97 additions & 0 deletions lib/Fuzzer/FuzzerShmemPosix.cpp
Original file line number Diff line number Diff line change
@@ -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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>

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
6 changes: 4 additions & 2 deletions lib/Fuzzer/test/EquivalenceATest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
// License. See LICENSE.TXT for details.
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>

// 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);
Expand Down
9 changes: 5 additions & 4 deletions lib/Fuzzer/test/EquivalenceBTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions lib/Fuzzer/test/equivalence.test
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 7404114

Please sign in to comment.