➕ added libFuzzer
This commit is contained in:
parent
7107072f71
commit
69ed19e483
156 changed files with 9535 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -20,3 +20,4 @@ cmake-build-debug
|
|||
|
||||
|
||||
test/test-*
|
||||
test/fuzz_test/Fuzzer/.svn
|
||||
|
|
45
test/fuzz_test/Fuzzer/CMakeLists.txt
Normal file
45
test/fuzz_test/Fuzzer/CMakeLists.txt
Normal file
|
@ -0,0 +1,45 @@
|
|||
set(LIBFUZZER_FLAGS_BASE "${CMAKE_CXX_FLAGS}")
|
||||
# Disable the coverage and sanitizer instrumentation for the fuzzer itself.
|
||||
set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fno-sanitize-coverage=trace-pc-guard,edge,trace-cmp,indirect-calls,8bit-counters -Werror")
|
||||
if( LLVM_USE_SANITIZE_COVERAGE )
|
||||
if(NOT "${LLVM_USE_SANITIZER}" STREQUAL "Address")
|
||||
message(FATAL_ERROR
|
||||
"LibFuzzer and its tests require LLVM_USE_SANITIZER=Address and "
|
||||
"LLVM_USE_SANITIZE_COVERAGE=YES to be set."
|
||||
)
|
||||
endif()
|
||||
add_library(LLVMFuzzerNoMainObjects OBJECT
|
||||
FuzzerCrossOver.cpp
|
||||
FuzzerDriver.cpp
|
||||
FuzzerExtFunctionsDlsym.cpp
|
||||
FuzzerExtFunctionsWeak.cpp
|
||||
FuzzerExtFunctionsWeakAlias.cpp
|
||||
FuzzerIO.cpp
|
||||
FuzzerIOPosix.cpp
|
||||
FuzzerIOWindows.cpp
|
||||
FuzzerLoop.cpp
|
||||
FuzzerMerge.cpp
|
||||
FuzzerMutate.cpp
|
||||
FuzzerSHA1.cpp
|
||||
FuzzerTracePC.cpp
|
||||
FuzzerTraceState.cpp
|
||||
FuzzerUtil.cpp
|
||||
FuzzerUtilDarwin.cpp
|
||||
FuzzerUtilLinux.cpp
|
||||
FuzzerUtilPosix.cpp
|
||||
FuzzerUtilWindows.cpp
|
||||
)
|
||||
add_library(LLVMFuzzerNoMain STATIC
|
||||
$<TARGET_OBJECTS:LLVMFuzzerNoMainObjects>
|
||||
)
|
||||
target_link_libraries(LLVMFuzzerNoMain ${PTHREAD_LIB})
|
||||
add_library(LLVMFuzzer STATIC
|
||||
FuzzerMain.cpp
|
||||
$<TARGET_OBJECTS:LLVMFuzzerNoMainObjects>
|
||||
)
|
||||
target_link_libraries(LLVMFuzzer ${PTHREAD_LIB})
|
||||
|
||||
if( LLVM_INCLUDE_TESTS )
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
endif()
|
217
test/fuzz_test/Fuzzer/FuzzerCorpus.h
Normal file
217
test/fuzz_test/Fuzzer/FuzzerCorpus.h
Normal file
|
@ -0,0 +1,217 @@
|
|||
//===- FuzzerCorpus.h - Internal header for the Fuzzer ----------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::InputCorpus
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_CORPUS
|
||||
#define LLVM_FUZZER_CORPUS
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "FuzzerSHA1.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
struct InputInfo {
|
||||
Unit U; // The actual input data.
|
||||
uint8_t Sha1[kSHA1NumBytes]; // Checksum.
|
||||
// Number of features that this input has and no smaller input has.
|
||||
size_t NumFeatures = 0;
|
||||
size_t Tmp = 0; // Used by ValidateFeatureSet.
|
||||
// Stats.
|
||||
size_t NumExecutedMutations = 0;
|
||||
size_t NumSuccessfullMutations = 0;
|
||||
bool MayDeleteFile = false;
|
||||
};
|
||||
|
||||
class InputCorpus {
|
||||
public:
|
||||
static const size_t kFeatureSetSize = 1 << 16;
|
||||
InputCorpus(const std::string &OutputCorpus) : OutputCorpus(OutputCorpus) {
|
||||
memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
|
||||
memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
|
||||
}
|
||||
~InputCorpus() {
|
||||
for (auto II : Inputs)
|
||||
delete II;
|
||||
}
|
||||
size_t size() const { return Inputs.size(); }
|
||||
size_t SizeInBytes() const {
|
||||
size_t Res = 0;
|
||||
for (auto II : Inputs)
|
||||
Res += II->U.size();
|
||||
return Res;
|
||||
}
|
||||
size_t NumActiveUnits() const {
|
||||
size_t Res = 0;
|
||||
for (auto II : Inputs)
|
||||
Res += !II->U.empty();
|
||||
return Res;
|
||||
}
|
||||
bool empty() const { return Inputs.empty(); }
|
||||
const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; }
|
||||
void AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile = false) {
|
||||
assert(!U.empty());
|
||||
uint8_t Hash[kSHA1NumBytes];
|
||||
if (FeatureDebug)
|
||||
Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures);
|
||||
ComputeSHA1(U.data(), U.size(), Hash);
|
||||
Hashes.insert(Sha1ToString(Hash));
|
||||
Inputs.push_back(new InputInfo());
|
||||
InputInfo &II = *Inputs.back();
|
||||
II.U = U;
|
||||
II.NumFeatures = NumFeatures;
|
||||
II.MayDeleteFile = MayDeleteFile;
|
||||
memcpy(II.Sha1, Hash, kSHA1NumBytes);
|
||||
UpdateCorpusDistribution();
|
||||
ValidateFeatureSet();
|
||||
}
|
||||
|
||||
bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); }
|
||||
bool HasUnit(const std::string &H) { return Hashes.count(H); }
|
||||
InputInfo &ChooseUnitToMutate(Random &Rand) {
|
||||
InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)];
|
||||
assert(!II.U.empty());
|
||||
return II;
|
||||
};
|
||||
|
||||
// Returns an index of random unit from the corpus to mutate.
|
||||
// Hypothesis: units added to the corpus last are more likely to be
|
||||
// interesting. This function gives more weight to the more recent units.
|
||||
size_t ChooseUnitIdxToMutate(Random &Rand) {
|
||||
size_t Idx = static_cast<size_t>(CorpusDistribution(Rand.Get_mt19937()));
|
||||
assert(Idx < Inputs.size());
|
||||
return Idx;
|
||||
}
|
||||
|
||||
void PrintStats() {
|
||||
for (size_t i = 0; i < Inputs.size(); i++) {
|
||||
const auto &II = *Inputs[i];
|
||||
Printf(" [%zd %s]\tsz: %zd\truns: %zd\tsucc: %zd\n", i,
|
||||
Sha1ToString(II.Sha1).c_str(), II.U.size(),
|
||||
II.NumExecutedMutations, II.NumSuccessfullMutations);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintFeatureSet() {
|
||||
for (size_t i = 0; i < kFeatureSetSize; i++) {
|
||||
if(size_t Sz = GetFeature(i))
|
||||
Printf("[%zd: id %zd sz%zd] ", i, SmallestElementPerFeature[i], Sz);
|
||||
}
|
||||
Printf("\n\t");
|
||||
for (size_t i = 0; i < Inputs.size(); i++)
|
||||
if (size_t N = Inputs[i]->NumFeatures)
|
||||
Printf(" %zd=>%zd ", i, N);
|
||||
Printf("\n");
|
||||
}
|
||||
|
||||
void DeleteInput(size_t Idx) {
|
||||
InputInfo &II = *Inputs[Idx];
|
||||
if (!OutputCorpus.empty() && II.MayDeleteFile)
|
||||
RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1)));
|
||||
Unit().swap(II.U);
|
||||
if (FeatureDebug)
|
||||
Printf("EVICTED %zd\n", Idx);
|
||||
}
|
||||
|
||||
bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) {
|
||||
assert(NewSize);
|
||||
Idx = Idx % kFeatureSetSize;
|
||||
uint32_t OldSize = GetFeature(Idx);
|
||||
if (OldSize == 0 || (Shrink && OldSize > NewSize)) {
|
||||
if (OldSize > 0) {
|
||||
size_t OldIdx = SmallestElementPerFeature[Idx];
|
||||
InputInfo &II = *Inputs[OldIdx];
|
||||
assert(II.NumFeatures > 0);
|
||||
II.NumFeatures--;
|
||||
if (II.NumFeatures == 0)
|
||||
DeleteInput(OldIdx);
|
||||
}
|
||||
if (FeatureDebug)
|
||||
Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize);
|
||||
SmallestElementPerFeature[Idx] = Inputs.size();
|
||||
InputSizesPerFeature[Idx] = NewSize;
|
||||
CountingFeatures = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t NumFeatures() const {
|
||||
size_t Res = 0;
|
||||
for (size_t i = 0; i < kFeatureSetSize; i++)
|
||||
Res += GetFeature(i) != 0;
|
||||
return Res;
|
||||
}
|
||||
|
||||
void ResetFeatureSet() {
|
||||
assert(Inputs.empty());
|
||||
memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
|
||||
memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static const bool FeatureDebug = false;
|
||||
|
||||
size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; }
|
||||
|
||||
void ValidateFeatureSet() {
|
||||
if (!CountingFeatures) return;
|
||||
if (FeatureDebug)
|
||||
PrintFeatureSet();
|
||||
for (size_t Idx = 0; Idx < kFeatureSetSize; Idx++)
|
||||
if (GetFeature(Idx))
|
||||
Inputs[SmallestElementPerFeature[Idx]]->Tmp++;
|
||||
for (auto II: Inputs) {
|
||||
if (II->Tmp != II->NumFeatures)
|
||||
Printf("ZZZ %zd %zd\n", II->Tmp, II->NumFeatures);
|
||||
assert(II->Tmp == II->NumFeatures);
|
||||
II->Tmp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the probability distribution for the units in the corpus.
|
||||
// Must be called whenever the corpus or unit weights are changed.
|
||||
void UpdateCorpusDistribution() {
|
||||
size_t N = Inputs.size();
|
||||
Intervals.resize(N + 1);
|
||||
Weights.resize(N);
|
||||
std::iota(Intervals.begin(), Intervals.end(), 0);
|
||||
if (CountingFeatures)
|
||||
for (size_t i = 0; i < N; i++)
|
||||
Weights[i] = Inputs[i]->NumFeatures * (i + 1);
|
||||
else
|
||||
std::iota(Weights.begin(), Weights.end(), 1);
|
||||
CorpusDistribution = std::piecewise_constant_distribution<double>(
|
||||
Intervals.begin(), Intervals.end(), Weights.begin());
|
||||
}
|
||||
std::piecewise_constant_distribution<double> CorpusDistribution;
|
||||
|
||||
std::vector<double> Intervals;
|
||||
std::vector<double> Weights;
|
||||
|
||||
std::unordered_set<std::string> Hashes;
|
||||
std::vector<InputInfo*> Inputs;
|
||||
|
||||
bool CountingFeatures = false;
|
||||
uint32_t InputSizesPerFeature[kFeatureSetSize];
|
||||
uint32_t SmallestElementPerFeature[kFeatureSetSize];
|
||||
|
||||
std::string OutputCorpus;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_CORPUS
|
52
test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp
Normal file
52
test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
//===- FuzzerCrossOver.cpp - Cross over two test inputs -------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Cross over test inputs.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out.
|
||||
size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1,
|
||||
const uint8_t *Data2, size_t Size2,
|
||||
uint8_t *Out, size_t MaxOutSize) {
|
||||
assert(Size1 || Size2);
|
||||
MaxOutSize = Rand(MaxOutSize) + 1;
|
||||
size_t OutPos = 0;
|
||||
size_t Pos1 = 0;
|
||||
size_t Pos2 = 0;
|
||||
size_t *InPos = &Pos1;
|
||||
size_t InSize = Size1;
|
||||
const uint8_t *Data = Data1;
|
||||
bool CurrentlyUsingFirstData = true;
|
||||
while (OutPos < MaxOutSize && (Pos1 < Size1 || Pos2 < Size2)) {
|
||||
// Merge a part of Data into Out.
|
||||
size_t OutSizeLeft = MaxOutSize - OutPos;
|
||||
if (*InPos < InSize) {
|
||||
size_t InSizeLeft = InSize - *InPos;
|
||||
size_t MaxExtraSize = std::min(OutSizeLeft, InSizeLeft);
|
||||
size_t ExtraSize = Rand(MaxExtraSize) + 1;
|
||||
memcpy(Out + OutPos, Data + *InPos, ExtraSize);
|
||||
OutPos += ExtraSize;
|
||||
(*InPos) += ExtraSize;
|
||||
}
|
||||
// Use the other input data on the next iteration.
|
||||
InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1;
|
||||
InSize = CurrentlyUsingFirstData ? Size2 : Size1;
|
||||
Data = CurrentlyUsingFirstData ? Data2 : Data1;
|
||||
CurrentlyUsingFirstData = !CurrentlyUsingFirstData;
|
||||
}
|
||||
return OutPos;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
89
test/fuzz_test/Fuzzer/FuzzerDefs.h
Normal file
89
test/fuzz_test/Fuzzer/FuzzerDefs.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
//===- FuzzerDefs.h - Internal header for the Fuzzer ------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Basic definitions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_DEFS_H
|
||||
#define LLVM_FUZZER_DEFS_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Platform detection.
|
||||
#ifdef __linux__
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_LINUX 1
|
||||
#define LIBFUZZER_WINDOWS 0
|
||||
#elif __APPLE__
|
||||
#define LIBFUZZER_APPLE 1
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_WINDOWS 0
|
||||
#elif _WIN32
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_WINDOWS 1
|
||||
#else
|
||||
#error "Support for your platform has not been implemented"
|
||||
#endif
|
||||
|
||||
#define LIBFUZZER_POSIX LIBFUZZER_APPLE || LIBFUZZER_LINUX
|
||||
|
||||
#ifdef __x86_64
|
||||
#define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt")))
|
||||
#else
|
||||
#define ATTRIBUTE_TARGET_POPCNT
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __clang__ // avoid gcc warning.
|
||||
# define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory")))
|
||||
#else
|
||||
# define ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
#endif
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
template <class T> T Min(T a, T b) { return a < b ? a : b; }
|
||||
template <class T> T Max(T a, T b) { return a > b ? a : b; }
|
||||
|
||||
class Random;
|
||||
class Dictionary;
|
||||
class DictionaryEntry;
|
||||
class MutationDispatcher;
|
||||
struct FuzzingOptions;
|
||||
class InputCorpus;
|
||||
struct InputInfo;
|
||||
struct ExternalFunctions;
|
||||
|
||||
// Global interface to functions that may or may not be available.
|
||||
extern ExternalFunctions *EF;
|
||||
|
||||
typedef std::vector<uint8_t> Unit;
|
||||
typedef std::vector<Unit> UnitVector;
|
||||
typedef int (*UserCallback)(const uint8_t *Data, size_t Size);
|
||||
|
||||
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback);
|
||||
|
||||
struct ScopedDoingMyOwnMemmem {
|
||||
ScopedDoingMyOwnMemmem();
|
||||
~ScopedDoingMyOwnMemmem();
|
||||
};
|
||||
|
||||
inline uint8_t Bswap(uint8_t x) { return x; }
|
||||
inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); }
|
||||
inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); }
|
||||
inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); }
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_DEFS_H
|
124
test/fuzz_test/Fuzzer/FuzzerDictionary.h
Normal file
124
test/fuzz_test/Fuzzer/FuzzerDictionary.h
Normal file
|
@ -0,0 +1,124 @@
|
|||
//===- FuzzerDictionary.h - Internal header for the Fuzzer ------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::Dictionary
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_DICTIONARY_H
|
||||
#define LLVM_FUZZER_DICTIONARY_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerUtil.h"
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace fuzzer {
|
||||
// A simple POD sized array of bytes.
|
||||
template <size_t kMaxSize> class FixedWord {
|
||||
public:
|
||||
FixedWord() {}
|
||||
FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); }
|
||||
|
||||
void Set(const uint8_t *B, uint8_t S) {
|
||||
assert(S <= kMaxSize);
|
||||
memcpy(Data, B, S);
|
||||
Size = S;
|
||||
}
|
||||
|
||||
bool operator==(const FixedWord<kMaxSize> &w) const {
|
||||
return Size == w.Size && 0 == memcmp(Data, w.Data, Size);
|
||||
}
|
||||
|
||||
bool operator<(const FixedWord<kMaxSize> &w) const {
|
||||
if (Size != w.Size)
|
||||
return Size < w.Size;
|
||||
return memcmp(Data, w.Data, Size) < 0;
|
||||
}
|
||||
|
||||
static size_t GetMaxSize() { return kMaxSize; }
|
||||
const uint8_t *data() const { return Data; }
|
||||
uint8_t size() const { return Size; }
|
||||
|
||||
private:
|
||||
uint8_t Size = 0;
|
||||
uint8_t Data[kMaxSize];
|
||||
};
|
||||
|
||||
typedef FixedWord<27> Word; // 28 bytes.
|
||||
|
||||
class DictionaryEntry {
|
||||
public:
|
||||
DictionaryEntry() {}
|
||||
DictionaryEntry(Word W) : W(W) {}
|
||||
DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {}
|
||||
const Word &GetW() const { return W; }
|
||||
|
||||
bool HasPositionHint() const { return PositionHint != std::numeric_limits<size_t>::max(); }
|
||||
size_t GetPositionHint() const {
|
||||
assert(HasPositionHint());
|
||||
return PositionHint;
|
||||
}
|
||||
void IncUseCount() { UseCount++; }
|
||||
void IncSuccessCount() { SuccessCount++; }
|
||||
size_t GetUseCount() const { return UseCount; }
|
||||
size_t GetSuccessCount() const {return SuccessCount; }
|
||||
|
||||
void Print(const char *PrintAfter = "\n") {
|
||||
PrintASCII(W.data(), W.size());
|
||||
if (HasPositionHint())
|
||||
Printf("@%zd", GetPositionHint());
|
||||
Printf("%s", PrintAfter);
|
||||
}
|
||||
|
||||
private:
|
||||
Word W;
|
||||
size_t PositionHint = std::numeric_limits<size_t>::max();
|
||||
size_t UseCount = 0;
|
||||
size_t SuccessCount = 0;
|
||||
};
|
||||
|
||||
class Dictionary {
|
||||
public:
|
||||
static const size_t kMaxDictSize = 1 << 14;
|
||||
|
||||
bool ContainsWord(const Word &W) const {
|
||||
return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) {
|
||||
return DE.GetW() == W;
|
||||
});
|
||||
}
|
||||
const DictionaryEntry *begin() const { return &DE[0]; }
|
||||
const DictionaryEntry *end() const { return begin() + Size; }
|
||||
DictionaryEntry & operator[] (size_t Idx) {
|
||||
assert(Idx < Size);
|
||||
return DE[Idx];
|
||||
}
|
||||
void push_back(DictionaryEntry DE) {
|
||||
if (Size < kMaxDictSize)
|
||||
this->DE[Size++] = DE;
|
||||
}
|
||||
void clear() { Size = 0; }
|
||||
bool empty() const { return Size == 0; }
|
||||
size_t size() const { return Size; }
|
||||
|
||||
private:
|
||||
DictionaryEntry DE[kMaxDictSize];
|
||||
size_t Size = 0;
|
||||
};
|
||||
|
||||
// Parses one dictionary entry.
|
||||
// If successfull, write the enty to Unit and returns true,
|
||||
// otherwise returns false.
|
||||
bool ParseOneDictionaryEntry(const std::string &Str, Unit *U);
|
||||
// Parses the dictionary file, fills Units, returns true iff all lines
|
||||
// were parsed succesfully.
|
||||
bool ParseDictionaryFile(const std::string &Text, std::vector<Unit> *Units);
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_DICTIONARY_H
|
545
test/fuzz_test/Fuzzer/FuzzerDriver.cpp
Normal file
545
test/fuzz_test/Fuzzer/FuzzerDriver.cpp
Normal file
|
@ -0,0 +1,545 @@
|
|||
//===- FuzzerDriver.cpp - FuzzerDriver function and flags -----------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// FuzzerDriver and flag parsing.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerInterface.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// This function should be present in the libFuzzer so that the client
|
||||
// binary can test for its existence.
|
||||
extern "C" __attribute__((used)) void __libfuzzer_is_present() {}
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// Program arguments.
|
||||
struct FlagDescription {
|
||||
const char *Name;
|
||||
const char *Description;
|
||||
int Default;
|
||||
int *IntFlag;
|
||||
const char **StrFlag;
|
||||
unsigned int *UIntFlag;
|
||||
};
|
||||
|
||||
struct {
|
||||
#define FUZZER_DEPRECATED_FLAG(Name)
|
||||
#define FUZZER_FLAG_INT(Name, Default, Description) int Name;
|
||||
#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) unsigned int Name;
|
||||
#define FUZZER_FLAG_STRING(Name, Description) const char *Name;
|
||||
#include "FuzzerFlags.def"
|
||||
#undef FUZZER_DEPRECATED_FLAG
|
||||
#undef FUZZER_FLAG_INT
|
||||
#undef FUZZER_FLAG_UNSIGNED
|
||||
#undef FUZZER_FLAG_STRING
|
||||
} Flags;
|
||||
|
||||
static const FlagDescription FlagDescriptions [] {
|
||||
#define FUZZER_DEPRECATED_FLAG(Name) \
|
||||
{#Name, "Deprecated; don't use", 0, nullptr, nullptr, nullptr},
|
||||
#define FUZZER_FLAG_INT(Name, Default, Description) \
|
||||
{#Name, Description, Default, &Flags.Name, nullptr, nullptr},
|
||||
#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) \
|
||||
{#Name, Description, static_cast<int>(Default), \
|
||||
nullptr, nullptr, &Flags.Name},
|
||||
#define FUZZER_FLAG_STRING(Name, Description) \
|
||||
{#Name, Description, 0, nullptr, &Flags.Name, nullptr},
|
||||
#include "FuzzerFlags.def"
|
||||
#undef FUZZER_DEPRECATED_FLAG
|
||||
#undef FUZZER_FLAG_INT
|
||||
#undef FUZZER_FLAG_UNSIGNED
|
||||
#undef FUZZER_FLAG_STRING
|
||||
};
|
||||
|
||||
static const size_t kNumFlags =
|
||||
sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]);
|
||||
|
||||
static std::vector<std::string> *Inputs;
|
||||
static std::string *ProgName;
|
||||
|
||||
static void PrintHelp() {
|
||||
Printf("Usage:\n");
|
||||
auto Prog = ProgName->c_str();
|
||||
Printf("\nTo run fuzzing pass 0 or more directories.\n");
|
||||
Printf("%s [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n", Prog);
|
||||
|
||||
Printf("\nTo run individual tests without fuzzing pass 1 or more files:\n");
|
||||
Printf("%s [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n", Prog);
|
||||
|
||||
Printf("\nFlags: (strictly in form -flag=value)\n");
|
||||
size_t MaxFlagLen = 0;
|
||||
for (size_t F = 0; F < kNumFlags; F++)
|
||||
MaxFlagLen = std::max(strlen(FlagDescriptions[F].Name), MaxFlagLen);
|
||||
|
||||
for (size_t F = 0; F < kNumFlags; F++) {
|
||||
const auto &D = FlagDescriptions[F];
|
||||
if (strstr(D.Description, "internal flag") == D.Description) continue;
|
||||
Printf(" %s", D.Name);
|
||||
for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++)
|
||||
Printf(" ");
|
||||
Printf("\t");
|
||||
Printf("%d\t%s\n", D.Default, D.Description);
|
||||
}
|
||||
Printf("\nFlags starting with '--' will be ignored and "
|
||||
"will be passed verbatim to subprocesses.\n");
|
||||
}
|
||||
|
||||
static const char *FlagValue(const char *Param, const char *Name) {
|
||||
size_t Len = strlen(Name);
|
||||
if (Param[0] == '-' && strstr(Param + 1, Name) == Param + 1 &&
|
||||
Param[Len + 1] == '=')
|
||||
return &Param[Len + 2];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Avoid calling stol as it triggers a bug in clang/glibc build.
|
||||
static long MyStol(const char *Str) {
|
||||
long Res = 0;
|
||||
long Sign = 1;
|
||||
if (*Str == '-') {
|
||||
Str++;
|
||||
Sign = -1;
|
||||
}
|
||||
for (size_t i = 0; Str[i]; i++) {
|
||||
char Ch = Str[i];
|
||||
if (Ch < '0' || Ch > '9')
|
||||
return Res;
|
||||
Res = Res * 10 + (Ch - '0');
|
||||
}
|
||||
return Res * Sign;
|
||||
}
|
||||
|
||||
static bool ParseOneFlag(const char *Param) {
|
||||
if (Param[0] != '-') return false;
|
||||
if (Param[1] == '-') {
|
||||
static bool PrintedWarning = false;
|
||||
if (!PrintedWarning) {
|
||||
PrintedWarning = true;
|
||||
Printf("INFO: libFuzzer ignores flags that start with '--'\n");
|
||||
}
|
||||
for (size_t F = 0; F < kNumFlags; F++)
|
||||
if (FlagValue(Param + 1, FlagDescriptions[F].Name))
|
||||
Printf("WARNING: did you mean '%s' (single dash)?\n", Param + 1);
|
||||
return true;
|
||||
}
|
||||
for (size_t F = 0; F < kNumFlags; F++) {
|
||||
const char *Name = FlagDescriptions[F].Name;
|
||||
const char *Str = FlagValue(Param, Name);
|
||||
if (Str) {
|
||||
if (FlagDescriptions[F].IntFlag) {
|
||||
int Val = MyStol(Str);
|
||||
*FlagDescriptions[F].IntFlag = Val;
|
||||
if (Flags.verbosity >= 2)
|
||||
Printf("Flag: %s %d\n", Name, Val);;
|
||||
return true;
|
||||
} else if (FlagDescriptions[F].UIntFlag) {
|
||||
unsigned int Val = std::stoul(Str);
|
||||
*FlagDescriptions[F].UIntFlag = Val;
|
||||
if (Flags.verbosity >= 2)
|
||||
Printf("Flag: %s %u\n", Name, Val);
|
||||
return true;
|
||||
} else if (FlagDescriptions[F].StrFlag) {
|
||||
*FlagDescriptions[F].StrFlag = Str;
|
||||
if (Flags.verbosity >= 2)
|
||||
Printf("Flag: %s %s\n", Name, Str);
|
||||
return true;
|
||||
} else { // Deprecated flag.
|
||||
Printf("Flag: %s: deprecated, don't use\n", Name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Printf("\n\nWARNING: unrecognized flag '%s'; "
|
||||
"use -help=1 to list all flags\n\n", Param);
|
||||
return true;
|
||||
}
|
||||
|
||||
// We don't use any library to minimize dependencies.
|
||||
static void ParseFlags(const std::vector<std::string> &Args) {
|
||||
for (size_t F = 0; F < kNumFlags; F++) {
|
||||
if (FlagDescriptions[F].IntFlag)
|
||||
*FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default;
|
||||
if (FlagDescriptions[F].UIntFlag)
|
||||
*FlagDescriptions[F].UIntFlag =
|
||||
static_cast<unsigned int>(FlagDescriptions[F].Default);
|
||||
if (FlagDescriptions[F].StrFlag)
|
||||
*FlagDescriptions[F].StrFlag = nullptr;
|
||||
}
|
||||
Inputs = new std::vector<std::string>;
|
||||
for (size_t A = 1; A < Args.size(); A++) {
|
||||
if (ParseOneFlag(Args[A].c_str())) continue;
|
||||
Inputs->push_back(Args[A]);
|
||||
}
|
||||
}
|
||||
|
||||
static std::mutex Mu;
|
||||
|
||||
static void PulseThread() {
|
||||
while (true) {
|
||||
SleepSeconds(600);
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
Printf("pulse...\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void WorkerThread(const std::string &Cmd, std::atomic<unsigned> *Counter,
|
||||
unsigned NumJobs, std::atomic<bool> *HasErrors) {
|
||||
while (true) {
|
||||
unsigned C = (*Counter)++;
|
||||
if (C >= NumJobs) break;
|
||||
std::string Log = "fuzz-" + std::to_string(C) + ".log";
|
||||
std::string ToRun = Cmd + " > " + Log + " 2>&1\n";
|
||||
if (Flags.verbosity)
|
||||
Printf("%s", ToRun.c_str());
|
||||
int ExitCode = ExecuteCommand(ToRun);
|
||||
if (ExitCode != 0)
|
||||
*HasErrors = true;
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
Printf("================== Job %u exited with exit code %d ============\n",
|
||||
C, ExitCode);
|
||||
fuzzer::CopyFileToErr(Log);
|
||||
}
|
||||
}
|
||||
|
||||
std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
|
||||
const char *X1, const char *X2) {
|
||||
std::string Cmd;
|
||||
for (auto &S : Args) {
|
||||
if (FlagValue(S.c_str(), X1) || FlagValue(S.c_str(), X2))
|
||||
continue;
|
||||
Cmd += S + " ";
|
||||
}
|
||||
return Cmd;
|
||||
}
|
||||
|
||||
static int RunInMultipleProcesses(const std::vector<std::string> &Args,
|
||||
unsigned NumWorkers, unsigned NumJobs) {
|
||||
std::atomic<unsigned> Counter(0);
|
||||
std::atomic<bool> HasErrors(false);
|
||||
std::string Cmd = CloneArgsWithoutX(Args, "jobs", "workers");
|
||||
std::vector<std::thread> V;
|
||||
std::thread Pulse(PulseThread);
|
||||
Pulse.detach();
|
||||
for (unsigned i = 0; i < NumWorkers; i++)
|
||||
V.push_back(std::thread(WorkerThread, Cmd, &Counter, NumJobs, &HasErrors));
|
||||
for (auto &T : V)
|
||||
T.join();
|
||||
return HasErrors ? 1 : 0;
|
||||
}
|
||||
|
||||
static void RssThread(Fuzzer *F, size_t RssLimitMb) {
|
||||
while (true) {
|
||||
SleepSeconds(1);
|
||||
size_t Peak = GetPeakRSSMb();
|
||||
if (Peak > RssLimitMb)
|
||||
F->RssLimitCallback();
|
||||
}
|
||||
}
|
||||
|
||||
static void StartRssThread(Fuzzer *F, size_t RssLimitMb) {
|
||||
if (!RssLimitMb) return;
|
||||
std::thread T(RssThread, F, RssLimitMb);
|
||||
T.detach();
|
||||
}
|
||||
|
||||
int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) {
|
||||
Unit U = FileToVector(InputFilePath);
|
||||
if (MaxLen && MaxLen < U.size())
|
||||
U.resize(MaxLen);
|
||||
F->RunOne(U.data(), U.size());
|
||||
F->TryDetectingAMemoryLeak(U.data(), U.size(), true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool AllInputsAreFiles() {
|
||||
if (Inputs->empty()) return false;
|
||||
for (auto &Path : *Inputs)
|
||||
if (!IsFile(Path))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int MinimizeCrashInput(const std::vector<std::string> &Args) {
|
||||
if (Inputs->size() != 1) {
|
||||
Printf("ERROR: -minimize_crash should be given one input file\n");
|
||||
exit(1);
|
||||
}
|
||||
std::string InputFilePath = Inputs->at(0);
|
||||
std::string BaseCmd =
|
||||
CloneArgsWithoutX(Args, "minimize_crash", "exact_artifact_path");
|
||||
auto InputPos = BaseCmd.find(" " + InputFilePath + " ");
|
||||
assert(InputPos != std::string::npos);
|
||||
BaseCmd.erase(InputPos, InputFilePath.size() + 1);
|
||||
if (Flags.runs <= 0 && Flags.max_total_time == 0) {
|
||||
Printf("INFO: you need to specify -runs=N or "
|
||||
"-max_total_time=N with -minimize_crash=1\n"
|
||||
"INFO: defaulting to -max_total_time=600\n");
|
||||
BaseCmd += " -max_total_time=600";
|
||||
}
|
||||
// BaseCmd += " > /dev/null 2>&1 ";
|
||||
|
||||
std::string CurrentFilePath = InputFilePath;
|
||||
while (true) {
|
||||
Unit U = FileToVector(CurrentFilePath);
|
||||
if (U.size() < 2) {
|
||||
Printf("CRASH_MIN: '%s' is small enough\n", CurrentFilePath.c_str());
|
||||
return 0;
|
||||
}
|
||||
Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n",
|
||||
CurrentFilePath.c_str(), U.size());
|
||||
|
||||
auto Cmd = BaseCmd + " " + CurrentFilePath;
|
||||
|
||||
Printf("CRASH_MIN: executing: %s\n", Cmd.c_str());
|
||||
int ExitCode = ExecuteCommand(Cmd);
|
||||
if (ExitCode == 0) {
|
||||
Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str());
|
||||
exit(1);
|
||||
}
|
||||
Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize "
|
||||
"it further\n",
|
||||
CurrentFilePath.c_str(), U.size());
|
||||
|
||||
std::string ArtifactPath = "minimized-from-" + Hash(U);
|
||||
Cmd += " -minimize_crash_internal_step=1 -exact_artifact_path=" +
|
||||
ArtifactPath;
|
||||
Printf("CRASH_MIN: executing: %s\n", Cmd.c_str());
|
||||
ExitCode = ExecuteCommand(Cmd);
|
||||
if (ExitCode == 0) {
|
||||
if (Flags.exact_artifact_path) {
|
||||
CurrentFilePath = Flags.exact_artifact_path;
|
||||
WriteToFile(U, CurrentFilePath);
|
||||
}
|
||||
Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n",
|
||||
CurrentFilePath.c_str(), U.size());
|
||||
return 0;
|
||||
}
|
||||
CurrentFilePath = ArtifactPath;
|
||||
Printf("\n\n\n\n\n\n*********************************\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
|
||||
assert(Inputs->size() == 1);
|
||||
std::string InputFilePath = Inputs->at(0);
|
||||
Unit U = FileToVector(InputFilePath);
|
||||
assert(U.size() > 2);
|
||||
Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size());
|
||||
Corpus->AddToCorpus(U, 0);
|
||||
F->SetMaxInputLen(U.size());
|
||||
F->SetMaxMutationLen(U.size() - 1);
|
||||
F->MinimizeCrashLoop(U);
|
||||
Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n");
|
||||
exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
|
||||
using namespace fuzzer;
|
||||
assert(argc && argv && "Argument pointers cannot be nullptr");
|
||||
EF = new ExternalFunctions();
|
||||
if (EF->LLVMFuzzerInitialize)
|
||||
EF->LLVMFuzzerInitialize(argc, argv);
|
||||
const std::vector<std::string> Args(*argv, *argv + *argc);
|
||||
assert(!Args.empty());
|
||||
ProgName = new std::string(Args[0]);
|
||||
ParseFlags(Args);
|
||||
if (Flags.help) {
|
||||
PrintHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Flags.minimize_crash)
|
||||
return MinimizeCrashInput(Args);
|
||||
|
||||
if (Flags.close_fd_mask & 2)
|
||||
DupAndCloseStderr();
|
||||
if (Flags.close_fd_mask & 1)
|
||||
CloseStdout();
|
||||
|
||||
if (Flags.jobs > 0 && Flags.workers == 0) {
|
||||
Flags.workers = std::min(NumberOfCpuCores() / 2, Flags.jobs);
|
||||
if (Flags.workers > 1)
|
||||
Printf("Running %u workers\n", Flags.workers);
|
||||
}
|
||||
|
||||
if (Flags.workers > 0 && Flags.jobs > 0)
|
||||
return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs);
|
||||
|
||||
const size_t kMaxSaneLen = 1 << 20;
|
||||
const size_t kMinDefaultLen = 64;
|
||||
FuzzingOptions Options;
|
||||
Options.Verbosity = Flags.verbosity;
|
||||
Options.MaxLen = Flags.max_len;
|
||||
Options.UnitTimeoutSec = Flags.timeout;
|
||||
Options.ErrorExitCode = Flags.error_exitcode;
|
||||
Options.TimeoutExitCode = Flags.timeout_exitcode;
|
||||
Options.MaxTotalTimeSec = Flags.max_total_time;
|
||||
Options.DoCrossOver = Flags.cross_over;
|
||||
Options.MutateDepth = Flags.mutate_depth;
|
||||
Options.UseCounters = Flags.use_counters;
|
||||
Options.UseIndirCalls = Flags.use_indir_calls;
|
||||
Options.UseMemcmp = Flags.use_memcmp;
|
||||
Options.UseMemmem = Flags.use_memmem;
|
||||
Options.UseCmp = Flags.use_cmp;
|
||||
Options.UseValueProfile = Flags.use_value_profile;
|
||||
Options.Shrink = Flags.shrink;
|
||||
Options.ShuffleAtStartUp = Flags.shuffle;
|
||||
Options.PreferSmall = Flags.prefer_small;
|
||||
Options.ReloadIntervalSec = Flags.reload;
|
||||
Options.OnlyASCII = Flags.only_ascii;
|
||||
Options.OutputCSV = Flags.output_csv;
|
||||
Options.DetectLeaks = Flags.detect_leaks;
|
||||
Options.TraceMalloc = Flags.trace_malloc;
|
||||
Options.RssLimitMb = Flags.rss_limit_mb;
|
||||
if (Flags.runs >= 0)
|
||||
Options.MaxNumberOfRuns = Flags.runs;
|
||||
if (!Inputs->empty() && !Flags.minimize_crash_internal_step)
|
||||
Options.OutputCorpus = (*Inputs)[0];
|
||||
Options.ReportSlowUnits = Flags.report_slow_units;
|
||||
if (Flags.artifact_prefix)
|
||||
Options.ArtifactPrefix = Flags.artifact_prefix;
|
||||
if (Flags.exact_artifact_path)
|
||||
Options.ExactArtifactPath = Flags.exact_artifact_path;
|
||||
std::vector<Unit> Dictionary;
|
||||
if (Flags.dict)
|
||||
if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary))
|
||||
return 1;
|
||||
if (Flags.verbosity > 0 && !Dictionary.empty())
|
||||
Printf("Dictionary: %zd entries\n", Dictionary.size());
|
||||
bool DoPlainRun = AllInputsAreFiles();
|
||||
Options.SaveArtifacts =
|
||||
!DoPlainRun || Flags.minimize_crash_internal_step;
|
||||
Options.PrintNewCovPcs = Flags.print_pcs;
|
||||
Options.PrintFinalStats = Flags.print_final_stats;
|
||||
Options.PrintCorpusStats = Flags.print_corpus_stats;
|
||||
Options.PrintCoverage = Flags.print_coverage;
|
||||
Options.DumpCoverage = Flags.dump_coverage;
|
||||
if (Flags.exit_on_src_pos)
|
||||
Options.ExitOnSrcPos = Flags.exit_on_src_pos;
|
||||
if (Flags.exit_on_item)
|
||||
Options.ExitOnItem = Flags.exit_on_item;
|
||||
|
||||
unsigned Seed = Flags.seed;
|
||||
// Initialize Seed.
|
||||
if (Seed == 0)
|
||||
Seed = (std::chrono::system_clock::now().time_since_epoch().count() << 10) +
|
||||
GetPid();
|
||||
if (Flags.verbosity)
|
||||
Printf("INFO: Seed: %u\n", Seed);
|
||||
|
||||
Random Rand(Seed);
|
||||
auto *MD = new MutationDispatcher(Rand, Options);
|
||||
auto *Corpus = new InputCorpus(Options.OutputCorpus);
|
||||
auto *F = new Fuzzer(Callback, *Corpus, *MD, Options);
|
||||
|
||||
for (auto &U: Dictionary)
|
||||
if (U.size() <= Word::GetMaxSize())
|
||||
MD->AddWordToManualDictionary(Word(U.data(), U.size()));
|
||||
|
||||
StartRssThread(F, Flags.rss_limit_mb);
|
||||
|
||||
Options.HandleAbrt = Flags.handle_abrt;
|
||||
Options.HandleBus = Flags.handle_bus;
|
||||
Options.HandleFpe = Flags.handle_fpe;
|
||||
Options.HandleIll = Flags.handle_ill;
|
||||
Options.HandleInt = Flags.handle_int;
|
||||
Options.HandleSegv = Flags.handle_segv;
|
||||
Options.HandleTerm = Flags.handle_term;
|
||||
SetSignalHandler(Options);
|
||||
|
||||
if (Flags.minimize_crash_internal_step)
|
||||
return MinimizeCrashInputInternalStep(F, Corpus);
|
||||
|
||||
if (DoPlainRun) {
|
||||
Options.SaveArtifacts = false;
|
||||
int Runs = std::max(1, Flags.runs);
|
||||
Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(),
|
||||
Inputs->size(), Runs);
|
||||
for (auto &Path : *Inputs) {
|
||||
auto StartTime = system_clock::now();
|
||||
Printf("Running: %s\n", Path.c_str());
|
||||
for (int Iter = 0; Iter < Runs; Iter++)
|
||||
RunOneTest(F, Path.c_str(), Options.MaxLen);
|
||||
auto StopTime = system_clock::now();
|
||||
auto MS = duration_cast<milliseconds>(StopTime - StartTime).count();
|
||||
Printf("Executed %s in %zd ms\n", Path.c_str(), (long)MS);
|
||||
}
|
||||
Printf("***\n"
|
||||
"*** NOTE: fuzzing was not performed, you have only\n"
|
||||
"*** executed the target code on a fixed set of inputs.\n"
|
||||
"***\n");
|
||||
F->PrintFinalStats();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (Flags.merge) {
|
||||
if (Options.MaxLen == 0)
|
||||
F->SetMaxInputLen(kMaxSaneLen);
|
||||
if (TPC.UsingTracePcGuard()) {
|
||||
if (Flags.merge_control_file)
|
||||
F->CrashResistantMergeInternalStep(Flags.merge_control_file);
|
||||
else
|
||||
F->CrashResistantMerge(Args, *Inputs);
|
||||
} else {
|
||||
F->Merge(*Inputs);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
size_t TemporaryMaxLen = Options.MaxLen ? Options.MaxLen : kMaxSaneLen;
|
||||
|
||||
UnitVector InitialCorpus;
|
||||
for (auto &Inp : *Inputs) {
|
||||
Printf("Loading corpus dir: %s\n", Inp.c_str());
|
||||
ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr,
|
||||
TemporaryMaxLen, /*ExitOnError=*/false);
|
||||
}
|
||||
|
||||
if (Options.MaxLen == 0) {
|
||||
size_t MaxLen = 0;
|
||||
for (auto &U : InitialCorpus)
|
||||
MaxLen = std::max(U.size(), MaxLen);
|
||||
F->SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxLen), kMaxSaneLen));
|
||||
}
|
||||
|
||||
if (InitialCorpus.empty()) {
|
||||
InitialCorpus.push_back(Unit({'\n'})); // Valid ASCII input.
|
||||
if (Options.Verbosity)
|
||||
Printf("INFO: A corpus is not provided, starting from an empty corpus\n");
|
||||
}
|
||||
F->ShuffleAndMinimize(&InitialCorpus);
|
||||
InitialCorpus.clear(); // Don't need this memory any more.
|
||||
F->Loop();
|
||||
|
||||
if (Flags.verbosity)
|
||||
Printf("Done %d runs in %zd second(s)\n", F->getTotalNumberOfRuns(),
|
||||
F->secondsSinceProcessStartUp());
|
||||
F->PrintFinalStats();
|
||||
|
||||
exit(0); // Don't let F destroy itself.
|
||||
}
|
||||
|
||||
// Storage for global ExternalFunctions object.
|
||||
ExternalFunctions *EF = nullptr;
|
||||
|
||||
} // namespace fuzzer
|
50
test/fuzz_test/Fuzzer/FuzzerExtFunctions.def
Normal file
50
test/fuzz_test/Fuzzer/FuzzerExtFunctions.def
Normal file
|
@ -0,0 +1,50 @@
|
|||
//===- FuzzerExtFunctions.def - External functions --------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This defines the external function pointers that
|
||||
// ``fuzzer::ExternalFunctions`` should contain and try to initialize. The
|
||||
// EXT_FUNC macro must be defined at the point of inclusion. The signature of
|
||||
// the macro is:
|
||||
//
|
||||
// EXT_FUNC(<name>, <return_type>, <function_signature>, <warn_if_missing>)
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Optional user functions
|
||||
EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false);
|
||||
EXT_FUNC(LLVMFuzzerCustomMutator, size_t,
|
||||
(uint8_t * Data, size_t Size, size_t MaxSize, unsigned int Seed),
|
||||
false);
|
||||
EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t,
|
||||
(const uint8_t * Data1, size_t Size1,
|
||||
const uint8_t * Data2, size_t Size2,
|
||||
uint8_t * Out, size_t MaxOutSize, unsigned int Seed),
|
||||
false);
|
||||
|
||||
// Sanitizer functions
|
||||
EXT_FUNC(__lsan_enable, void, (), false);
|
||||
EXT_FUNC(__lsan_disable, void, (), false);
|
||||
EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false);
|
||||
EXT_FUNC(__sanitizer_get_number_of_counters, size_t, (), false);
|
||||
EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int,
|
||||
(void (*malloc_hook)(const volatile void *, size_t),
|
||||
void (*free_hook)(const volatile void *)),
|
||||
false);
|
||||
EXT_FUNC(__sanitizer_get_total_unique_caller_callee_pairs, size_t, (), false);
|
||||
EXT_FUNC(__sanitizer_get_total_unique_coverage, size_t, (), true);
|
||||
EXT_FUNC(__sanitizer_print_memory_profile, int, (size_t), false);
|
||||
EXT_FUNC(__sanitizer_print_stack_trace, void, (), true);
|
||||
EXT_FUNC(__sanitizer_symbolize_pc, void,
|
||||
(void *, const char *fmt, char *out_buf, size_t out_buf_size), false);
|
||||
EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int,
|
||||
(void *pc, char *module_path,
|
||||
size_t module_path_len,void **pc_offset), false);
|
||||
EXT_FUNC(__sanitizer_reset_coverage, void, (), true);
|
||||
EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true);
|
||||
EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false);
|
||||
EXT_FUNC(__sanitizer_update_counter_bitset_and_clear_counters, uintptr_t,
|
||||
(uint8_t*), false);
|
35
test/fuzz_test/Fuzzer/FuzzerExtFunctions.h
Normal file
35
test/fuzz_test/Fuzzer/FuzzerExtFunctions.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
//===- FuzzerExtFunctions.h - Interface to external functions ---*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Defines an interface to (possibly optional) functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_EXT_FUNCTIONS_H
|
||||
#define LLVM_FUZZER_EXT_FUNCTIONS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
struct ExternalFunctions {
|
||||
// Initialize function pointers. Functions that are not available will be set
|
||||
// to nullptr. Do not call this constructor before ``main()`` has been
|
||||
// entered.
|
||||
ExternalFunctions();
|
||||
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
RETURN_TYPE(*NAME) FUNC_SIG = nullptr
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
};
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif
|
52
test/fuzz_test/Fuzzer/FuzzerExtFunctionsDlsym.cpp
Normal file
52
test/fuzz_test/Fuzzer/FuzzerExtFunctionsDlsym.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
//===- FuzzerExtFunctionsDlsym.cpp - Interface to external functions ------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation for operating systems that support dlsym(). We only use it on
|
||||
// Apple platforms for now. We don't use this approach on Linux because it
|
||||
// requires that clients of LibFuzzer pass ``--export-dynamic`` to the linker.
|
||||
// That is a complication we don't wish to expose to clients right now.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_APPLE
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
using namespace fuzzer;
|
||||
|
||||
template <typename T>
|
||||
static T GetFnPtr(const char *FnName, bool WarnIfMissing) {
|
||||
dlerror(); // Clear any previous errors.
|
||||
void *Fn = dlsym(RTLD_DEFAULT, FnName);
|
||||
if (Fn == nullptr) {
|
||||
if (WarnIfMissing) {
|
||||
const char *ErrorMsg = dlerror();
|
||||
Printf("WARNING: Failed to find function \"%s\".", FnName);
|
||||
if (ErrorMsg)
|
||||
Printf(" Reason %s.", ErrorMsg);
|
||||
Printf("\n");
|
||||
}
|
||||
}
|
||||
return reinterpret_cast<T>(Fn);
|
||||
}
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
ExternalFunctions::ExternalFunctions() {
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
this->NAME = GetFnPtr<decltype(ExternalFunctions::NAME)>(#NAME, WARN)
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_APPLE
|
53
test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeak.cpp
Normal file
53
test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeak.cpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
//===- FuzzerExtFunctionsWeak.cpp - Interface to external functions -------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation for Linux. This relies on the linker's support for weak
|
||||
// symbols. We don't use this approach on Apple platforms because it requires
|
||||
// clients of LibFuzzer to pass ``-U _<symbol_name>`` to the linker to allow
|
||||
// weak symbols to be undefined. That is a complication we don't want to expose
|
||||
// to clients right now.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_LINUX
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
|
||||
extern "C" {
|
||||
// Declare these symbols as weak to allow them to be optionally defined.
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
__attribute__((weak)) RETURN_TYPE NAME FUNC_SIG
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
using namespace fuzzer;
|
||||
|
||||
static void CheckFnPtr(void *FnPtr, const char *FnName, bool WarnIfMissing) {
|
||||
if (FnPtr == nullptr && WarnIfMissing) {
|
||||
Printf("WARNING: Failed to find function \"%s\".\n", FnName);
|
||||
}
|
||||
}
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
ExternalFunctions::ExternalFunctions() {
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
this->NAME = ::NAME; \
|
||||
CheckFnPtr((void *)::NAME, #NAME, WARN);
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_LINUX
|
56
test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp
Normal file
56
test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
//===- FuzzerExtFunctionsWeakAlias.cpp - Interface to external functions --===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation using weak aliases. Works for Windows.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
|
||||
using namespace fuzzer;
|
||||
|
||||
extern "C" {
|
||||
// Declare these symbols as weak to allow them to be optionally defined.
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
RETURN_TYPE NAME##Def FUNC_SIG { \
|
||||
Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \
|
||||
exit(1); \
|
||||
} \
|
||||
RETURN_TYPE NAME FUNC_SIG __attribute__((weak, alias(#NAME "Def")));
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) {
|
||||
if (Fun == FunDef) {
|
||||
if (WarnIfMissing)
|
||||
Printf("WARNING: Failed to find function \"%s\".\n", FnName);
|
||||
return nullptr;
|
||||
}
|
||||
return Fun;
|
||||
}
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
ExternalFunctions::ExternalFunctions() {
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
this->NAME = GetFnPtr<decltype(::NAME)>(::NAME, ::NAME##Def, #NAME, WARN);
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
115
test/fuzz_test/Fuzzer/FuzzerFlags.def
Normal file
115
test/fuzz_test/Fuzzer/FuzzerFlags.def
Normal file
|
@ -0,0 +1,115 @@
|
|||
//===- FuzzerFlags.def - Run-time flags -------------------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the
|
||||
// point of inclusion. We are not using any flag parsing library for better
|
||||
// portability and independence.
|
||||
//===----------------------------------------------------------------------===//
|
||||
FUZZER_FLAG_INT(verbosity, 1, "Verbosity level.")
|
||||
FUZZER_FLAG_UNSIGNED(seed, 0, "Random seed. If 0, seed is generated.")
|
||||
FUZZER_FLAG_INT(runs, -1,
|
||||
"Number of individual test runs (-1 for infinite runs).")
|
||||
FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. "
|
||||
"If 0, libFuzzer tries to guess a good value based on the corpus "
|
||||
"and reports it. ")
|
||||
FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.")
|
||||
FUZZER_FLAG_INT(mutate_depth, 5,
|
||||
"Apply this number of consecutive mutations to each input.")
|
||||
FUZZER_FLAG_INT(shuffle, 1, "Shuffle inputs at startup")
|
||||
FUZZER_FLAG_INT(prefer_small, 1,
|
||||
"If 1, always prefer smaller inputs during the corpus shuffle.")
|
||||
FUZZER_FLAG_INT(
|
||||
timeout, 1200,
|
||||
"Timeout in seconds (if positive). "
|
||||
"If one unit runs more than this number of seconds the process will abort.")
|
||||
FUZZER_FLAG_INT(error_exitcode, 77, "When libFuzzer itself reports a bug "
|
||||
"this exit code will be used.")
|
||||
FUZZER_FLAG_INT(timeout_exitcode, 77, "When libFuzzer reports a timeout "
|
||||
"this exit code will be used.")
|
||||
FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total "
|
||||
"time in seconds to run the fuzzer.")
|
||||
FUZZER_FLAG_INT(help, 0, "Print help.")
|
||||
FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be "
|
||||
"merged into the 1-st corpus. Only interesting units will be taken. "
|
||||
"This flag can be used to minimize a corpus.")
|
||||
FUZZER_FLAG_STRING(merge_control_file, "internal flag")
|
||||
FUZZER_FLAG_INT(minimize_crash, 0, "If 1, minimizes the provided"
|
||||
" crash input. Use with -runs=N or -max_total_time=N to limit "
|
||||
"the number attempts")
|
||||
FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag")
|
||||
FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters")
|
||||
FUZZER_FLAG_INT(use_indir_calls, 1, "Use indirect caller-callee counters")
|
||||
FUZZER_FLAG_INT(use_memcmp, 1,
|
||||
"Use hints from intercepting memcmp, strcmp, etc")
|
||||
FUZZER_FLAG_INT(use_memmem, 1,
|
||||
"Use hints from intercepting memmem, strstr, etc")
|
||||
FUZZER_FLAG_INT(use_value_profile, 0,
|
||||
"Experimental. Use value profile to guide fuzzing.")
|
||||
FUZZER_FLAG_INT(use_cmp, 1, "Use CMP traces to guide mutations")
|
||||
FUZZER_FLAG_INT(shrink, 0, "Experimental. Try to shrink corpus elements.")
|
||||
FUZZER_FLAG_UNSIGNED(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn"
|
||||
" this number of jobs in separate worker processes"
|
||||
" with stdout/stderr redirected to fuzz-JOB.log.")
|
||||
FUZZER_FLAG_UNSIGNED(workers, 0,
|
||||
"Number of simultaneous worker processes to run the jobs."
|
||||
" If zero, \"min(jobs,NumberOfCpuCores()/2)\" is used.")
|
||||
FUZZER_FLAG_INT(reload, 1,
|
||||
"Reload the main corpus every <N> seconds to get new units"
|
||||
" discovered by other processes. If 0, disabled")
|
||||
FUZZER_FLAG_INT(report_slow_units, 10,
|
||||
"Report slowest units if they run for more than this number of seconds.")
|
||||
FUZZER_FLAG_INT(only_ascii, 0,
|
||||
"If 1, generate only ASCII (isprint+isspace) inputs.")
|
||||
FUZZER_FLAG_STRING(dict, "Experimental. Use the dictionary file.")
|
||||
FUZZER_FLAG_STRING(artifact_prefix, "Write fuzzing artifacts (crash, "
|
||||
"timeout, or slow inputs) as "
|
||||
"$(artifact_prefix)file")
|
||||
FUZZER_FLAG_STRING(exact_artifact_path,
|
||||
"Write the single artifact on failure (crash, timeout) "
|
||||
"as $(exact_artifact_path). This overrides -artifact_prefix "
|
||||
"and will not use checksum in the file name. Do not "
|
||||
"use the same path for several parallel processes.")
|
||||
FUZZER_FLAG_INT(output_csv, 0, "Enable pulse output in CSV format.")
|
||||
FUZZER_FLAG_INT(print_pcs, 0, "If 1, print out newly covered PCs.")
|
||||
FUZZER_FLAG_INT(print_final_stats, 0, "If 1, print statistics at exit.")
|
||||
FUZZER_FLAG_INT(print_corpus_stats, 0,
|
||||
"If 1, print statistics on corpus elements at exit.")
|
||||
FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information at exit."
|
||||
" Experimental, only with trace-pc-guard")
|
||||
FUZZER_FLAG_INT(dump_coverage, 0, "If 1, dump coverage information at exit."
|
||||
" Experimental, only with trace-pc-guard")
|
||||
FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.")
|
||||
FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGSEGV.")
|
||||
FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.")
|
||||
FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.")
|
||||
FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.")
|
||||
FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.")
|
||||
FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.")
|
||||
FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; "
|
||||
"if 2, close stderr; if 3, close both. "
|
||||
"Be careful, this will also close e.g. asan's stderr/stdout.")
|
||||
FUZZER_FLAG_INT(detect_leaks, 1, "If 1, and if LeakSanitizer is enabled "
|
||||
"try to detect memory leaks during fuzzing (i.e. not only at shut down).")
|
||||
FUZZER_FLAG_INT(trace_malloc, 0, "If >= 1 will print all mallocs/frees. "
|
||||
"If >= 2 will also print stack traces.")
|
||||
FUZZER_FLAG_INT(rss_limit_mb, 2048, "If non-zero, the fuzzer will exit upon"
|
||||
"reaching this limit of RSS memory usage.")
|
||||
FUZZER_FLAG_STRING(exit_on_src_pos, "Exit if a newly found PC originates"
|
||||
" from the given source location. Example: -exit_on_src_pos=foo.cc:123. "
|
||||
"Used primarily for testing libFuzzer itself.")
|
||||
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_DEPRECATED_FLAG(exit_on_first)
|
||||
FUZZER_DEPRECATED_FLAG(save_minimized_corpus)
|
||||
FUZZER_DEPRECATED_FLAG(sync_command)
|
||||
FUZZER_DEPRECATED_FLAG(sync_timeout)
|
||||
FUZZER_DEPRECATED_FLAG(test_single_input)
|
||||
FUZZER_DEPRECATED_FLAG(drill)
|
||||
FUZZER_DEPRECATED_FLAG(truncate_units)
|
117
test/fuzz_test/Fuzzer/FuzzerIO.cpp
Normal file
117
test/fuzz_test/Fuzzer/FuzzerIO.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
//===- FuzzerIO.cpp - IO utils. -------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IO functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
static FILE *OutputFile = stderr;
|
||||
|
||||
long GetEpoch(const std::string &Path) {
|
||||
struct stat St;
|
||||
if (stat(Path.c_str(), &St))
|
||||
return 0; // Can't stat, be conservative.
|
||||
return St.st_mtime;
|
||||
}
|
||||
|
||||
Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) {
|
||||
std::ifstream T(Path);
|
||||
if (ExitOnError && !T) {
|
||||
Printf("No such directory: %s; exiting\n", Path.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
T.seekg(0, T.end);
|
||||
size_t FileLen = T.tellg();
|
||||
if (MaxSize)
|
||||
FileLen = std::min(FileLen, MaxSize);
|
||||
|
||||
T.seekg(0, T.beg);
|
||||
Unit Res(FileLen);
|
||||
T.read(reinterpret_cast<char *>(Res.data()), FileLen);
|
||||
return Res;
|
||||
}
|
||||
|
||||
std::string FileToString(const std::string &Path) {
|
||||
std::ifstream T(Path);
|
||||
return std::string((std::istreambuf_iterator<char>(T)),
|
||||
std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
void CopyFileToErr(const std::string &Path) {
|
||||
Printf("%s", FileToString(Path).c_str());
|
||||
}
|
||||
|
||||
void WriteToFile(const Unit &U, const std::string &Path) {
|
||||
// Use raw C interface because this function may be called from a sig handler.
|
||||
FILE *Out = fopen(Path.c_str(), "w");
|
||||
if (!Out) return;
|
||||
fwrite(U.data(), sizeof(U[0]), U.size(), Out);
|
||||
fclose(Out);
|
||||
}
|
||||
|
||||
void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V,
|
||||
long *Epoch, size_t MaxSize, bool ExitOnError) {
|
||||
long E = Epoch ? *Epoch : 0;
|
||||
std::vector<std::string> Files;
|
||||
ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true);
|
||||
size_t NumLoaded = 0;
|
||||
for (size_t i = 0; i < Files.size(); i++) {
|
||||
auto &X = Files[i];
|
||||
if (Epoch && GetEpoch(X) < E) continue;
|
||||
NumLoaded++;
|
||||
if ((NumLoaded & (NumLoaded - 1)) == 0 && NumLoaded >= 1024)
|
||||
Printf("Loaded %zd/%zd files from %s\n", NumLoaded, Files.size(), Path);
|
||||
auto S = FileToVector(X, MaxSize, ExitOnError);
|
||||
if (!S.empty())
|
||||
V->push_back(S);
|
||||
}
|
||||
}
|
||||
|
||||
std::string DirPlusFile(const std::string &DirPath,
|
||||
const std::string &FileName) {
|
||||
return DirPath + GetSeparator() + FileName;
|
||||
}
|
||||
|
||||
void DupAndCloseStderr() {
|
||||
int OutputFd = DuplicateFile(2);
|
||||
if (OutputFd > 0) {
|
||||
FILE *NewOutputFile = OpenFile(OutputFd, "w");
|
||||
if (NewOutputFile) {
|
||||
OutputFile = NewOutputFile;
|
||||
if (EF->__sanitizer_set_report_fd)
|
||||
EF->__sanitizer_set_report_fd(reinterpret_cast<void *>(OutputFd));
|
||||
CloseFile(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CloseStdout() {
|
||||
CloseFile(1);
|
||||
}
|
||||
|
||||
void Printf(const char *Fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, Fmt);
|
||||
vfprintf(OutputFile, Fmt, ap);
|
||||
va_end(ap);
|
||||
fflush(OutputFile);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
64
test/fuzz_test/Fuzzer/FuzzerIO.h
Normal file
64
test/fuzz_test/Fuzzer/FuzzerIO.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
//===- FuzzerIO.h - Internal header for IO utils ----------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IO interface.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_IO_H
|
||||
#define LLVM_FUZZER_IO_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
long GetEpoch(const std::string &Path);
|
||||
|
||||
Unit FileToVector(const std::string &Path, size_t MaxSize = 0,
|
||||
bool ExitOnError = true);
|
||||
|
||||
std::string FileToString(const std::string &Path);
|
||||
|
||||
void CopyFileToErr(const std::string &Path);
|
||||
|
||||
void WriteToFile(const Unit &U, const std::string &Path);
|
||||
|
||||
void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V,
|
||||
long *Epoch, size_t MaxSize, bool ExitOnError);
|
||||
|
||||
// Returns "Dir/FileName" or equivalent for the current OS.
|
||||
std::string DirPlusFile(const std::string &DirPath,
|
||||
const std::string &FileName);
|
||||
|
||||
// Returns the name of the dir, similar to the 'dirname' utility.
|
||||
std::string DirName(const std::string &FileName);
|
||||
|
||||
void DupAndCloseStderr();
|
||||
|
||||
void CloseStdout();
|
||||
|
||||
void Printf(const char *Fmt, ...);
|
||||
|
||||
// Platform specific functions:
|
||||
bool IsFile(const std::string &Path);
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
std::vector<std::string> *V, bool TopDir);
|
||||
|
||||
char GetSeparator();
|
||||
|
||||
FILE* OpenFile(int Fd, const char *Mode);
|
||||
|
||||
int CloseFile(int Fd);
|
||||
|
||||
int DuplicateFile(int Fd);
|
||||
|
||||
void RemoveFile(const std::string &Path);
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_IO_H
|
88
test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp
Normal file
88
test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
//===- FuzzerIOPosix.cpp - IO utils for Posix. ----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IO functions implementation using Posix API.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_POSIX
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <dirent.h>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <libgen.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
bool IsFile(const std::string &Path) {
|
||||
struct stat St;
|
||||
if (stat(Path.c_str(), &St))
|
||||
return false;
|
||||
return S_ISREG(St.st_mode);
|
||||
}
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
std::vector<std::string> *V, bool TopDir) {
|
||||
auto E = GetEpoch(Dir);
|
||||
if (Epoch)
|
||||
if (E && *Epoch >= E) return;
|
||||
|
||||
DIR *D = opendir(Dir.c_str());
|
||||
if (!D) {
|
||||
Printf("No such directory: %s; exiting\n", Dir.c_str());
|
||||
exit(1);
|
||||
}
|
||||
while (auto E = readdir(D)) {
|
||||
std::string Path = DirPlusFile(Dir, E->d_name);
|
||||
if (E->d_type == DT_REG || E->d_type == DT_LNK)
|
||||
V->push_back(Path);
|
||||
else if (E->d_type == DT_DIR && *E->d_name != '.')
|
||||
ListFilesInDirRecursive(Path, Epoch, V, false);
|
||||
}
|
||||
closedir(D);
|
||||
if (Epoch && TopDir)
|
||||
*Epoch = E;
|
||||
}
|
||||
|
||||
char GetSeparator() {
|
||||
return '/';
|
||||
}
|
||||
|
||||
FILE* OpenFile(int Fd, const char* Mode) {
|
||||
return fdopen(Fd, Mode);
|
||||
}
|
||||
|
||||
int CloseFile(int fd) {
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
int DuplicateFile(int Fd) {
|
||||
return dup(Fd);
|
||||
}
|
||||
|
||||
void RemoveFile(const std::string &Path) {
|
||||
unlink(Path.c_str());
|
||||
}
|
||||
|
||||
std::string DirName(const std::string &FileName) {
|
||||
char *Tmp = new char[FileName.size() + 1];
|
||||
memcpy(Tmp, FileName.c_str(), FileName.size() + 1);
|
||||
std::string Res = dirname(Tmp);
|
||||
delete [] Tmp;
|
||||
return Res;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_POSIX
|
282
test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp
Normal file
282
test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp
Normal file
|
@ -0,0 +1,282 @@
|
|||
//===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// IO functions implementation for Windows.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <io.h>
|
||||
#include <iterator>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <windows.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
static bool IsFile(const std::string &Path, const DWORD &FileAttributes) {
|
||||
|
||||
if (FileAttributes & FILE_ATTRIBUTE_NORMAL)
|
||||
return true;
|
||||
|
||||
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
return false;
|
||||
|
||||
HANDLE FileHandle(
|
||||
CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, 0));
|
||||
|
||||
if (FileHandle == INVALID_HANDLE_VALUE) {
|
||||
Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
|
||||
GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD FileType = GetFileType(FileHandle);
|
||||
|
||||
if (FileType == FILE_TYPE_UNKNOWN) {
|
||||
Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
|
||||
GetLastError());
|
||||
CloseHandle(FileHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FileType != FILE_TYPE_DISK) {
|
||||
CloseHandle(FileHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
CloseHandle(FileHandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsFile(const std::string &Path) {
|
||||
DWORD Att = GetFileAttributesA(Path.c_str());
|
||||
|
||||
if (Att == INVALID_FILE_ATTRIBUTES) {
|
||||
Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n",
|
||||
Path.c_str(), GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsFile(Path, Att);
|
||||
}
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
std::vector<std::string> *V, bool TopDir) {
|
||||
auto E = GetEpoch(Dir);
|
||||
if (Epoch)
|
||||
if (E && *Epoch >= E) return;
|
||||
|
||||
std::string Path(Dir);
|
||||
assert(!Path.empty());
|
||||
if (Path.back() != '\\')
|
||||
Path.push_back('\\');
|
||||
Path.push_back('*');
|
||||
|
||||
// Get the first directory entry.
|
||||
WIN32_FIND_DATAA FindInfo;
|
||||
HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo));
|
||||
if (FindHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Printf("No file found in: %s.\n", Dir.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
std::string FileName = DirPlusFile(Dir, FindInfo.cFileName);
|
||||
|
||||
if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
size_t FilenameLen = strlen(FindInfo.cFileName);
|
||||
if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') ||
|
||||
(FilenameLen == 2 && FindInfo.cFileName[0] == '.' &&
|
||||
FindInfo.cFileName[1] == '.'))
|
||||
continue;
|
||||
|
||||
ListFilesInDirRecursive(FileName, Epoch, V, false);
|
||||
}
|
||||
else if (IsFile(FileName, FindInfo.dwFileAttributes))
|
||||
V->push_back(FileName);
|
||||
} while (FindNextFileA(FindHandle, &FindInfo));
|
||||
|
||||
DWORD LastError = GetLastError();
|
||||
if (LastError != ERROR_NO_MORE_FILES)
|
||||
Printf("FindNextFileA failed (Error code: %lu).\n", LastError);
|
||||
|
||||
FindClose(FindHandle);
|
||||
|
||||
if (Epoch && TopDir)
|
||||
*Epoch = E;
|
||||
}
|
||||
|
||||
char GetSeparator() {
|
||||
return '\\';
|
||||
}
|
||||
|
||||
FILE* OpenFile(int Fd, const char* Mode) {
|
||||
return _fdopen(Fd, Mode);
|
||||
}
|
||||
|
||||
int CloseFile(int Fd) {
|
||||
return _close(Fd);
|
||||
}
|
||||
|
||||
int DuplicateFile(int Fd) {
|
||||
return _dup(Fd);
|
||||
}
|
||||
|
||||
void RemoveFile(const std::string &Path) {
|
||||
_unlink(Path.c_str());
|
||||
}
|
||||
|
||||
static bool IsSeparator(char C) {
|
||||
return C == '\\' || C == '/';
|
||||
}
|
||||
|
||||
// Parse disk designators, like "C:\". If Relative == true, also accepts: "C:".
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseDrive(const std::string &FileName, const size_t Offset,
|
||||
bool Relative = true) {
|
||||
if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':')
|
||||
return 0;
|
||||
if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) {
|
||||
if (!Relative) // Accept relative path?
|
||||
return 0;
|
||||
else
|
||||
return 2;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Parse a file name, like: SomeFile.txt
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseFileName(const std::string &FileName, const size_t Offset) {
|
||||
size_t Pos = Offset;
|
||||
const size_t End = FileName.size();
|
||||
for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
|
||||
;
|
||||
return Pos - Offset;
|
||||
}
|
||||
|
||||
// Parse a directory ending in separator, like: SomeDir\
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseDir(const std::string &FileName, const size_t Offset) {
|
||||
size_t Pos = Offset;
|
||||
const size_t End = FileName.size();
|
||||
if (Pos >= End || IsSeparator(FileName[Pos]))
|
||||
return 0;
|
||||
for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
|
||||
;
|
||||
if (Pos >= End)
|
||||
return 0;
|
||||
++Pos; // Include separator.
|
||||
return Pos - Offset;
|
||||
}
|
||||
|
||||
// Parse a servername and share, like: SomeServer\SomeShare\
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseServerAndShare(const std::string &FileName,
|
||||
const size_t Offset) {
|
||||
size_t Pos = Offset, Res;
|
||||
if (!(Res = ParseDir(FileName, Pos)))
|
||||
return 0;
|
||||
Pos += Res;
|
||||
if (!(Res = ParseDir(FileName, Pos)))
|
||||
return 0;
|
||||
Pos += Res;
|
||||
return Pos - Offset;
|
||||
}
|
||||
|
||||
// Parse the given Ref string from the position Offset, to exactly match the given
|
||||
// string Patt.
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseCustomString(const std::string &Ref, size_t Offset,
|
||||
const char *Patt) {
|
||||
size_t Len = strlen(Patt);
|
||||
if (Offset + Len > Ref.size())
|
||||
return 0;
|
||||
return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0;
|
||||
}
|
||||
|
||||
// Parse a location, like:
|
||||
// \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C:
|
||||
// Returns number of characters considered if successful.
|
||||
static size_t ParseLocation(const std::string &FileName) {
|
||||
size_t Pos = 0, Res;
|
||||
|
||||
if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) {
|
||||
Pos += Res;
|
||||
if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) {
|
||||
Pos += Res;
|
||||
if ((Res = ParseServerAndShare(FileName, Pos)))
|
||||
return Pos + Res;
|
||||
return 0;
|
||||
}
|
||||
if ((Res = ParseDrive(FileName, Pos, false)))
|
||||
return Pos + Res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
|
||||
++Pos;
|
||||
if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
|
||||
++Pos;
|
||||
if ((Res = ParseServerAndShare(FileName, Pos)))
|
||||
return Pos + Res;
|
||||
return 0;
|
||||
}
|
||||
return Pos;
|
||||
}
|
||||
|
||||
if ((Res = ParseDrive(FileName, Pos)))
|
||||
return Pos + Res;
|
||||
|
||||
return Pos;
|
||||
}
|
||||
|
||||
std::string DirName(const std::string &FileName) {
|
||||
size_t LocationLen = ParseLocation(FileName);
|
||||
size_t DirLen = 0, Res;
|
||||
while ((Res = ParseDir(FileName, LocationLen + DirLen)))
|
||||
DirLen += Res;
|
||||
size_t FileLen = ParseFileName(FileName, LocationLen + DirLen);
|
||||
|
||||
if (LocationLen + DirLen + FileLen != FileName.size()) {
|
||||
Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (DirLen) {
|
||||
--DirLen; // Remove trailing separator.
|
||||
if (!FileLen) { // Path ended in separator.
|
||||
assert(DirLen);
|
||||
// Remove file name from Dir.
|
||||
while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1]))
|
||||
--DirLen;
|
||||
if (DirLen) // Remove trailing separator.
|
||||
--DirLen;
|
||||
}
|
||||
}
|
||||
|
||||
if (!LocationLen) { // Relative path.
|
||||
if (!DirLen)
|
||||
return ".";
|
||||
return std::string(".\\").append(FileName, 0, DirLen);
|
||||
}
|
||||
|
||||
return FileName.substr(0, LocationLen + DirLen);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
67
test/fuzz_test/Fuzzer/FuzzerInterface.h
Normal file
67
test/fuzz_test/Fuzzer/FuzzerInterface.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
//===- FuzzerInterface.h - Interface header for the Fuzzer ------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Define the interface between libFuzzer and the library being tested.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// NOTE: the libFuzzer interface is thin and in the majority of cases
|
||||
// you should not include this file into your target. In 95% of cases
|
||||
// all you need is to define the following function in your file:
|
||||
// extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||
|
||||
// WARNING: keep the interface in C.
|
||||
|
||||
#ifndef LLVM_FUZZER_INTERFACE_H
|
||||
#define LLVM_FUZZER_INTERFACE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
// Mandatory user-provided target function.
|
||||
// Executes the code under test with [Data, Data+Size) as the input.
|
||||
// libFuzzer will invoke this function *many* times with different inputs.
|
||||
// Must return 0.
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||
|
||||
// Optional user-provided initialization function.
|
||||
// If provided, this function will be called by libFuzzer once at startup.
|
||||
// It may read and modify argc/argv.
|
||||
// Must return 0.
|
||||
int LLVMFuzzerInitialize(int *argc, char ***argv);
|
||||
|
||||
// Optional user-provided custom mutator.
|
||||
// Mutates raw data in [Data, Data+Size) inplace.
|
||||
// Returns the new size, which is not greater than MaxSize.
|
||||
// Given the same Seed produces the same mutation.
|
||||
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
unsigned int Seed);
|
||||
|
||||
// Optional user-provided custom cross-over function.
|
||||
// Combines pieces of Data1 & Data2 together into Out.
|
||||
// Returns the new size, which is not greater than MaxOutSize.
|
||||
// Should produce the same mutation given the same Seed.
|
||||
size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
|
||||
const uint8_t *Data2, size_t Size2,
|
||||
uint8_t *Out, size_t MaxOutSize,
|
||||
unsigned int Seed);
|
||||
|
||||
// Experimental, may go away in future.
|
||||
// libFuzzer-provided function to be used inside LLVMFuzzerTestOneInput.
|
||||
// Mutates raw data in [Data, Data+Size) inplace.
|
||||
// Returns the new size, which is not greater than MaxSize.
|
||||
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // LLVM_FUZZER_INTERFACE_H
|
182
test/fuzz_test/Fuzzer/FuzzerInternal.h
Normal file
182
test/fuzz_test/Fuzzer/FuzzerInternal.h
Normal file
|
@ -0,0 +1,182 @@
|
|||
//===- FuzzerInternal.h - Internal header for the Fuzzer --------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Define the main class fuzzer::Fuzzer and most functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_INTERNAL_H
|
||||
#define LLVM_FUZZER_INTERNAL_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerInterface.h"
|
||||
#include "FuzzerOptions.h"
|
||||
#include "FuzzerSHA1.h"
|
||||
#include "FuzzerValueBitMap.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <string.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
class Fuzzer {
|
||||
public:
|
||||
|
||||
// Aggregates all available coverage measurements.
|
||||
struct Coverage {
|
||||
Coverage() { Reset(); }
|
||||
|
||||
void Reset() {
|
||||
BlockCoverage = 0;
|
||||
CallerCalleeCoverage = 0;
|
||||
CounterBitmapBits = 0;
|
||||
CounterBitmap.clear();
|
||||
VPMap.Reset();
|
||||
}
|
||||
|
||||
size_t BlockCoverage;
|
||||
size_t CallerCalleeCoverage;
|
||||
// Precalculated number of bits in CounterBitmap.
|
||||
size_t CounterBitmapBits;
|
||||
std::vector<uint8_t> CounterBitmap;
|
||||
ValueBitMap VPMap;
|
||||
};
|
||||
|
||||
Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
|
||||
FuzzingOptions Options);
|
||||
~Fuzzer();
|
||||
void Loop();
|
||||
void MinimizeCrashLoop(const Unit &U);
|
||||
void ShuffleAndMinimize(UnitVector *V);
|
||||
void InitializeTraceState();
|
||||
void RereadOutputCorpus(size_t MaxSize);
|
||||
|
||||
size_t secondsSinceProcessStartUp() {
|
||||
return duration_cast<seconds>(system_clock::now() - ProcessStartTime)
|
||||
.count();
|
||||
}
|
||||
|
||||
bool TimedOut() {
|
||||
return Options.MaxTotalTimeSec > 0 &&
|
||||
secondsSinceProcessStartUp() >
|
||||
static_cast<size_t>(Options.MaxTotalTimeSec);
|
||||
}
|
||||
|
||||
size_t execPerSec() {
|
||||
size_t Seconds = secondsSinceProcessStartUp();
|
||||
return Seconds ? TotalNumberOfRuns / Seconds : 0;
|
||||
}
|
||||
|
||||
size_t getTotalNumberOfRuns() { return TotalNumberOfRuns; }
|
||||
|
||||
static void StaticAlarmCallback();
|
||||
static void StaticCrashSignalCallback();
|
||||
static void StaticInterruptCallback();
|
||||
|
||||
void ExecuteCallback(const uint8_t *Data, size_t Size);
|
||||
size_t RunOne(const uint8_t *Data, size_t Size);
|
||||
|
||||
// Merge Corpora[1:] into Corpora[0].
|
||||
void Merge(const std::vector<std::string> &Corpora);
|
||||
void CrashResistantMerge(const std::vector<std::string> &Args,
|
||||
const std::vector<std::string> &Corpora);
|
||||
void CrashResistantMergeInternalStep(const std::string &ControlFilePath);
|
||||
// Returns a subset of 'Extra' that adds coverage to 'Initial'.
|
||||
UnitVector FindExtraUnits(const UnitVector &Initial, const UnitVector &Extra);
|
||||
MutationDispatcher &GetMD() { return MD; }
|
||||
void PrintFinalStats();
|
||||
void SetMaxInputLen(size_t MaxInputLen);
|
||||
void SetMaxMutationLen(size_t MaxMutationLen);
|
||||
void RssLimitCallback();
|
||||
|
||||
// Public for tests.
|
||||
void ResetCoverage();
|
||||
|
||||
bool InFuzzingThread() const { return IsMyThread; }
|
||||
size_t GetCurrentUnitInFuzzingThead(const uint8_t **Data) const;
|
||||
void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,
|
||||
bool DuringInitialCorpusExecution);
|
||||
|
||||
void HandleMalloc(size_t Size);
|
||||
|
||||
private:
|
||||
void AlarmCallback();
|
||||
void CrashCallback();
|
||||
void InterruptCallback();
|
||||
void MutateAndTestOne();
|
||||
void ReportNewCoverage(InputInfo *II, const Unit &U);
|
||||
size_t RunOne(const Unit &U) { return RunOne(U.data(), U.size()); }
|
||||
void WriteToOutputCorpus(const Unit &U);
|
||||
void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix);
|
||||
void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0);
|
||||
void PrintStatusForNewUnit(const Unit &U);
|
||||
void ShuffleCorpus(UnitVector *V);
|
||||
void AddToCorpus(const Unit &U);
|
||||
void CheckExitOnSrcPosOrItem();
|
||||
|
||||
// Trace-based fuzzing: we run a unit with some kind of tracing
|
||||
// enabled and record potentially useful mutations. Then
|
||||
// We apply these mutations one by one to the unit and run it again.
|
||||
|
||||
// Start tracing; forget all previously proposed mutations.
|
||||
void StartTraceRecording();
|
||||
// Stop tracing.
|
||||
void StopTraceRecording();
|
||||
|
||||
void SetDeathCallback();
|
||||
static void StaticDeathCallback();
|
||||
void DumpCurrentUnit(const char *Prefix);
|
||||
void DeathCallback();
|
||||
|
||||
void ResetEdgeCoverage();
|
||||
void ResetCounters();
|
||||
void PrepareCounters(Fuzzer::Coverage *C);
|
||||
bool RecordMaxCoverage(Fuzzer::Coverage *C);
|
||||
|
||||
void AllocateCurrentUnitData();
|
||||
uint8_t *CurrentUnitData = nullptr;
|
||||
std::atomic<size_t> CurrentUnitSize;
|
||||
uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit.
|
||||
bool RunningCB = false;
|
||||
|
||||
size_t TotalNumberOfRuns = 0;
|
||||
size_t NumberOfNewUnitsAdded = 0;
|
||||
|
||||
bool HasMoreMallocsThanFrees = false;
|
||||
size_t NumberOfLeakDetectionAttempts = 0;
|
||||
|
||||
UserCallback CB;
|
||||
InputCorpus &Corpus;
|
||||
MutationDispatcher &MD;
|
||||
FuzzingOptions Options;
|
||||
|
||||
system_clock::time_point ProcessStartTime = system_clock::now();
|
||||
system_clock::time_point UnitStartTime, UnitStopTime;
|
||||
long TimeOfLongestUnitInSeconds = 0;
|
||||
long EpochOfLastReadOfOutputCorpus = 0;
|
||||
|
||||
// Maximum recorded coverage.
|
||||
Coverage MaxCoverage;
|
||||
|
||||
size_t MaxInputLen = 0;
|
||||
size_t MaxMutationLen = 0;
|
||||
|
||||
// Need to know our own thread.
|
||||
static thread_local bool IsMyThread;
|
||||
|
||||
bool InMergeMode = false;
|
||||
};
|
||||
|
||||
}; // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_INTERNAL_H
|
792
test/fuzz_test/Fuzzer/FuzzerLoop.cpp
Normal file
792
test/fuzz_test/Fuzzer/FuzzerLoop.cpp
Normal file
|
@ -0,0 +1,792 @@
|
|||
//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Fuzzer's main loop.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<sanitizer / coverage_interface.h>)
|
||||
#include <sanitizer/coverage_interface.h>
|
||||
#endif
|
||||
#if __has_include(<sanitizer / lsan_interface.h>)
|
||||
#include <sanitizer/lsan_interface.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define NO_SANITIZE_MEMORY
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(memory_sanitizer)
|
||||
#undef NO_SANITIZE_MEMORY
|
||||
#define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace fuzzer {
|
||||
static const size_t kMaxUnitSizeToPrint = 256;
|
||||
|
||||
thread_local bool Fuzzer::IsMyThread;
|
||||
|
||||
static void MissingExternalApiFunction(const char *FnName) {
|
||||
Printf("ERROR: %s is not defined. Exiting.\n"
|
||||
"Did you use -fsanitize-coverage=... to build your code?\n",
|
||||
FnName);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#define CHECK_EXTERNAL_FUNCTION(fn) \
|
||||
do { \
|
||||
if (!(EF->fn)) \
|
||||
MissingExternalApiFunction(#fn); \
|
||||
} while (false)
|
||||
|
||||
// Only one Fuzzer per process.
|
||||
static Fuzzer *F;
|
||||
|
||||
void Fuzzer::ResetEdgeCoverage() {
|
||||
CHECK_EXTERNAL_FUNCTION(__sanitizer_reset_coverage);
|
||||
EF->__sanitizer_reset_coverage();
|
||||
}
|
||||
|
||||
void Fuzzer::ResetCounters() {
|
||||
if (Options.UseCounters)
|
||||
EF->__sanitizer_update_counter_bitset_and_clear_counters(0);
|
||||
}
|
||||
|
||||
void Fuzzer::PrepareCounters(Fuzzer::Coverage *C) {
|
||||
if (Options.UseCounters) {
|
||||
size_t NumCounters = EF->__sanitizer_get_number_of_counters();
|
||||
C->CounterBitmap.resize(NumCounters);
|
||||
}
|
||||
}
|
||||
|
||||
// Records data to a maximum coverage tracker. Returns true if additional
|
||||
// coverage was discovered.
|
||||
bool Fuzzer::RecordMaxCoverage(Fuzzer::Coverage *C) {
|
||||
bool Res = false;
|
||||
|
||||
uint64_t NewBlockCoverage = EF->__sanitizer_get_total_unique_coverage();
|
||||
if (NewBlockCoverage > C->BlockCoverage) {
|
||||
Res = true;
|
||||
C->BlockCoverage = NewBlockCoverage;
|
||||
}
|
||||
|
||||
if (Options.UseIndirCalls &&
|
||||
EF->__sanitizer_get_total_unique_caller_callee_pairs) {
|
||||
uint64_t NewCallerCalleeCoverage =
|
||||
EF->__sanitizer_get_total_unique_caller_callee_pairs();
|
||||
if (NewCallerCalleeCoverage > C->CallerCalleeCoverage) {
|
||||
Res = true;
|
||||
C->CallerCalleeCoverage = NewCallerCalleeCoverage;
|
||||
}
|
||||
}
|
||||
|
||||
if (Options.UseCounters) {
|
||||
uint64_t CounterDelta =
|
||||
EF->__sanitizer_update_counter_bitset_and_clear_counters(
|
||||
C->CounterBitmap.data());
|
||||
if (CounterDelta > 0) {
|
||||
Res = true;
|
||||
C->CounterBitmapBits += CounterDelta;
|
||||
}
|
||||
}
|
||||
|
||||
return Res;
|
||||
}
|
||||
|
||||
// Leak detection is expensive, so we first check if there were more mallocs
|
||||
// than frees (using the sanitizer malloc hooks) and only then try to call lsan.
|
||||
struct MallocFreeTracer {
|
||||
void Start(int TraceLevel) {
|
||||
this->TraceLevel = TraceLevel;
|
||||
if (TraceLevel)
|
||||
Printf("MallocFreeTracer: START\n");
|
||||
Mallocs = 0;
|
||||
Frees = 0;
|
||||
}
|
||||
// Returns true if there were more mallocs than frees.
|
||||
bool Stop() {
|
||||
if (TraceLevel)
|
||||
Printf("MallocFreeTracer: STOP %zd %zd (%s)\n", Mallocs.load(),
|
||||
Frees.load(), Mallocs == Frees ? "same" : "DIFFERENT");
|
||||
bool Result = Mallocs > Frees;
|
||||
Mallocs = 0;
|
||||
Frees = 0;
|
||||
TraceLevel = 0;
|
||||
return Result;
|
||||
}
|
||||
std::atomic<size_t> Mallocs;
|
||||
std::atomic<size_t> Frees;
|
||||
int TraceLevel = 0;
|
||||
};
|
||||
|
||||
static MallocFreeTracer AllocTracer;
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void MallocHook(const volatile void *ptr, size_t size) {
|
||||
size_t N = AllocTracer.Mallocs++;
|
||||
F->HandleMalloc(size);
|
||||
if (int TraceLevel = AllocTracer.TraceLevel) {
|
||||
Printf("MALLOC[%zd] %p %zd\n", N, ptr, size);
|
||||
if (TraceLevel >= 2 && EF)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
}
|
||||
}
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void FreeHook(const volatile void *ptr) {
|
||||
size_t N = AllocTracer.Frees++;
|
||||
if (int TraceLevel = AllocTracer.TraceLevel) {
|
||||
Printf("FREE[%zd] %p\n", N, ptr);
|
||||
if (TraceLevel >= 2 && EF)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
}
|
||||
}
|
||||
|
||||
// Crash on a single malloc that exceeds the rss limit.
|
||||
void Fuzzer::HandleMalloc(size_t Size) {
|
||||
if (!Options.RssLimitMb || (Size >> 20) < (size_t)Options.RssLimitMb)
|
||||
return;
|
||||
Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(),
|
||||
Size);
|
||||
Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");
|
||||
if (EF->__sanitizer_print_stack_trace)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
DumpCurrentUnit("oom-");
|
||||
Printf("SUMMARY: libFuzzer: out-of-memory\n");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.ErrorExitCode); // Stop right now.
|
||||
}
|
||||
|
||||
Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
|
||||
FuzzingOptions Options)
|
||||
: CB(CB), Corpus(Corpus), MD(MD), Options(Options) {
|
||||
SetDeathCallback();
|
||||
InitializeTraceState();
|
||||
assert(!F);
|
||||
F = this;
|
||||
TPC.ResetMaps();
|
||||
ResetCoverage();
|
||||
IsMyThread = true;
|
||||
if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks)
|
||||
EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook);
|
||||
TPC.SetUseCounters(Options.UseCounters);
|
||||
TPC.SetUseValueProfile(Options.UseValueProfile);
|
||||
TPC.SetPrintNewPCs(Options.PrintNewCovPcs);
|
||||
|
||||
if (Options.Verbosity)
|
||||
TPC.PrintModuleInfo();
|
||||
if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec)
|
||||
EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus);
|
||||
MaxInputLen = MaxMutationLen = Options.MaxLen;
|
||||
AllocateCurrentUnitData();
|
||||
CurrentUnitSize = 0;
|
||||
memset(BaseSha1, 0, sizeof(BaseSha1));
|
||||
}
|
||||
|
||||
Fuzzer::~Fuzzer() { }
|
||||
|
||||
void Fuzzer::AllocateCurrentUnitData() {
|
||||
if (CurrentUnitData || MaxInputLen == 0) return;
|
||||
CurrentUnitData = new uint8_t[MaxInputLen];
|
||||
}
|
||||
|
||||
void Fuzzer::SetDeathCallback() {
|
||||
CHECK_EXTERNAL_FUNCTION(__sanitizer_set_death_callback);
|
||||
EF->__sanitizer_set_death_callback(StaticDeathCallback);
|
||||
}
|
||||
|
||||
void Fuzzer::StaticDeathCallback() {
|
||||
assert(F);
|
||||
F->DeathCallback();
|
||||
}
|
||||
|
||||
static void WarnOnUnsuccessfullMerge(bool DoWarn) {
|
||||
if (!DoWarn) return;
|
||||
Printf(
|
||||
"***\n"
|
||||
"***\n"
|
||||
"***\n"
|
||||
"*** NOTE: merge did not succeed due to a failure on one of the inputs.\n"
|
||||
"*** You will need to filter out crashes from the corpus, e.g. like this:\n"
|
||||
"*** for f in WITH_CRASHES/*; do ./fuzzer $f && cp $f NO_CRASHES; done\n"
|
||||
"*** Future versions may have crash-resistant merge, stay tuned.\n"
|
||||
"***\n"
|
||||
"***\n"
|
||||
"***\n");
|
||||
}
|
||||
|
||||
void Fuzzer::DumpCurrentUnit(const char *Prefix) {
|
||||
WarnOnUnsuccessfullMerge(InMergeMode);
|
||||
if (!CurrentUnitData) return; // Happens when running individual inputs.
|
||||
MD.PrintMutationSequence();
|
||||
Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str());
|
||||
size_t UnitSize = CurrentUnitSize;
|
||||
if (UnitSize <= kMaxUnitSizeToPrint) {
|
||||
PrintHexArray(CurrentUnitData, UnitSize, "\n");
|
||||
PrintASCII(CurrentUnitData, UnitSize, "\n");
|
||||
}
|
||||
WriteUnitToFileWithPrefix({CurrentUnitData, CurrentUnitData + UnitSize},
|
||||
Prefix);
|
||||
}
|
||||
|
||||
NO_SANITIZE_MEMORY
|
||||
void Fuzzer::DeathCallback() {
|
||||
DumpCurrentUnit("crash-");
|
||||
PrintFinalStats();
|
||||
}
|
||||
|
||||
void Fuzzer::StaticAlarmCallback() {
|
||||
assert(F);
|
||||
F->AlarmCallback();
|
||||
}
|
||||
|
||||
void Fuzzer::StaticCrashSignalCallback() {
|
||||
assert(F);
|
||||
F->CrashCallback();
|
||||
}
|
||||
|
||||
void Fuzzer::StaticInterruptCallback() {
|
||||
assert(F);
|
||||
F->InterruptCallback();
|
||||
}
|
||||
|
||||
void Fuzzer::CrashCallback() {
|
||||
Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid());
|
||||
if (EF->__sanitizer_print_stack_trace)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
Printf("NOTE: libFuzzer has rudimentary signal handlers.\n"
|
||||
" Combine libFuzzer with AddressSanitizer or similar for better "
|
||||
"crash reports.\n");
|
||||
Printf("SUMMARY: libFuzzer: deadly signal\n");
|
||||
DumpCurrentUnit("crash-");
|
||||
PrintFinalStats();
|
||||
exit(Options.ErrorExitCode);
|
||||
}
|
||||
|
||||
void Fuzzer::InterruptCallback() {
|
||||
Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid());
|
||||
PrintFinalStats();
|
||||
_Exit(0); // Stop right now, don't perform any at-exit actions.
|
||||
}
|
||||
|
||||
NO_SANITIZE_MEMORY
|
||||
void Fuzzer::AlarmCallback() {
|
||||
assert(Options.UnitTimeoutSec > 0);
|
||||
if (!InFuzzingThread()) return;
|
||||
if (!RunningCB)
|
||||
return; // We have not started running units yet.
|
||||
size_t Seconds =
|
||||
duration_cast<seconds>(system_clock::now() - UnitStartTime).count();
|
||||
if (Seconds == 0)
|
||||
return;
|
||||
if (Options.Verbosity >= 2)
|
||||
Printf("AlarmCallback %zd\n", Seconds);
|
||||
if (Seconds >= (size_t)Options.UnitTimeoutSec) {
|
||||
Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds);
|
||||
Printf(" and the timeout value is %d (use -timeout=N to change)\n",
|
||||
Options.UnitTimeoutSec);
|
||||
DumpCurrentUnit("timeout-");
|
||||
Printf("==%lu== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(),
|
||||
Seconds);
|
||||
if (EF->__sanitizer_print_stack_trace)
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
Printf("SUMMARY: libFuzzer: timeout\n");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.TimeoutExitCode); // Stop right now.
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::RssLimitCallback() {
|
||||
Printf(
|
||||
"==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n",
|
||||
GetPid(), GetPeakRSSMb(), Options.RssLimitMb);
|
||||
Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");
|
||||
if (EF->__sanitizer_print_memory_profile)
|
||||
EF->__sanitizer_print_memory_profile(95);
|
||||
DumpCurrentUnit("oom-");
|
||||
Printf("SUMMARY: libFuzzer: out-of-memory\n");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.ErrorExitCode); // Stop right now.
|
||||
}
|
||||
|
||||
void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
|
||||
size_t ExecPerSec = execPerSec();
|
||||
if (Options.OutputCSV) {
|
||||
static bool csvHeaderPrinted = false;
|
||||
if (!csvHeaderPrinted) {
|
||||
csvHeaderPrinted = true;
|
||||
Printf("runs,block_cov,bits,cc_cov,corpus,execs_per_sec,tbms,reason\n");
|
||||
}
|
||||
Printf("%zd,%zd,%zd,%zd,%zd,%zd,%s\n", TotalNumberOfRuns,
|
||||
MaxCoverage.BlockCoverage, MaxCoverage.CounterBitmapBits,
|
||||
MaxCoverage.CallerCalleeCoverage, Corpus.size(), ExecPerSec, Where);
|
||||
}
|
||||
|
||||
if (!Options.Verbosity)
|
||||
return;
|
||||
Printf("#%zd\t%s", TotalNumberOfRuns, Where);
|
||||
if (MaxCoverage.BlockCoverage)
|
||||
Printf(" cov: %zd", MaxCoverage.BlockCoverage);
|
||||
if (size_t N = MaxCoverage.VPMap.GetNumBitsSinceLastMerge())
|
||||
Printf(" vp: %zd", N);
|
||||
if (size_t N = TPC.GetTotalPCCoverage())
|
||||
Printf(" cov: %zd", N);
|
||||
if (auto TB = MaxCoverage.CounterBitmapBits)
|
||||
Printf(" bits: %zd", TB);
|
||||
if (size_t N = Corpus.NumFeatures())
|
||||
Printf( " ft: %zd", N);
|
||||
if (MaxCoverage.CallerCalleeCoverage)
|
||||
Printf(" indir: %zd", MaxCoverage.CallerCalleeCoverage);
|
||||
if (!Corpus.empty()) {
|
||||
Printf(" corp: %zd", Corpus.NumActiveUnits());
|
||||
if (size_t N = Corpus.SizeInBytes()) {
|
||||
if (N < (1<<14))
|
||||
Printf("/%zdb", N);
|
||||
else if (N < (1 << 24))
|
||||
Printf("/%zdKb", N >> 10);
|
||||
else
|
||||
Printf("/%zdMb", N >> 20);
|
||||
}
|
||||
}
|
||||
if (Units)
|
||||
Printf(" units: %zd", Units);
|
||||
|
||||
Printf(" exec/s: %zd", ExecPerSec);
|
||||
Printf(" rss: %zdMb", GetPeakRSSMb());
|
||||
Printf("%s", End);
|
||||
}
|
||||
|
||||
void Fuzzer::PrintFinalStats() {
|
||||
if (Options.PrintCoverage)
|
||||
TPC.PrintCoverage();
|
||||
if (Options.DumpCoverage)
|
||||
TPC.DumpCoverage();
|
||||
if (Options.PrintCorpusStats)
|
||||
Corpus.PrintStats();
|
||||
if (!Options.PrintFinalStats) return;
|
||||
size_t ExecPerSec = execPerSec();
|
||||
Printf("stat::number_of_executed_units: %zd\n", TotalNumberOfRuns);
|
||||
Printf("stat::average_exec_per_sec: %zd\n", ExecPerSec);
|
||||
Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded);
|
||||
Printf("stat::slowest_unit_time_sec: %zd\n", TimeOfLongestUnitInSeconds);
|
||||
Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb());
|
||||
}
|
||||
|
||||
void Fuzzer::SetMaxInputLen(size_t MaxInputLen) {
|
||||
assert(this->MaxInputLen == 0); // Can only reset MaxInputLen from 0 to non-0.
|
||||
assert(MaxInputLen);
|
||||
this->MaxInputLen = MaxInputLen;
|
||||
this->MaxMutationLen = MaxInputLen;
|
||||
AllocateCurrentUnitData();
|
||||
Printf("INFO: -max_len is not provided, using %zd\n", MaxInputLen);
|
||||
}
|
||||
|
||||
void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) {
|
||||
assert(MaxMutationLen && MaxMutationLen <= MaxInputLen);
|
||||
this->MaxMutationLen = MaxMutationLen;
|
||||
}
|
||||
|
||||
void Fuzzer::CheckExitOnSrcPosOrItem() {
|
||||
if (!Options.ExitOnSrcPos.empty()) {
|
||||
static auto *PCsSet = new std::set<uintptr_t>;
|
||||
for (size_t i = 1, N = TPC.GetNumPCs(); i < N; i++) {
|
||||
uintptr_t PC = TPC.GetPC(i);
|
||||
if (!PC) continue;
|
||||
if (!PCsSet->insert(PC).second) continue;
|
||||
std::string Descr = DescribePC("%L", PC);
|
||||
if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) {
|
||||
Printf("INFO: found line matching '%s', exiting.\n",
|
||||
Options.ExitOnSrcPos.c_str());
|
||||
_Exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Options.ExitOnItem.empty()) {
|
||||
if (Corpus.HasUnit(Options.ExitOnItem)) {
|
||||
Printf("INFO: found item with checksum '%s', exiting.\n",
|
||||
Options.ExitOnItem.c_str());
|
||||
_Exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::RereadOutputCorpus(size_t MaxSize) {
|
||||
if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return;
|
||||
std::vector<Unit> AdditionalCorpus;
|
||||
ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus,
|
||||
&EpochOfLastReadOfOutputCorpus, MaxSize,
|
||||
/*ExitOnError*/ false);
|
||||
if (Options.Verbosity >= 2)
|
||||
Printf("Reload: read %zd new units.\n", AdditionalCorpus.size());
|
||||
bool Reloaded = false;
|
||||
for (auto &U : AdditionalCorpus) {
|
||||
if (U.size() > MaxSize)
|
||||
U.resize(MaxSize);
|
||||
if (!Corpus.HasUnit(U)) {
|
||||
if (size_t NumFeatures = RunOne(U)) {
|
||||
CheckExitOnSrcPosOrItem();
|
||||
Corpus.AddToCorpus(U, NumFeatures);
|
||||
Reloaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Reloaded)
|
||||
PrintStats("RELOAD");
|
||||
}
|
||||
|
||||
void Fuzzer::ShuffleCorpus(UnitVector *V) {
|
||||
std::random_shuffle(V->begin(), V->end(), MD.GetRand());
|
||||
if (Options.PreferSmall)
|
||||
std::stable_sort(V->begin(), V->end(), [](const Unit &A, const Unit &B) {
|
||||
return A.size() < B.size();
|
||||
});
|
||||
}
|
||||
|
||||
void Fuzzer::ShuffleAndMinimize(UnitVector *InitialCorpus) {
|
||||
Printf("#0\tREAD units: %zd\n", InitialCorpus->size());
|
||||
if (Options.ShuffleAtStartUp)
|
||||
ShuffleCorpus(InitialCorpus);
|
||||
|
||||
// Test the callback with empty input and never try it again.
|
||||
uint8_t dummy;
|
||||
ExecuteCallback(&dummy, 0);
|
||||
|
||||
for (const auto &U : *InitialCorpus) {
|
||||
if (size_t NumFeatures = RunOne(U)) {
|
||||
CheckExitOnSrcPosOrItem();
|
||||
Corpus.AddToCorpus(U, NumFeatures);
|
||||
if (Options.Verbosity >= 2)
|
||||
Printf("NEW0: %zd L %zd\n", MaxCoverage.BlockCoverage, U.size());
|
||||
}
|
||||
TryDetectingAMemoryLeak(U.data(), U.size(),
|
||||
/*DuringInitialCorpusExecution*/ true);
|
||||
}
|
||||
PrintStats("INITED");
|
||||
if (Corpus.empty()) {
|
||||
Printf("ERROR: no interesting inputs were found. "
|
||||
"Is the code instrumented for coverage? Exiting.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Fuzzer::RunOne(const uint8_t *Data, size_t Size) {
|
||||
if (!Size) return 0;
|
||||
TotalNumberOfRuns++;
|
||||
|
||||
ExecuteCallback(Data, Size);
|
||||
|
||||
size_t Res = 0;
|
||||
if (size_t NumFeatures = TPC.CollectFeatures([&](size_t Feature) -> bool {
|
||||
return Corpus.AddFeature(Feature, Size, Options.Shrink);
|
||||
}))
|
||||
Res = NumFeatures;
|
||||
|
||||
if (!TPC.UsingTracePcGuard()) {
|
||||
if (TPC.UpdateValueProfileMap(&MaxCoverage.VPMap))
|
||||
Res = 1;
|
||||
if (!Res && RecordMaxCoverage(&MaxCoverage))
|
||||
Res = 1;
|
||||
}
|
||||
|
||||
auto TimeOfUnit =
|
||||
duration_cast<seconds>(UnitStopTime - UnitStartTime).count();
|
||||
if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) &&
|
||||
secondsSinceProcessStartUp() >= 2)
|
||||
PrintStats("pulse ");
|
||||
if (TimeOfUnit > TimeOfLongestUnitInSeconds * 1.1 &&
|
||||
TimeOfUnit >= Options.ReportSlowUnits) {
|
||||
TimeOfLongestUnitInSeconds = TimeOfUnit;
|
||||
Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds);
|
||||
WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-");
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const {
|
||||
assert(InFuzzingThread());
|
||||
*Data = CurrentUnitData;
|
||||
return CurrentUnitSize;
|
||||
}
|
||||
|
||||
void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
|
||||
assert(InFuzzingThread());
|
||||
// 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];
|
||||
memcpy(DataCopy, Data, Size);
|
||||
if (CurrentUnitData && CurrentUnitData != Data)
|
||||
memcpy(CurrentUnitData, Data, Size);
|
||||
CurrentUnitSize = Size;
|
||||
AllocTracer.Start(Options.TraceMalloc);
|
||||
UnitStartTime = system_clock::now();
|
||||
ResetCounters(); // Reset coverage right before the callback.
|
||||
TPC.ResetMaps();
|
||||
RunningCB = true;
|
||||
int Res = CB(DataCopy, Size);
|
||||
RunningCB = false;
|
||||
UnitStopTime = system_clock::now();
|
||||
(void)Res;
|
||||
assert(Res == 0);
|
||||
HasMoreMallocsThanFrees = AllocTracer.Stop();
|
||||
CurrentUnitSize = 0;
|
||||
delete[] DataCopy;
|
||||
}
|
||||
|
||||
void Fuzzer::WriteToOutputCorpus(const Unit &U) {
|
||||
if (Options.OnlyASCII)
|
||||
assert(IsASCII(U));
|
||||
if (Options.OutputCorpus.empty())
|
||||
return;
|
||||
std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U));
|
||||
WriteToFile(U, Path);
|
||||
if (Options.Verbosity >= 2)
|
||||
Printf("Written to %s\n", Path.c_str());
|
||||
}
|
||||
|
||||
void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) {
|
||||
if (!Options.SaveArtifacts)
|
||||
return;
|
||||
std::string Path = Options.ArtifactPrefix + Prefix + Hash(U);
|
||||
if (!Options.ExactArtifactPath.empty())
|
||||
Path = Options.ExactArtifactPath; // Overrides ArtifactPrefix.
|
||||
WriteToFile(U, Path);
|
||||
Printf("artifact_prefix='%s'; Test unit written to %s\n",
|
||||
Options.ArtifactPrefix.c_str(), Path.c_str());
|
||||
if (U.size() <= kMaxUnitSizeToPrint)
|
||||
Printf("Base64: %s\n", Base64(U).c_str());
|
||||
}
|
||||
|
||||
void Fuzzer::PrintStatusForNewUnit(const Unit &U) {
|
||||
if (!Options.PrintNEW)
|
||||
return;
|
||||
PrintStats("NEW ", "");
|
||||
if (Options.Verbosity) {
|
||||
Printf(" L: %zd ", U.size());
|
||||
MD.PrintMutationSequence();
|
||||
Printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) {
|
||||
II->NumSuccessfullMutations++;
|
||||
MD.RecordSuccessfulMutationSequence();
|
||||
PrintStatusForNewUnit(U);
|
||||
WriteToOutputCorpus(U);
|
||||
NumberOfNewUnitsAdded++;
|
||||
TPC.PrintNewPCs();
|
||||
}
|
||||
|
||||
// Finds minimal number of units in 'Extra' that add coverage to 'Initial'.
|
||||
// We do it by actually executing the units, sometimes more than once,
|
||||
// because we may be using different coverage-like signals and the only
|
||||
// common thing between them is that we can say "this unit found new stuff".
|
||||
UnitVector Fuzzer::FindExtraUnits(const UnitVector &Initial,
|
||||
const UnitVector &Extra) {
|
||||
UnitVector Res = Extra;
|
||||
UnitVector Tmp;
|
||||
size_t OldSize = Res.size();
|
||||
for (int Iter = 0; Iter < 10; Iter++) {
|
||||
ShuffleCorpus(&Res);
|
||||
TPC.ResetMaps();
|
||||
Corpus.ResetFeatureSet();
|
||||
ResetCoverage();
|
||||
|
||||
for (auto &U : Initial) {
|
||||
TPC.ResetMaps();
|
||||
RunOne(U);
|
||||
}
|
||||
|
||||
Tmp.clear();
|
||||
for (auto &U : Res) {
|
||||
TPC.ResetMaps();
|
||||
if (RunOne(U))
|
||||
Tmp.push_back(U);
|
||||
}
|
||||
|
||||
char Stat[7] = "MIN ";
|
||||
Stat[3] = '0' + Iter;
|
||||
PrintStats(Stat, "\n", Tmp.size());
|
||||
|
||||
size_t NewSize = Tmp.size();
|
||||
assert(NewSize <= OldSize);
|
||||
Res.swap(Tmp);
|
||||
|
||||
if (NewSize + 5 >= OldSize)
|
||||
break;
|
||||
OldSize = NewSize;
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
void Fuzzer::Merge(const std::vector<std::string> &Corpora) {
|
||||
if (Corpora.size() <= 1) {
|
||||
Printf("Merge requires two or more corpus dirs\n");
|
||||
return;
|
||||
}
|
||||
InMergeMode = true;
|
||||
std::vector<std::string> ExtraCorpora(Corpora.begin() + 1, Corpora.end());
|
||||
|
||||
assert(MaxInputLen > 0);
|
||||
UnitVector Initial, Extra;
|
||||
ReadDirToVectorOfUnits(Corpora[0].c_str(), &Initial, nullptr, MaxInputLen,
|
||||
true);
|
||||
for (auto &C : ExtraCorpora)
|
||||
ReadDirToVectorOfUnits(C.c_str(), &Extra, nullptr, MaxInputLen, true);
|
||||
|
||||
if (!Initial.empty()) {
|
||||
Printf("=== Minimizing the initial corpus of %zd units\n", Initial.size());
|
||||
Initial = FindExtraUnits({}, Initial);
|
||||
}
|
||||
|
||||
Printf("=== Merging extra %zd units\n", Extra.size());
|
||||
auto Res = FindExtraUnits(Initial, Extra);
|
||||
|
||||
for (auto &U: Res)
|
||||
WriteToOutputCorpus(U);
|
||||
|
||||
Printf("=== Merge: written %zd units\n", Res.size());
|
||||
}
|
||||
|
||||
// Tries detecting a memory leak on the particular input that we have just
|
||||
// executed before calling this function.
|
||||
void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,
|
||||
bool DuringInitialCorpusExecution) {
|
||||
if (!HasMoreMallocsThanFrees) return; // mallocs==frees, a leak is unlikely.
|
||||
if (!Options.DetectLeaks) return;
|
||||
if (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) ||
|
||||
!(EF->__lsan_do_recoverable_leak_check))
|
||||
return; // No lsan.
|
||||
// Run the target once again, but with lsan disabled so that if there is
|
||||
// a real leak we do not report it twice.
|
||||
EF->__lsan_disable();
|
||||
ExecuteCallback(Data, Size);
|
||||
EF->__lsan_enable();
|
||||
if (!HasMoreMallocsThanFrees) return; // a leak is unlikely.
|
||||
if (NumberOfLeakDetectionAttempts++ > 1000) {
|
||||
Options.DetectLeaks = false;
|
||||
Printf("INFO: libFuzzer disabled leak detection after every mutation.\n"
|
||||
" Most likely the target function accumulates allocated\n"
|
||||
" memory in a global state w/o actually leaking it.\n"
|
||||
" You may try running this binary with -trace_malloc=[12]"
|
||||
" to get a trace of mallocs and frees.\n"
|
||||
" If LeakSanitizer is enabled in this process it will still\n"
|
||||
" run on the process shutdown.\n");
|
||||
return;
|
||||
}
|
||||
// Now perform the actual lsan pass. This is expensive and we must ensure
|
||||
// we don't call it too often.
|
||||
if (EF->__lsan_do_recoverable_leak_check()) { // Leak is found, report it.
|
||||
if (DuringInitialCorpusExecution)
|
||||
Printf("\nINFO: a leak has been found in the initial corpus.\n\n");
|
||||
Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n");
|
||||
CurrentUnitSize = Size;
|
||||
DumpCurrentUnit("leak-");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.ErrorExitCode); // not exit() to disable lsan further on.
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::MutateAndTestOne() {
|
||||
MD.StartMutationSequence();
|
||||
|
||||
auto &II = Corpus.ChooseUnitToMutate(MD.GetRand());
|
||||
const auto &U = II.U;
|
||||
memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1));
|
||||
assert(CurrentUnitData);
|
||||
size_t Size = U.size();
|
||||
assert(Size <= MaxInputLen && "Oversized Unit");
|
||||
memcpy(CurrentUnitData, U.data(), Size);
|
||||
|
||||
assert(MaxMutationLen > 0);
|
||||
|
||||
for (int i = 0; i < Options.MutateDepth; i++) {
|
||||
if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
|
||||
break;
|
||||
size_t NewSize = 0;
|
||||
NewSize = MD.Mutate(CurrentUnitData, Size, MaxMutationLen);
|
||||
assert(NewSize > 0 && "Mutator returned empty unit");
|
||||
assert(NewSize <= MaxMutationLen && "Mutator return overisized unit");
|
||||
Size = NewSize;
|
||||
if (i == 0)
|
||||
StartTraceRecording();
|
||||
II.NumExecutedMutations++;
|
||||
if (size_t NumFeatures = RunOne(CurrentUnitData, Size)) {
|
||||
Corpus.AddToCorpus({CurrentUnitData, CurrentUnitData + Size}, NumFeatures,
|
||||
/*MayDeleteFile=*/true);
|
||||
ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size});
|
||||
CheckExitOnSrcPosOrItem();
|
||||
}
|
||||
StopTraceRecording();
|
||||
TryDetectingAMemoryLeak(CurrentUnitData, Size,
|
||||
/*DuringInitialCorpusExecution*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::ResetCoverage() {
|
||||
ResetEdgeCoverage();
|
||||
MaxCoverage.Reset();
|
||||
PrepareCounters(&MaxCoverage);
|
||||
}
|
||||
|
||||
void Fuzzer::Loop() {
|
||||
system_clock::time_point LastCorpusReload = system_clock::now();
|
||||
if (Options.DoCrossOver)
|
||||
MD.SetCorpus(&Corpus);
|
||||
while (true) {
|
||||
auto Now = system_clock::now();
|
||||
if (duration_cast<seconds>(Now - LastCorpusReload).count() >=
|
||||
Options.ReloadIntervalSec) {
|
||||
RereadOutputCorpus(MaxInputLen);
|
||||
LastCorpusReload = system_clock::now();
|
||||
}
|
||||
if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
|
||||
break;
|
||||
if (TimedOut()) break;
|
||||
// Perform several mutations and runs.
|
||||
MutateAndTestOne();
|
||||
}
|
||||
|
||||
PrintStats("DONE ", "\n");
|
||||
MD.PrintRecommendedDictionary();
|
||||
}
|
||||
|
||||
void Fuzzer::MinimizeCrashLoop(const Unit &U) {
|
||||
if (U.size() <= 2) return;
|
||||
while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) {
|
||||
MD.StartMutationSequence();
|
||||
memcpy(CurrentUnitData, U.data(), U.size());
|
||||
for (int i = 0; i < Options.MutateDepth; i++) {
|
||||
size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen);
|
||||
assert(NewSize > 0 && NewSize <= MaxMutationLen);
|
||||
RunOne(CurrentUnitData, NewSize);
|
||||
TryDetectingAMemoryLeak(CurrentUnitData, NewSize,
|
||||
/*DuringInitialCorpusExecution*/ false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
extern "C" {
|
||||
|
||||
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
assert(fuzzer::F);
|
||||
return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize);
|
||||
}
|
||||
} // extern "C"
|
21
test/fuzz_test/Fuzzer/FuzzerMain.cpp
Normal file
21
test/fuzz_test/Fuzzer/FuzzerMain.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
//===- FuzzerMain.cpp - main() function and flags -------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// main() and flags.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
extern "C" {
|
||||
// This function should be defined by the user.
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||
} // extern "C"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return fuzzer::FuzzerDriver(&argc, &argv, LLVMFuzzerTestOneInput);
|
||||
}
|
261
test/fuzz_test/Fuzzer/FuzzerMerge.cpp
Normal file
261
test/fuzz_test/Fuzzer/FuzzerMerge.cpp
Normal file
|
@ -0,0 +1,261 @@
|
|||
//===- FuzzerMerge.cpp - merging corpora ----------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Merging corpora.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerMerge.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include "FuzzerUtil.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
bool Merger::Parse(const std::string &Str, bool ParseCoverage) {
|
||||
std::istringstream SS(Str);
|
||||
return Parse(SS, ParseCoverage);
|
||||
}
|
||||
|
||||
void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) {
|
||||
if (!Parse(IS, ParseCoverage)) {
|
||||
Printf("MERGE: failed to parse the control file (unexpected error)\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// The control file example:
|
||||
//
|
||||
// 3 # The number of inputs
|
||||
// 1 # The number of inputs in the first corpus, <= the previous number
|
||||
// file0
|
||||
// file1
|
||||
// file2 # One file name per line.
|
||||
// STARTED 0 123 # FileID, file size
|
||||
// DONE 0 1 4 6 8 # FileID COV1 COV2 ...
|
||||
// STARTED 1 456 # If DONE is missing, the input crashed while processing.
|
||||
// STARTED 2 567
|
||||
// DONE 2 8 9
|
||||
bool Merger::Parse(std::istream &IS, bool ParseCoverage) {
|
||||
LastFailure.clear();
|
||||
std::string Line;
|
||||
|
||||
// Parse NumFiles.
|
||||
if (!std::getline(IS, Line, '\n')) return false;
|
||||
std::istringstream L1(Line);
|
||||
size_t NumFiles = 0;
|
||||
L1 >> NumFiles;
|
||||
if (NumFiles == 0 || NumFiles > 10000000) return false;
|
||||
|
||||
// Parse NumFilesInFirstCorpus.
|
||||
if (!std::getline(IS, Line, '\n')) return false;
|
||||
std::istringstream L2(Line);
|
||||
NumFilesInFirstCorpus = NumFiles + 1;
|
||||
L2 >> NumFilesInFirstCorpus;
|
||||
if (NumFilesInFirstCorpus > NumFiles) return false;
|
||||
|
||||
// Parse file names.
|
||||
Files.resize(NumFiles);
|
||||
for (size_t i = 0; i < NumFiles; i++)
|
||||
if (!std::getline(IS, Files[i].Name, '\n'))
|
||||
return false;
|
||||
|
||||
// Parse STARTED and DONE lines.
|
||||
size_t ExpectedStartMarker = 0;
|
||||
const size_t kInvalidStartMarker = -1;
|
||||
size_t LastSeenStartMarker = kInvalidStartMarker;
|
||||
while (std::getline(IS, Line, '\n')) {
|
||||
std::istringstream ISS1(Line);
|
||||
std::string Marker;
|
||||
size_t N;
|
||||
ISS1 >> Marker;
|
||||
ISS1 >> N;
|
||||
if (Marker == "STARTED") {
|
||||
// STARTED FILE_ID FILE_SIZE
|
||||
if (ExpectedStartMarker != N)
|
||||
return false;
|
||||
ISS1 >> Files[ExpectedStartMarker].Size;
|
||||
LastSeenStartMarker = ExpectedStartMarker;
|
||||
assert(ExpectedStartMarker < Files.size());
|
||||
ExpectedStartMarker++;
|
||||
} else if (Marker == "DONE") {
|
||||
// DONE FILE_SIZE COV1 COV2 COV3 ...
|
||||
size_t CurrentFileIdx = N;
|
||||
if (CurrentFileIdx != LastSeenStartMarker)
|
||||
return false;
|
||||
LastSeenStartMarker = kInvalidStartMarker;
|
||||
if (ParseCoverage) {
|
||||
auto &V = Files[CurrentFileIdx].Features;
|
||||
V.clear();
|
||||
while (ISS1 >> std::hex >> N)
|
||||
V.push_back(N);
|
||||
std::sort(V.begin(), V.end());
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (LastSeenStartMarker != kInvalidStartMarker)
|
||||
LastFailure = Files[LastSeenStartMarker].Name;
|
||||
|
||||
FirstNotProcessedFile = ExpectedStartMarker;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Decides which files need to be merged (add thost to NewFiles).
|
||||
// Returns the number of new features added.
|
||||
size_t Merger::Merge(std::vector<std::string> *NewFiles) {
|
||||
NewFiles->clear();
|
||||
assert(NumFilesInFirstCorpus <= Files.size());
|
||||
std::set<uint32_t> AllFeatures;
|
||||
|
||||
// What features are in the initial corpus?
|
||||
for (size_t i = 0; i < NumFilesInFirstCorpus; i++) {
|
||||
auto &Cur = Files[i].Features;
|
||||
AllFeatures.insert(Cur.begin(), Cur.end());
|
||||
}
|
||||
size_t InitialNumFeatures = AllFeatures.size();
|
||||
|
||||
// Remove all features that we already know from all other inputs.
|
||||
for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
|
||||
auto &Cur = Files[i].Features;
|
||||
std::vector<uint32_t> Tmp;
|
||||
std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(),
|
||||
AllFeatures.end(), std::inserter(Tmp, Tmp.begin()));
|
||||
Cur.swap(Tmp);
|
||||
}
|
||||
|
||||
// Sort. Give preference to
|
||||
// * smaller files
|
||||
// * files with more features.
|
||||
std::sort(Files.begin() + NumFilesInFirstCorpus, Files.end(),
|
||||
[&](const MergeFileInfo &a, const MergeFileInfo &b) -> bool {
|
||||
if (a.Size != b.Size)
|
||||
return a.Size < b.Size;
|
||||
return a.Features.size() > b.Features.size();
|
||||
});
|
||||
|
||||
// One greedy pass: add the file's features to AllFeatures.
|
||||
// If new features were added, add this file to NewFiles.
|
||||
for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
|
||||
auto &Cur = Files[i].Features;
|
||||
// Printf("%s -> sz %zd ft %zd\n", Files[i].Name.c_str(),
|
||||
// Files[i].Size, Cur.size());
|
||||
size_t OldSize = AllFeatures.size();
|
||||
AllFeatures.insert(Cur.begin(), Cur.end());
|
||||
if (AllFeatures.size() > OldSize)
|
||||
NewFiles->push_back(Files[i].Name);
|
||||
}
|
||||
return AllFeatures.size() - InitialNumFeatures;
|
||||
}
|
||||
|
||||
// Inner process. May crash if the target crashes.
|
||||
void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
|
||||
Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str());
|
||||
Merger M;
|
||||
std::ifstream IF(CFPath);
|
||||
M.ParseOrExit(IF, false);
|
||||
IF.close();
|
||||
if (!M.LastFailure.empty())
|
||||
Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n",
|
||||
M.LastFailure.c_str());
|
||||
|
||||
Printf("MERGE-INNER: %zd total files;"
|
||||
" %zd processed earlier; will process %zd files now\n",
|
||||
M.Files.size(), M.FirstNotProcessedFile,
|
||||
M.Files.size() - M.FirstNotProcessedFile);
|
||||
|
||||
std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app);
|
||||
for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) {
|
||||
auto U = FileToVector(M.Files[i].Name);
|
||||
if (U.size() > MaxInputLen) {
|
||||
U.resize(MaxInputLen);
|
||||
U.shrink_to_fit();
|
||||
}
|
||||
std::ostringstream StartedLine;
|
||||
// Write the pre-run marker.
|
||||
OF << "STARTED " << std::dec << i << " " << U.size() << "\n";
|
||||
OF.flush(); // Flush is important since ExecuteCommand may crash.
|
||||
// Run.
|
||||
TPC.ResetMaps();
|
||||
ExecuteCallback(U.data(), U.size());
|
||||
// Collect coverage.
|
||||
std::set<size_t> Features;
|
||||
TPC.CollectFeatures([&](size_t Feature) -> bool {
|
||||
Features.insert(Feature);
|
||||
return true;
|
||||
});
|
||||
// Show stats.
|
||||
TotalNumberOfRuns++;
|
||||
if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)))
|
||||
PrintStats("pulse ");
|
||||
// Write the post-run marker and the coverage.
|
||||
OF << "DONE " << i;
|
||||
for (size_t F : Features)
|
||||
OF << " " << std::hex << F;
|
||||
OF << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Outer process. Does not call the target code and thus sohuld not fail.
|
||||
void Fuzzer::CrashResistantMerge(const std::vector<std::string> &Args,
|
||||
const std::vector<std::string> &Corpora) {
|
||||
if (Corpora.size() <= 1) {
|
||||
Printf("Merge requires two or more corpus dirs\n");
|
||||
return;
|
||||
}
|
||||
std::vector<std::string> AllFiles;
|
||||
ListFilesInDirRecursive(Corpora[0], nullptr, &AllFiles, /*TopDir*/true);
|
||||
size_t NumFilesInFirstCorpus = AllFiles.size();
|
||||
for (size_t i = 1; i < Corpora.size(); i++)
|
||||
ListFilesInDirRecursive(Corpora[i], nullptr, &AllFiles, /*TopDir*/true);
|
||||
Printf("MERGE-OUTER: %zd files, %zd in the initial corpus\n",
|
||||
AllFiles.size(), NumFilesInFirstCorpus);
|
||||
std::string CFPath =
|
||||
"libFuzzerTemp." + std::to_string(GetPid()) + ".txt";
|
||||
// Write the control file.
|
||||
RemoveFile(CFPath);
|
||||
std::ofstream ControlFile(CFPath);
|
||||
ControlFile << AllFiles.size() << "\n";
|
||||
ControlFile << NumFilesInFirstCorpus << "\n";
|
||||
for (auto &Path: AllFiles)
|
||||
ControlFile << Path << "\n";
|
||||
ControlFile.close();
|
||||
|
||||
// Execute the inner process untill it passes.
|
||||
// Every inner process should execute at least one input.
|
||||
std::string BaseCmd = CloneArgsWithoutX(Args, "keep-all-flags");
|
||||
for (size_t i = 1; i <= AllFiles.size(); i++) {
|
||||
Printf("MERGE-OUTER: attempt %zd\n", i);
|
||||
auto ExitCode =
|
||||
ExecuteCommand(BaseCmd + " -merge_control_file=" + CFPath);
|
||||
if (!ExitCode) {
|
||||
Printf("MERGE-OUTER: succesfull in %zd attempt(s)\n", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Read the control file and do the merge.
|
||||
Merger M;
|
||||
std::ifstream IF(CFPath);
|
||||
M.ParseOrExit(IF, true);
|
||||
IF.close();
|
||||
std::vector<std::string> NewFiles;
|
||||
size_t NumNewFeatures = M.Merge(&NewFiles);
|
||||
Printf("MERGE-OUTER: %zd new files with %zd new features added\n",
|
||||
NewFiles.size(), NumNewFeatures);
|
||||
for (auto &F: NewFiles)
|
||||
WriteToOutputCorpus(FileToVector(F));
|
||||
// We are done, delete the control file.
|
||||
RemoveFile(CFPath);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
70
test/fuzz_test/Fuzzer/FuzzerMerge.h
Normal file
70
test/fuzz_test/Fuzzer/FuzzerMerge.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
//===- FuzzerMerge.h - merging corpa ----------------------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Merging Corpora.
|
||||
//
|
||||
// The task:
|
||||
// Take the existing corpus (possibly empty) and merge new inputs into
|
||||
// it so that only inputs with new coverage ('features') are added.
|
||||
// The process should tolerate the crashes, OOMs, leaks, etc.
|
||||
//
|
||||
// Algorithm:
|
||||
// The outter process collects the set of files and writes their names
|
||||
// into a temporary "control" file, then repeatedly launches the inner
|
||||
// process until all inputs are processed.
|
||||
// The outer process does not actually execute the target code.
|
||||
//
|
||||
// The inner process reads the control file and sees a) list of all the inputs
|
||||
// and b) the last processed input. Then it starts processing the inputs one
|
||||
// by one. Before processing every input it writes one line to control file:
|
||||
// STARTED INPUT_ID INPUT_SIZE
|
||||
// After processing an input it write another line:
|
||||
// DONE INPUT_ID Feature1 Feature2 Feature3 ...
|
||||
// If a crash happens while processing an input the last line in the control
|
||||
// file will be "STARTED INPUT_ID" and so the next process will know
|
||||
// where to resume.
|
||||
//
|
||||
// Once all inputs are processed by the innner process(es) the outer process
|
||||
// reads the control files and does the merge based entirely on the contents
|
||||
// of control file.
|
||||
// It uses a single pass greedy algorithm choosing first the smallest inputs
|
||||
// within the same size the inputs that have more new features.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_MERGE_H
|
||||
#define LLVM_FUZZER_MERGE_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
#include <istream>
|
||||
#include <set>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
struct MergeFileInfo {
|
||||
std::string Name;
|
||||
size_t Size = 0;
|
||||
std::vector<uint32_t> Features;
|
||||
};
|
||||
|
||||
struct Merger {
|
||||
std::vector<MergeFileInfo> Files;
|
||||
size_t NumFilesInFirstCorpus = 0;
|
||||
size_t FirstNotProcessedFile = 0;
|
||||
std::string LastFailure;
|
||||
|
||||
bool Parse(std::istream &IS, bool ParseCoverage);
|
||||
bool Parse(const std::string &Str, bool ParseCoverage);
|
||||
void ParseOrExit(std::istream &IS, bool ParseCoverage);
|
||||
size_t Merge(std::vector<std::string> *NewFiles);
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_MERGE_H
|
527
test/fuzz_test/Fuzzer/FuzzerMutate.cpp
Normal file
527
test/fuzz_test/Fuzzer/FuzzerMutate.cpp
Normal file
|
@ -0,0 +1,527 @@
|
|||
//===- FuzzerMutate.cpp - Mutate a test input -----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Mutate a test input.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerOptions.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
const size_t Dictionary::kMaxDictSize;
|
||||
|
||||
static void PrintASCII(const Word &W, const char *PrintAfter) {
|
||||
PrintASCII(W.data(), W.size(), PrintAfter);
|
||||
}
|
||||
|
||||
MutationDispatcher::MutationDispatcher(Random &Rand,
|
||||
const FuzzingOptions &Options)
|
||||
: Rand(Rand), Options(Options) {
|
||||
DefaultMutators.insert(
|
||||
DefaultMutators.begin(),
|
||||
{
|
||||
{&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"},
|
||||
{&MutationDispatcher::Mutate_InsertByte, "InsertByte"},
|
||||
{&MutationDispatcher::Mutate_InsertRepeatedBytes,
|
||||
"InsertRepeatedBytes"},
|
||||
{&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"},
|
||||
{&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"},
|
||||
{&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"},
|
||||
{&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"},
|
||||
{&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"},
|
||||
{&MutationDispatcher::Mutate_CopyPart, "CopyPart"},
|
||||
{&MutationDispatcher::Mutate_CrossOver, "CrossOver"},
|
||||
{&MutationDispatcher::Mutate_AddWordFromManualDictionary,
|
||||
"ManualDict"},
|
||||
{&MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary,
|
||||
"TempAutoDict"},
|
||||
{&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
|
||||
"PersAutoDict"},
|
||||
});
|
||||
if(Options.UseCmp)
|
||||
DefaultMutators.push_back(
|
||||
{&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
|
||||
|
||||
if (EF->LLVMFuzzerCustomMutator)
|
||||
Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
|
||||
else
|
||||
Mutators = DefaultMutators;
|
||||
|
||||
if (EF->LLVMFuzzerCustomCrossOver)
|
||||
Mutators.push_back(
|
||||
{&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"});
|
||||
}
|
||||
|
||||
static char RandCh(Random &Rand) {
|
||||
if (Rand.RandBool()) return Rand(256);
|
||||
const char *Special = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00";
|
||||
return Special[Rand(sizeof(Special) - 1)];
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand());
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (!Corpus || Corpus->size() < 2 || Size == 0)
|
||||
return 0;
|
||||
size_t Idx = Rand(Corpus->size());
|
||||
const Unit &Other = (*Corpus)[Idx];
|
||||
if (Other.empty())
|
||||
return 0;
|
||||
MutateInPlaceHere.resize(MaxSize);
|
||||
auto &U = MutateInPlaceHere;
|
||||
size_t NewSize = EF->LLVMFuzzerCustomCrossOver(
|
||||
Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand());
|
||||
if (!NewSize)
|
||||
return 0;
|
||||
assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit");
|
||||
memcpy(Data, U.data(), NewSize);
|
||||
return NewSize;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
assert(Size);
|
||||
size_t ShuffleAmount =
|
||||
Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size.
|
||||
size_t ShuffleStart = Rand(Size - ShuffleAmount);
|
||||
assert(ShuffleStart + ShuffleAmount <= Size);
|
||||
std::random_shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount,
|
||||
Rand);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
assert(Size);
|
||||
if (Size == 1) return 0;
|
||||
size_t N = Rand(Size / 2) + 1;
|
||||
assert(N < Size);
|
||||
size_t Idx = Rand(Size - N + 1);
|
||||
// Erase Data[Idx:Idx+N].
|
||||
memmove(Data + Idx, Data + Idx + N, Size - Idx - N);
|
||||
// Printf("Erase: %zd %zd => %zd; Idx %zd\n", N, Size, Size - N, Idx);
|
||||
return Size - N;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size >= MaxSize) return 0;
|
||||
size_t Idx = Rand(Size + 1);
|
||||
// Insert new value at Data[Idx].
|
||||
memmove(Data + Idx + 1, Data + Idx, Size - Idx);
|
||||
Data[Idx] = RandCh(Rand);
|
||||
return Size + 1;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
const size_t kMinBytesToInsert = 3;
|
||||
if (Size + kMinBytesToInsert >= MaxSize) return 0;
|
||||
size_t MaxBytesToInsert = std::min(MaxSize - Size, (size_t)128);
|
||||
size_t N = Rand(MaxBytesToInsert - kMinBytesToInsert + 1) + kMinBytesToInsert;
|
||||
assert(Size + N <= MaxSize && N);
|
||||
size_t Idx = Rand(Size + 1);
|
||||
// Insert new values at Data[Idx].
|
||||
memmove(Data + Idx + N, Data + Idx, Size - Idx);
|
||||
// Give preference to 0x00 and 0xff.
|
||||
uint8_t Byte = Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255);
|
||||
for (size_t i = 0; i < N; i++)
|
||||
Data[Idx + i] = Byte;
|
||||
return Size + N;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
size_t Idx = Rand(Size);
|
||||
Data[Idx] = RandCh(Rand);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
size_t Idx = Rand(Size);
|
||||
Data[Idx] ^= 1 << Rand(8);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary(
|
||||
uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
return AddWordFromDictionary(TempAutoDictionary, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize,
|
||||
DictionaryEntry &DE) {
|
||||
const Word &W = DE.GetW();
|
||||
bool UsePositionHint = DE.HasPositionHint() &&
|
||||
DE.GetPositionHint() + W.size() < Size &&
|
||||
Rand.RandBool();
|
||||
if (Rand.RandBool()) { // Insert W.
|
||||
if (Size + W.size() > MaxSize) return 0;
|
||||
size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1);
|
||||
memmove(Data + Idx + W.size(), Data + Idx, Size - Idx);
|
||||
memcpy(Data + Idx, W.data(), W.size());
|
||||
Size += W.size();
|
||||
} else { // Overwrite some bytes with W.
|
||||
if (W.size() > Size) return 0;
|
||||
size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size - W.size());
|
||||
memcpy(Data + Idx, W.data(), W.size());
|
||||
}
|
||||
return Size;
|
||||
}
|
||||
|
||||
// Somewhere in the past we have observed a comparison instructions
|
||||
// with arguments Arg1 Arg2. This function tries to guess a dictionary
|
||||
// entry that will satisfy that comparison.
|
||||
// It first tries to find one of the arguments (possibly swapped) in the
|
||||
// input and if it succeeds it creates a DE with a position hint.
|
||||
// Otherwise it creates a DE with one of the arguments w/o a position hint.
|
||||
template <class T>
|
||||
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||
T Arg1, T Arg2, const uint8_t *Data, size_t Size) {
|
||||
ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem;
|
||||
bool HandleFirst = Rand.RandBool();
|
||||
T ExistingBytes, DesiredBytes;
|
||||
Word W;
|
||||
const uint8_t *End = Data + Size;
|
||||
for (int Arg = 0; Arg < 2; Arg++) {
|
||||
ExistingBytes = HandleFirst ? Arg1 : Arg2;
|
||||
DesiredBytes = HandleFirst ? Arg2 : Arg1;
|
||||
DesiredBytes += Rand(-1, 1);
|
||||
if (Rand.RandBool()) ExistingBytes = Bswap(ExistingBytes);
|
||||
if (Rand.RandBool()) DesiredBytes = Bswap(DesiredBytes);
|
||||
HandleFirst = !HandleFirst;
|
||||
W.Set(reinterpret_cast<uint8_t*>(&DesiredBytes), sizeof(T));
|
||||
const size_t kMaxNumPositions = 8;
|
||||
size_t Positions[kMaxNumPositions];
|
||||
size_t NumPositions = 0;
|
||||
for (const uint8_t *Cur = Data;
|
||||
Cur < End && NumPositions < kMaxNumPositions; Cur++) {
|
||||
Cur = (uint8_t *)SearchMemory(Cur, End - Cur, &ExistingBytes, sizeof(T));
|
||||
if (!Cur) break;
|
||||
Positions[NumPositions++] = Cur - Data;
|
||||
}
|
||||
if (!NumPositions) break;
|
||||
return DictionaryEntry(W, Positions[Rand(NumPositions)]);
|
||||
}
|
||||
DictionaryEntry DE(W);
|
||||
return DE;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromTORC(
|
||||
uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
Word W;
|
||||
DictionaryEntry DE;
|
||||
if (Rand.RandBool()) {
|
||||
auto X = TPC.TORC8.Get(Rand.Rand());
|
||||
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||
} else {
|
||||
auto X = TPC.TORC4.Get(Rand.Rand());
|
||||
if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool())
|
||||
DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data,
|
||||
Size);
|
||||
else
|
||||
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||
}
|
||||
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
|
||||
if (!Size) return 0;
|
||||
DictionaryEntry &DERef =
|
||||
CmpDictionaryEntriesDeque[CmpDictionaryEntriesDequeIdx++ %
|
||||
kCmpDictionaryEntriesDequeSize];
|
||||
DERef = DE;
|
||||
CurrentDictionaryEntrySequence.push_back(&DERef);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary(
|
||||
uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::AddWordFromDictionary(Dictionary &D, uint8_t *Data,
|
||||
size_t Size, size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
if (D.empty()) return 0;
|
||||
DictionaryEntry &DE = D[Rand(D.size())];
|
||||
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
|
||||
if (!Size) return 0;
|
||||
DE.IncUseCount();
|
||||
CurrentDictionaryEntrySequence.push_back(&DE);
|
||||
return Size;
|
||||
}
|
||||
|
||||
// Overwrites part of To[0,ToSize) with a part of From[0,FromSize).
|
||||
// Returns ToSize.
|
||||
size_t MutationDispatcher::CopyPartOf(const uint8_t *From, size_t FromSize,
|
||||
uint8_t *To, size_t ToSize) {
|
||||
// Copy From[FromBeg, FromBeg + CopySize) into To[ToBeg, ToBeg + CopySize).
|
||||
size_t ToBeg = Rand(ToSize);
|
||||
size_t CopySize = Rand(ToSize - ToBeg) + 1;
|
||||
assert(ToBeg + CopySize <= ToSize);
|
||||
CopySize = std::min(CopySize, FromSize);
|
||||
size_t FromBeg = Rand(FromSize - CopySize + 1);
|
||||
assert(FromBeg + CopySize <= FromSize);
|
||||
memmove(To + ToBeg, From + FromBeg, CopySize);
|
||||
return ToSize;
|
||||
}
|
||||
|
||||
// Inserts part of From[0,ToSize) into To.
|
||||
// Returns new size of To on success or 0 on failure.
|
||||
size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize,
|
||||
uint8_t *To, size_t ToSize,
|
||||
size_t MaxToSize) {
|
||||
if (ToSize >= MaxToSize) return 0;
|
||||
size_t AvailableSpace = MaxToSize - ToSize;
|
||||
size_t MaxCopySize = std::min(AvailableSpace, FromSize);
|
||||
size_t CopySize = Rand(MaxCopySize) + 1;
|
||||
size_t FromBeg = Rand(FromSize - CopySize + 1);
|
||||
assert(FromBeg + CopySize <= FromSize);
|
||||
size_t ToInsertPos = Rand(ToSize + 1);
|
||||
assert(ToInsertPos + CopySize <= MaxToSize);
|
||||
size_t TailSize = ToSize - ToInsertPos;
|
||||
if (To == From) {
|
||||
MutateInPlaceHere.resize(MaxToSize);
|
||||
memcpy(MutateInPlaceHere.data(), From + FromBeg, CopySize);
|
||||
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
|
||||
memmove(To + ToInsertPos, MutateInPlaceHere.data(), CopySize);
|
||||
} else {
|
||||
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
|
||||
memmove(To + ToInsertPos, From + FromBeg, CopySize);
|
||||
}
|
||||
return ToSize + CopySize;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
if (Rand.RandBool())
|
||||
return CopyPartOf(Data, Size, Data, Size);
|
||||
else
|
||||
return InsertPartOf(Data, Size, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
size_t B = Rand(Size);
|
||||
while (B < Size && !isdigit(Data[B])) B++;
|
||||
if (B == Size) return 0;
|
||||
size_t E = B;
|
||||
while (E < Size && isdigit(Data[E])) E++;
|
||||
assert(B < E);
|
||||
// now we have digits in [B, E).
|
||||
// strtol and friends don't accept non-zero-teminated data, parse it manually.
|
||||
uint64_t Val = Data[B] - '0';
|
||||
for (size_t i = B + 1; i < E; i++)
|
||||
Val = Val * 10 + Data[i] - '0';
|
||||
|
||||
// Mutate the integer value.
|
||||
switch(Rand(5)) {
|
||||
case 0: Val++; break;
|
||||
case 1: Val--; break;
|
||||
case 2: Val /= 2; break;
|
||||
case 3: Val *= 2; break;
|
||||
case 4: Val = Rand(Val * Val); break;
|
||||
default: assert(0);
|
||||
}
|
||||
// Just replace the bytes with the new ones, don't bother moving bytes.
|
||||
for (size_t i = B; i < E; i++) {
|
||||
size_t Idx = E + B - i - 1;
|
||||
assert(Idx >= B && Idx < E);
|
||||
Data[Idx] = (Val % 10) + '0';
|
||||
Val /= 10;
|
||||
}
|
||||
return Size;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) {
|
||||
if (Size < sizeof(T)) return 0;
|
||||
size_t Off = Rand(Size - sizeof(T) + 1);
|
||||
assert(Off + sizeof(T) <= Size);
|
||||
T Val;
|
||||
if (Off < 64 && !Rand(4)) {
|
||||
Val = Size;
|
||||
if (Rand.RandBool())
|
||||
Val = Bswap(Val);
|
||||
} else {
|
||||
memcpy(&Val, Data + Off, sizeof(Val));
|
||||
T Add = Rand(21);
|
||||
Add -= 10;
|
||||
if (Rand.RandBool())
|
||||
Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes.
|
||||
else
|
||||
Val = Val + Add; // Add assuming current endiannes.
|
||||
if (Add == 0 || Rand.RandBool()) // Maybe negate.
|
||||
Val = -Val;
|
||||
}
|
||||
memcpy(Data + Off, &Val, sizeof(Val));
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
switch (Rand(4)) {
|
||||
case 3: return ChangeBinaryInteger<uint64_t>(Data, Size, Rand);
|
||||
case 2: return ChangeBinaryInteger<uint32_t>(Data, Size, Rand);
|
||||
case 1: return ChangeBinaryInteger<uint16_t>(Data, Size, Rand);
|
||||
case 0: return ChangeBinaryInteger<uint8_t>(Data, Size, Rand);
|
||||
default: assert(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
if (!Corpus || Corpus->size() < 2 || Size == 0) return 0;
|
||||
size_t Idx = Rand(Corpus->size());
|
||||
const Unit &O = (*Corpus)[Idx];
|
||||
if (O.empty()) return 0;
|
||||
MutateInPlaceHere.resize(MaxSize);
|
||||
auto &U = MutateInPlaceHere;
|
||||
size_t NewSize = 0;
|
||||
switch(Rand(3)) {
|
||||
case 0:
|
||||
NewSize = CrossOver(Data, Size, O.data(), O.size(), U.data(), U.size());
|
||||
break;
|
||||
case 1:
|
||||
NewSize = InsertPartOf(O.data(), O.size(), U.data(), U.size(), MaxSize);
|
||||
if (NewSize)
|
||||
break;
|
||||
// LLVM_FALLTHROUGH;
|
||||
case 2:
|
||||
NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size());
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
assert(NewSize > 0 && "CrossOver returned empty unit");
|
||||
assert(NewSize <= MaxSize && "CrossOver returned overisized unit");
|
||||
memcpy(Data, U.data(), NewSize);
|
||||
return NewSize;
|
||||
}
|
||||
|
||||
void MutationDispatcher::StartMutationSequence() {
|
||||
CurrentMutatorSequence.clear();
|
||||
CurrentDictionaryEntrySequence.clear();
|
||||
}
|
||||
|
||||
// Copy successful dictionary entries to PersistentAutoDictionary.
|
||||
void MutationDispatcher::RecordSuccessfulMutationSequence() {
|
||||
for (auto DE : CurrentDictionaryEntrySequence) {
|
||||
// PersistentAutoDictionary.AddWithSuccessCountOne(DE);
|
||||
DE->IncSuccessCount();
|
||||
// Linear search is fine here as this happens seldom.
|
||||
if (!PersistentAutoDictionary.ContainsWord(DE->GetW()))
|
||||
PersistentAutoDictionary.push_back({DE->GetW(), 1});
|
||||
}
|
||||
}
|
||||
|
||||
void MutationDispatcher::PrintRecommendedDictionary() {
|
||||
std::vector<DictionaryEntry> V;
|
||||
for (auto &DE : PersistentAutoDictionary)
|
||||
if (!ManualDictionary.ContainsWord(DE.GetW()))
|
||||
V.push_back(DE);
|
||||
if (V.empty()) return;
|
||||
Printf("###### Recommended dictionary. ######\n");
|
||||
for (auto &DE: V) {
|
||||
Printf("\"");
|
||||
PrintASCII(DE.GetW(), "\"");
|
||||
Printf(" # Uses: %zd\n", DE.GetUseCount());
|
||||
}
|
||||
Printf("###### End of recommended dictionary. ######\n");
|
||||
}
|
||||
|
||||
void MutationDispatcher::PrintMutationSequence() {
|
||||
Printf("MS: %zd ", CurrentMutatorSequence.size());
|
||||
for (auto M : CurrentMutatorSequence)
|
||||
Printf("%s-", M.Name);
|
||||
if (!CurrentDictionaryEntrySequence.empty()) {
|
||||
Printf(" DE: ");
|
||||
for (auto DE : CurrentDictionaryEntrySequence) {
|
||||
Printf("\"");
|
||||
PrintASCII(DE->GetW(), "\"-");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
return MutateImpl(Data, Size, MaxSize, Mutators);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
return MutateImpl(Data, Size, MaxSize, DefaultMutators);
|
||||
}
|
||||
|
||||
// Mutates Data in place, returns new size.
|
||||
size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize,
|
||||
const std::vector<Mutator> &Mutators) {
|
||||
assert(MaxSize > 0);
|
||||
if (Size == 0) {
|
||||
for (size_t i = 0; i < MaxSize; i++)
|
||||
Data[i] = RandCh(Rand);
|
||||
if (Options.OnlyASCII)
|
||||
ToASCII(Data, MaxSize);
|
||||
return MaxSize;
|
||||
}
|
||||
assert(Size > 0);
|
||||
// Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize),
|
||||
// in which case they will return 0.
|
||||
// Try several times before returning un-mutated data.
|
||||
for (int Iter = 0; Iter < 100; Iter++) {
|
||||
auto M = Mutators[Rand(Mutators.size())];
|
||||
size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize);
|
||||
if (NewSize && NewSize <= MaxSize) {
|
||||
if (Options.OnlyASCII)
|
||||
ToASCII(Data, NewSize);
|
||||
CurrentMutatorSequence.push_back(M);
|
||||
return NewSize;
|
||||
}
|
||||
}
|
||||
return std::min(Size, MaxSize);
|
||||
}
|
||||
|
||||
void MutationDispatcher::AddWordToManualDictionary(const Word &W) {
|
||||
ManualDictionary.push_back(
|
||||
{W, std::numeric_limits<size_t>::max()});
|
||||
}
|
||||
|
||||
void MutationDispatcher::AddWordToAutoDictionary(DictionaryEntry DE) {
|
||||
static const size_t kMaxAutoDictSize = 1 << 14;
|
||||
if (TempAutoDictionary.size() >= kMaxAutoDictSize) return;
|
||||
TempAutoDictionary.push_back(DE);
|
||||
}
|
||||
|
||||
void MutationDispatcher::ClearAutoDictionary() {
|
||||
TempAutoDictionary.clear();
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
145
test/fuzz_test/Fuzzer/FuzzerMutate.h
Normal file
145
test/fuzz_test/Fuzzer/FuzzerMutate.h
Normal file
|
@ -0,0 +1,145 @@
|
|||
//===- FuzzerMutate.h - Internal header for the Fuzzer ----------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::MutationDispatcher
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_MUTATE_H
|
||||
#define LLVM_FUZZER_MUTATE_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerDictionary.h"
|
||||
#include "FuzzerRandom.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
class MutationDispatcher {
|
||||
public:
|
||||
MutationDispatcher(Random &Rand, const FuzzingOptions &Options);
|
||||
~MutationDispatcher() {}
|
||||
/// Indicate that we are about to start a new sequence of mutations.
|
||||
void StartMutationSequence();
|
||||
/// Print the current sequence of mutations.
|
||||
void PrintMutationSequence();
|
||||
/// Indicate that the current sequence of mutations was successfull.
|
||||
void RecordSuccessfulMutationSequence();
|
||||
/// Mutates data by invoking user-provided mutator.
|
||||
size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by invoking user-provided crossover.
|
||||
size_t Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by shuffling bytes.
|
||||
size_t Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by erasing bytes.
|
||||
size_t Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by inserting a byte.
|
||||
size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by inserting several repeated bytes.
|
||||
size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by chanding one byte.
|
||||
size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by chanding one bit.
|
||||
size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by copying/inserting a part of data into a different place.
|
||||
size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Mutates data by adding a word from the manual dictionary.
|
||||
size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
|
||||
/// Mutates data by adding a word from the temporary automatic dictionary.
|
||||
size_t Mutate_AddWordFromTemporaryAutoDictionary(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
|
||||
/// Mutates data by adding a word from the TORC.
|
||||
size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Mutates data by adding a word from the persistent automatic dictionary.
|
||||
size_t Mutate_AddWordFromPersistentAutoDictionary(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
|
||||
/// Tries to find an ASCII integer in Data, changes it to another ASCII int.
|
||||
size_t Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Change a 1-, 2-, 4-, or 8-byte integer in interesting ways.
|
||||
size_t Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// CrossOver Data with some other element of the corpus.
|
||||
size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Applies one of the configured mutations.
|
||||
/// Returns the new size of data which could be up to MaxSize.
|
||||
size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Applies one of the default mutations. Provided as a service
|
||||
/// to mutation authors.
|
||||
size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Creates a cross-over of two pieces of Data, returns its size.
|
||||
size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2,
|
||||
size_t Size2, uint8_t *Out, size_t MaxOutSize);
|
||||
|
||||
void AddWordToManualDictionary(const Word &W);
|
||||
|
||||
void AddWordToAutoDictionary(DictionaryEntry DE);
|
||||
void ClearAutoDictionary();
|
||||
void PrintRecommendedDictionary();
|
||||
|
||||
void SetCorpus(const InputCorpus *Corpus) { this->Corpus = Corpus; }
|
||||
|
||||
Random &GetRand() { return Rand; }
|
||||
|
||||
private:
|
||||
|
||||
struct Mutator {
|
||||
size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max);
|
||||
const char *Name;
|
||||
};
|
||||
|
||||
size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
const std::vector<Mutator> &Mutators);
|
||||
|
||||
size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
|
||||
size_t ToSize, size_t MaxToSize);
|
||||
size_t CopyPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
|
||||
size_t ToSize);
|
||||
size_t ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
DictionaryEntry &DE);
|
||||
|
||||
template <class T>
|
||||
DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2,
|
||||
const uint8_t *Data, size_t Size);
|
||||
|
||||
Random &Rand;
|
||||
const FuzzingOptions &Options;
|
||||
|
||||
// Dictionary provided by the user via -dict=DICT_FILE.
|
||||
Dictionary ManualDictionary;
|
||||
// Temporary dictionary modified by the fuzzer itself,
|
||||
// recreated periodically.
|
||||
Dictionary TempAutoDictionary;
|
||||
// Persistent dictionary modified by the fuzzer, consists of
|
||||
// entries that led to successfull discoveries in the past mutations.
|
||||
Dictionary PersistentAutoDictionary;
|
||||
|
||||
std::vector<Mutator> CurrentMutatorSequence;
|
||||
std::vector<DictionaryEntry *> CurrentDictionaryEntrySequence;
|
||||
|
||||
static const size_t kCmpDictionaryEntriesDequeSize = 16;
|
||||
DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
|
||||
size_t CmpDictionaryEntriesDequeIdx = 0;
|
||||
|
||||
const InputCorpus *Corpus = nullptr;
|
||||
std::vector<uint8_t> MutateInPlaceHere;
|
||||
|
||||
std::vector<Mutator> Mutators;
|
||||
std::vector<Mutator> DefaultMutators;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_MUTATE_H
|
68
test/fuzz_test/Fuzzer/FuzzerOptions.h
Normal file
68
test/fuzz_test/Fuzzer/FuzzerOptions.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
//===- FuzzerOptions.h - Internal header for the Fuzzer ---------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::FuzzingOptions
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_OPTIONS_H
|
||||
#define LLVM_FUZZER_OPTIONS_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
struct FuzzingOptions {
|
||||
int Verbosity = 1;
|
||||
size_t MaxLen = 0;
|
||||
int UnitTimeoutSec = 300;
|
||||
int TimeoutExitCode = 77;
|
||||
int ErrorExitCode = 77;
|
||||
int MaxTotalTimeSec = 0;
|
||||
int RssLimitMb = 0;
|
||||
bool DoCrossOver = true;
|
||||
int MutateDepth = 5;
|
||||
bool UseCounters = false;
|
||||
bool UseIndirCalls = true;
|
||||
bool UseMemcmp = true;
|
||||
bool UseMemmem = true;
|
||||
bool UseCmp = false;
|
||||
bool UseValueProfile = false;
|
||||
bool Shrink = false;
|
||||
int ReloadIntervalSec = 1;
|
||||
bool ShuffleAtStartUp = true;
|
||||
bool PreferSmall = true;
|
||||
size_t MaxNumberOfRuns = -1L;
|
||||
int ReportSlowUnits = 10;
|
||||
bool OnlyASCII = false;
|
||||
std::string OutputCorpus;
|
||||
std::string ArtifactPrefix = "./";
|
||||
std::string ExactArtifactPath;
|
||||
std::string ExitOnSrcPos;
|
||||
std::string ExitOnItem;
|
||||
bool SaveArtifacts = true;
|
||||
bool PrintNEW = true; // Print a status line when new units are found;
|
||||
bool OutputCSV = false;
|
||||
bool PrintNewCovPcs = false;
|
||||
bool PrintFinalStats = false;
|
||||
bool PrintCorpusStats = false;
|
||||
bool PrintCoverage = false;
|
||||
bool DumpCoverage = false;
|
||||
bool DetectLeaks = true;
|
||||
int TraceMalloc = 0;
|
||||
bool HandleAbrt = false;
|
||||
bool HandleBus = false;
|
||||
bool HandleFpe = false;
|
||||
bool HandleIll = false;
|
||||
bool HandleInt = false;
|
||||
bool HandleSegv = false;
|
||||
bool HandleTerm = false;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_OPTIONS_H
|
36
test/fuzz_test/Fuzzer/FuzzerRandom.h
Normal file
36
test/fuzz_test/Fuzzer/FuzzerRandom.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
//===- FuzzerRandom.h - Internal header for the Fuzzer ----------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::Random
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_RANDOM_H
|
||||
#define LLVM_FUZZER_RANDOM_H
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace fuzzer {
|
||||
class Random {
|
||||
public:
|
||||
Random(unsigned int seed) : R(seed) {}
|
||||
size_t Rand() { return R(); }
|
||||
size_t RandBool() { return Rand() % 2; }
|
||||
size_t operator()(size_t n) { return n ? Rand() % n : 0; }
|
||||
intptr_t operator()(intptr_t From, intptr_t To) {
|
||||
assert(From < To);
|
||||
intptr_t RangeSize = To - From + 1;
|
||||
return operator()(RangeSize) + From;
|
||||
}
|
||||
std::mt19937 &Get_mt19937() { return R; }
|
||||
private:
|
||||
std::mt19937 R;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_RANDOM_H
|
222
test/fuzz_test/Fuzzer/FuzzerSHA1.cpp
Normal file
222
test/fuzz_test/Fuzzer/FuzzerSHA1.cpp
Normal file
|
@ -0,0 +1,222 @@
|
|||
//===- FuzzerSHA1.h - Private copy of the SHA1 implementation ---*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This code is taken from public domain
|
||||
// (http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c)
|
||||
// and modified by adding anonymous namespace, adding an interface
|
||||
// function fuzzer::ComputeSHA1() and removing unnecessary code.
|
||||
//
|
||||
// lib/Fuzzer can not use SHA1 implementation from openssl because
|
||||
// openssl may not be available and because we may be fuzzing openssl itself.
|
||||
// For the same reason we do not want to depend on SHA1 from LLVM tree.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerSHA1.h"
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
/* This code is public-domain - it is based on libcrypt
|
||||
* placed in the public domain by Wei Dai and other contributors.
|
||||
*/
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace { // Added for LibFuzzer
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
# define SHA_BIG_ENDIAN
|
||||
#elif defined __LITTLE_ENDIAN__
|
||||
/* override */
|
||||
#elif defined __BYTE_ORDER
|
||||
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define SHA_BIG_ENDIAN
|
||||
# endif
|
||||
#else // ! defined __LITTLE_ENDIAN__
|
||||
# include <endian.h> // machine/endian.h
|
||||
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define SHA_BIG_ENDIAN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/* header */
|
||||
|
||||
#define HASH_LENGTH 20
|
||||
#define BLOCK_LENGTH 64
|
||||
|
||||
typedef struct sha1nfo {
|
||||
uint32_t buffer[BLOCK_LENGTH/4];
|
||||
uint32_t state[HASH_LENGTH/4];
|
||||
uint32_t byteCount;
|
||||
uint8_t bufferOffset;
|
||||
uint8_t keyBuffer[BLOCK_LENGTH];
|
||||
uint8_t innerHash[HASH_LENGTH];
|
||||
} sha1nfo;
|
||||
|
||||
/* public API - prototypes - TODO: doxygen*/
|
||||
|
||||
/**
|
||||
*/
|
||||
void sha1_init(sha1nfo *s);
|
||||
/**
|
||||
*/
|
||||
void sha1_writebyte(sha1nfo *s, uint8_t data);
|
||||
/**
|
||||
*/
|
||||
void sha1_write(sha1nfo *s, const char *data, size_t len);
|
||||
/**
|
||||
*/
|
||||
uint8_t* sha1_result(sha1nfo *s);
|
||||
|
||||
|
||||
/* code */
|
||||
#define SHA1_K0 0x5a827999
|
||||
#define SHA1_K20 0x6ed9eba1
|
||||
#define SHA1_K40 0x8f1bbcdc
|
||||
#define SHA1_K60 0xca62c1d6
|
||||
|
||||
void sha1_init(sha1nfo *s) {
|
||||
s->state[0] = 0x67452301;
|
||||
s->state[1] = 0xefcdab89;
|
||||
s->state[2] = 0x98badcfe;
|
||||
s->state[3] = 0x10325476;
|
||||
s->state[4] = 0xc3d2e1f0;
|
||||
s->byteCount = 0;
|
||||
s->bufferOffset = 0;
|
||||
}
|
||||
|
||||
uint32_t sha1_rol32(uint32_t number, uint8_t bits) {
|
||||
return ((number << bits) | (number >> (32-bits)));
|
||||
}
|
||||
|
||||
void sha1_hashBlock(sha1nfo *s) {
|
||||
uint8_t i;
|
||||
uint32_t a,b,c,d,e,t;
|
||||
|
||||
a=s->state[0];
|
||||
b=s->state[1];
|
||||
c=s->state[2];
|
||||
d=s->state[3];
|
||||
e=s->state[4];
|
||||
for (i=0; i<80; i++) {
|
||||
if (i>=16) {
|
||||
t = s->buffer[(i+13)&15] ^ s->buffer[(i+8)&15] ^ s->buffer[(i+2)&15] ^ s->buffer[i&15];
|
||||
s->buffer[i&15] = sha1_rol32(t,1);
|
||||
}
|
||||
if (i<20) {
|
||||
t = (d ^ (b & (c ^ d))) + SHA1_K0;
|
||||
} else if (i<40) {
|
||||
t = (b ^ c ^ d) + SHA1_K20;
|
||||
} else if (i<60) {
|
||||
t = ((b & c) | (d & (b | c))) + SHA1_K40;
|
||||
} else {
|
||||
t = (b ^ c ^ d) + SHA1_K60;
|
||||
}
|
||||
t+=sha1_rol32(a,5) + e + s->buffer[i&15];
|
||||
e=d;
|
||||
d=c;
|
||||
c=sha1_rol32(b,30);
|
||||
b=a;
|
||||
a=t;
|
||||
}
|
||||
s->state[0] += a;
|
||||
s->state[1] += b;
|
||||
s->state[2] += c;
|
||||
s->state[3] += d;
|
||||
s->state[4] += e;
|
||||
}
|
||||
|
||||
void sha1_addUncounted(sha1nfo *s, uint8_t data) {
|
||||
uint8_t * const b = (uint8_t*) s->buffer;
|
||||
#ifdef SHA_BIG_ENDIAN
|
||||
b[s->bufferOffset] = data;
|
||||
#else
|
||||
b[s->bufferOffset ^ 3] = data;
|
||||
#endif
|
||||
s->bufferOffset++;
|
||||
if (s->bufferOffset == BLOCK_LENGTH) {
|
||||
sha1_hashBlock(s);
|
||||
s->bufferOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void sha1_writebyte(sha1nfo *s, uint8_t data) {
|
||||
++s->byteCount;
|
||||
sha1_addUncounted(s, data);
|
||||
}
|
||||
|
||||
void sha1_write(sha1nfo *s, const char *data, size_t len) {
|
||||
for (;len--;) sha1_writebyte(s, (uint8_t) *data++);
|
||||
}
|
||||
|
||||
void sha1_pad(sha1nfo *s) {
|
||||
// Implement SHA-1 padding (fips180-2 §5.1.1)
|
||||
|
||||
// Pad with 0x80 followed by 0x00 until the end of the block
|
||||
sha1_addUncounted(s, 0x80);
|
||||
while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00);
|
||||
|
||||
// Append length in the last 8 bytes
|
||||
sha1_addUncounted(s, 0); // We're only using 32 bit lengths
|
||||
sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths
|
||||
sha1_addUncounted(s, 0); // So zero pad the top bits
|
||||
sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8
|
||||
sha1_addUncounted(s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as
|
||||
sha1_addUncounted(s, s->byteCount >> 13); // byte.
|
||||
sha1_addUncounted(s, s->byteCount >> 5);
|
||||
sha1_addUncounted(s, s->byteCount << 3);
|
||||
}
|
||||
|
||||
uint8_t* sha1_result(sha1nfo *s) {
|
||||
// Pad to complete the last block
|
||||
sha1_pad(s);
|
||||
|
||||
#ifndef SHA_BIG_ENDIAN
|
||||
// Swap byte order back
|
||||
int i;
|
||||
for (i=0; i<5; i++) {
|
||||
s->state[i]=
|
||||
(((s->state[i])<<24)& 0xff000000)
|
||||
| (((s->state[i])<<8) & 0x00ff0000)
|
||||
| (((s->state[i])>>8) & 0x0000ff00)
|
||||
| (((s->state[i])>>24)& 0x000000ff);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Return pointer to hash (20 characters)
|
||||
return (uint8_t*) s->state;
|
||||
}
|
||||
|
||||
} // namespace; Added for LibFuzzer
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// The rest is added for LibFuzzer
|
||||
void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out) {
|
||||
sha1nfo s;
|
||||
sha1_init(&s);
|
||||
sha1_write(&s, (const char*)Data, Len);
|
||||
memcpy(Out, sha1_result(&s), HASH_LENGTH);
|
||||
}
|
||||
|
||||
std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]) {
|
||||
std::stringstream SS;
|
||||
for (int i = 0; i < kSHA1NumBytes; i++)
|
||||
SS << std::hex << std::setfill('0') << std::setw(2) << (unsigned)Sha1[i];
|
||||
return SS.str();
|
||||
}
|
||||
|
||||
std::string Hash(const Unit &U) {
|
||||
uint8_t Hash[kSHA1NumBytes];
|
||||
ComputeSHA1(U.data(), U.size(), Hash);
|
||||
return Sha1ToString(Hash);
|
||||
}
|
||||
|
||||
}
|
33
test/fuzz_test/Fuzzer/FuzzerSHA1.h
Normal file
33
test/fuzz_test/Fuzzer/FuzzerSHA1.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
//===- FuzzerSHA1.h - Internal header for the SHA1 utils --------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SHA1 utils.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_SHA1_H
|
||||
#define LLVM_FUZZER_SHA1_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include <cstddef>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// Private copy of SHA1 implementation.
|
||||
static const int kSHA1NumBytes = 20;
|
||||
|
||||
// Computes SHA1 hash of 'Len' bytes in 'Data', writes kSHA1NumBytes to 'Out'.
|
||||
void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out);
|
||||
|
||||
std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]);
|
||||
|
||||
std::string Hash(const Unit &U);
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_SHA1_H
|
339
test/fuzz_test/Fuzzer/FuzzerTracePC.cpp
Normal file
339
test/fuzz_test/Fuzzer/FuzzerTracePC.cpp
Normal file
|
@ -0,0 +1,339 @@
|
|||
//===- FuzzerTracePC.cpp - PC tracing--------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Trace PCs.
|
||||
// This module implements __sanitizer_cov_trace_pc_guard[_init],
|
||||
// the callback required for -fsanitize-coverage=trace-pc-guard instrumentation.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerDictionary.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include "FuzzerValueBitMap.h"
|
||||
#include <map>
|
||||
#include <sanitizer/coverage_interface.h>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
TracePC TPC;
|
||||
|
||||
void TracePC::HandleTrace(uint32_t *Guard, uintptr_t PC) {
|
||||
uint32_t Idx = *Guard;
|
||||
if (!Idx) return;
|
||||
PCs[Idx % kNumPCs] = PC;
|
||||
Counters[Idx % kNumCounters]++;
|
||||
}
|
||||
|
||||
size_t TracePC::GetTotalPCCoverage() {
|
||||
size_t Res = 0;
|
||||
for (size_t i = 1; i < GetNumPCs(); i++)
|
||||
if (PCs[i])
|
||||
Res++;
|
||||
return Res;
|
||||
}
|
||||
|
||||
void TracePC::HandleInit(uint32_t *Start, uint32_t *Stop) {
|
||||
if (Start == Stop || *Start) return;
|
||||
assert(NumModules < sizeof(Modules) / sizeof(Modules[0]));
|
||||
for (uint32_t *P = Start; P < Stop; P++)
|
||||
*P = ++NumGuards;
|
||||
Modules[NumModules].Start = Start;
|
||||
Modules[NumModules].Stop = Stop;
|
||||
NumModules++;
|
||||
}
|
||||
|
||||
void TracePC::PrintModuleInfo() {
|
||||
Printf("INFO: Loaded %zd modules (%zd guards): ", NumModules, NumGuards);
|
||||
for (size_t i = 0; i < NumModules; i++)
|
||||
Printf("[%p, %p), ", Modules[i].Start, Modules[i].Stop);
|
||||
Printf("\n");
|
||||
}
|
||||
|
||||
void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) {
|
||||
const uintptr_t kBits = 12;
|
||||
const uintptr_t kMask = (1 << kBits) - 1;
|
||||
uintptr_t Idx = (Caller & kMask) | ((Callee & kMask) << kBits);
|
||||
HandleValueProfile(Idx);
|
||||
}
|
||||
|
||||
static bool IsInterestingCoverageFile(std::string &File) {
|
||||
if (File.find("compiler-rt/lib/") != std::string::npos)
|
||||
return false; // sanitizer internal.
|
||||
if (File.find("/usr/lib/") != std::string::npos)
|
||||
return false;
|
||||
if (File.find("/usr/include/") != std::string::npos)
|
||||
return false;
|
||||
if (File == "<null>")
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TracePC::PrintNewPCs() {
|
||||
if (DoPrintNewPCs) {
|
||||
if (!PrintedPCs)
|
||||
PrintedPCs = new std::set<uintptr_t>;
|
||||
for (size_t i = 1; i < GetNumPCs(); i++)
|
||||
if (PCs[i] && PrintedPCs->insert(PCs[i]).second)
|
||||
PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PCs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void TracePC::PrintCoverage() {
|
||||
if (!EF->__sanitizer_symbolize_pc ||
|
||||
!EF->__sanitizer_get_module_and_offset_for_pc) {
|
||||
Printf("INFO: __sanitizer_symbolize_pc or "
|
||||
"__sanitizer_get_module_and_offset_for_pc is not available,"
|
||||
" not printing coverage\n");
|
||||
return;
|
||||
}
|
||||
std::map<std::string, std::vector<uintptr_t>> CoveredPCsPerModule;
|
||||
std::map<std::string, uintptr_t> ModuleOffsets;
|
||||
std::set<std::string> CoveredDirs, CoveredFiles, CoveredFunctions,
|
||||
CoveredLines;
|
||||
Printf("COVERAGE:\n");
|
||||
for (size_t i = 1; i < GetNumPCs(); i++) {
|
||||
if (!PCs[i]) continue;
|
||||
std::string FileStr = DescribePC("%s", PCs[i]);
|
||||
if (!IsInterestingCoverageFile(FileStr)) continue;
|
||||
std::string FixedPCStr = DescribePC("%p", PCs[i]);
|
||||
std::string FunctionStr = DescribePC("%F", PCs[i]);
|
||||
std::string LineStr = DescribePC("%l", PCs[i]);
|
||||
char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++?
|
||||
void *OffsetRaw = nullptr;
|
||||
if (!EF->__sanitizer_get_module_and_offset_for_pc(
|
||||
reinterpret_cast<void *>(PCs[i]), ModulePathRaw,
|
||||
sizeof(ModulePathRaw), &OffsetRaw))
|
||||
continue;
|
||||
std::string Module = ModulePathRaw;
|
||||
uintptr_t FixedPC = std::stol(FixedPCStr, 0, 16);
|
||||
uintptr_t PcOffset = reinterpret_cast<uintptr_t>(OffsetRaw);
|
||||
ModuleOffsets[Module] = FixedPC - PcOffset;
|
||||
CoveredPCsPerModule[Module].push_back(PcOffset);
|
||||
CoveredFunctions.insert(FunctionStr);
|
||||
CoveredFiles.insert(FileStr);
|
||||
CoveredDirs.insert(DirName(FileStr));
|
||||
if (!CoveredLines.insert(FileStr + ":" + LineStr).second)
|
||||
continue;
|
||||
Printf("COVERED: %s %s:%s\n", FunctionStr.c_str(),
|
||||
FileStr.c_str(), LineStr.c_str());
|
||||
}
|
||||
|
||||
std::string CoveredDirsStr;
|
||||
for (auto &Dir : CoveredDirs) {
|
||||
if (!CoveredDirsStr.empty())
|
||||
CoveredDirsStr += ",";
|
||||
CoveredDirsStr += Dir;
|
||||
}
|
||||
Printf("COVERED_DIRS: %s\n", CoveredDirsStr.c_str());
|
||||
|
||||
for (auto &M : CoveredPCsPerModule) {
|
||||
std::set<std::string> UncoveredFiles, UncoveredFunctions;
|
||||
std::map<std::string, std::set<int> > UncoveredLines; // Func+File => lines
|
||||
auto &ModuleName = M.first;
|
||||
auto &CoveredOffsets = M.second;
|
||||
uintptr_t ModuleOffset = ModuleOffsets[ModuleName];
|
||||
std::sort(CoveredOffsets.begin(), CoveredOffsets.end());
|
||||
Printf("MODULE_WITH_COVERAGE: %s\n", ModuleName.c_str());
|
||||
// sancov does not yet fully support DSOs.
|
||||
// std::string Cmd = "sancov -print-coverage-pcs " + ModuleName;
|
||||
std::string Cmd = "objdump -d " + ModuleName +
|
||||
" | grep 'call.*__sanitizer_cov_trace_pc_guard' | awk -F: '{print $1}'";
|
||||
std::string SanCovOutput;
|
||||
if (!ExecuteCommandAndReadOutput(Cmd, &SanCovOutput)) {
|
||||
Printf("INFO: Command failed: %s\n", Cmd.c_str());
|
||||
continue;
|
||||
}
|
||||
std::istringstream ISS(SanCovOutput);
|
||||
std::string S;
|
||||
while (std::getline(ISS, S, '\n')) {
|
||||
uintptr_t PcOffset = std::stol(S, 0, 16);
|
||||
if (!std::binary_search(CoveredOffsets.begin(), CoveredOffsets.end(),
|
||||
PcOffset)) {
|
||||
uintptr_t PC = ModuleOffset + PcOffset;
|
||||
auto FileStr = DescribePC("%s", PC);
|
||||
if (!IsInterestingCoverageFile(FileStr)) continue;
|
||||
if (CoveredFiles.count(FileStr) == 0) {
|
||||
UncoveredFiles.insert(FileStr);
|
||||
continue;
|
||||
}
|
||||
auto FunctionStr = DescribePC("%F", PC);
|
||||
if (CoveredFunctions.count(FunctionStr) == 0) {
|
||||
UncoveredFunctions.insert(FunctionStr);
|
||||
continue;
|
||||
}
|
||||
std::string LineStr = DescribePC("%l", PC);
|
||||
uintptr_t Line = std::stoi(LineStr);
|
||||
std::string FileLineStr = FileStr + ":" + LineStr;
|
||||
if (CoveredLines.count(FileLineStr) == 0)
|
||||
UncoveredLines[FunctionStr + " " + FileStr].insert(Line);
|
||||
}
|
||||
}
|
||||
for (auto &FileLine: UncoveredLines)
|
||||
for (int Line : FileLine.second)
|
||||
Printf("UNCOVERED_LINE: %s:%d\n", FileLine.first.c_str(), Line);
|
||||
for (auto &Func : UncoveredFunctions)
|
||||
Printf("UNCOVERED_FUNC: %s\n", Func.c_str());
|
||||
for (auto &File : UncoveredFiles)
|
||||
Printf("UNCOVERED_FILE: %s\n", File.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void TracePC::DumpCoverage() {
|
||||
__sanitizer_dump_coverage(PCs, GetNumPCs());
|
||||
}
|
||||
|
||||
// Value profile.
|
||||
// We keep track of various values that affect control flow.
|
||||
// These values are inserted into a bit-set-based hash map.
|
||||
// Every new bit in the map is treated as a new coverage.
|
||||
//
|
||||
// For memcmp/strcmp/etc the interesting value is the length of the common
|
||||
// prefix of the parameters.
|
||||
// For cmp instructions the interesting value is a XOR of the parameters.
|
||||
// The interesting value is mixed up with the PC and is then added to the map.
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
|
||||
size_t n) {
|
||||
if (!n) return;
|
||||
size_t Len = std::min(n, (size_t)32);
|
||||
const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1);
|
||||
const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2);
|
||||
size_t I = 0;
|
||||
for (; I < Len; I++)
|
||||
if (A1[I] != A2[I])
|
||||
break;
|
||||
size_t PC = reinterpret_cast<size_t>(caller_pc);
|
||||
size_t Idx = I;
|
||||
// if (I < Len)
|
||||
// Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1;
|
||||
TPC.HandleValueProfile((PC & 4095) | (Idx << 12));
|
||||
}
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void TracePC::AddValueForStrcmp(void *caller_pc, const char *s1, const char *s2,
|
||||
size_t n) {
|
||||
if (!n) return;
|
||||
size_t Len = std::min(n, (size_t)32);
|
||||
const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1);
|
||||
const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2);
|
||||
size_t I = 0;
|
||||
for (; I < Len; I++)
|
||||
if (A1[I] != A2[I] || A1[I] == 0)
|
||||
break;
|
||||
size_t PC = reinterpret_cast<size_t>(caller_pc);
|
||||
size_t Idx = I;
|
||||
// if (I < Len && A1[I])
|
||||
// Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1;
|
||||
TPC.HandleValueProfile((PC & 4095) | (Idx << 12));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
#ifdef __clang__ // g++ can't handle this __attribute__ here :(
|
||||
__attribute__((always_inline))
|
||||
#endif // __clang__
|
||||
void TracePC::HandleCmp(void *PC, T Arg1, T Arg2) {
|
||||
uintptr_t PCuint = reinterpret_cast<uintptr_t>(PC);
|
||||
uint64_t ArgXor = Arg1 ^ Arg2;
|
||||
uint64_t ArgDistance = __builtin_popcountl(ArgXor) + 1; // [1,65]
|
||||
uintptr_t Idx = ((PCuint & 4095) + 1) * ArgDistance;
|
||||
if (sizeof(T) == 4)
|
||||
TORC4.Insert(ArgXor, Arg1, Arg2);
|
||||
else if (sizeof(T) == 8)
|
||||
TORC8.Insert(ArgXor, Arg1, Arg2);
|
||||
HandleValueProfile(Idx);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
extern "C" {
|
||||
__attribute__((visibility("default")))
|
||||
void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
|
||||
uintptr_t PC = (uintptr_t)__builtin_return_address(0);
|
||||
fuzzer::TPC.HandleTrace(Guard, PC);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) {
|
||||
fuzzer::TPC.HandleInit(Start, Stop);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) {
|
||||
uintptr_t PC = (uintptr_t)__builtin_return_address(0);
|
||||
fuzzer::TPC.HandleCallerCallee(PC, Callee);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) {
|
||||
fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
|
||||
}
|
||||
__attribute__((visibility("default")))
|
||||
void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) {
|
||||
fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
|
||||
}
|
||||
__attribute__((visibility("default")))
|
||||
void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) {
|
||||
fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
|
||||
}
|
||||
__attribute__((visibility("default")))
|
||||
void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) {
|
||||
fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
|
||||
// Updates the value profile based on the relative position of Val and Cases.
|
||||
// We want to handle one random case at every call (handling all is slow).
|
||||
// Since none of the arguments contain any random bits we use a thread-local
|
||||
// counter to choose the random case to handle.
|
||||
static thread_local size_t Counter;
|
||||
Counter++;
|
||||
uint64_t N = Cases[0];
|
||||
uint64_t *Vals = Cases + 2;
|
||||
char *PC = (char*)__builtin_return_address(0);
|
||||
// We need a random number < N using Counter as a seed. But w/o DIV.
|
||||
// * find a power of two >= N
|
||||
// * mask Counter with this power of two.
|
||||
// * maybe subtract N.
|
||||
size_t Nlog = sizeof(long) * 8 - __builtin_clzl((long)N);
|
||||
size_t PowerOfTwoGeN = 1U << Nlog;
|
||||
assert(PowerOfTwoGeN >= N);
|
||||
size_t Idx = Counter & (PowerOfTwoGeN - 1);
|
||||
if (Idx >= N)
|
||||
Idx -= N;
|
||||
assert(Idx < N);
|
||||
uint64_t TwoIn32 = 1ULL << 32;
|
||||
if ((Val | Vals[Idx]) < TwoIn32)
|
||||
fuzzer::TPC.HandleCmp(PC + Idx, static_cast<uint32_t>(Val),
|
||||
static_cast<uint32_t>(Vals[Idx]));
|
||||
else
|
||||
fuzzer::TPC.HandleCmp(PC + Idx, Val, Vals[Idx]);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
void __sanitizer_cov_trace_div4(uint32_t Val) {
|
||||
fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint32_t)0);
|
||||
}
|
||||
__attribute__((visibility("default")))
|
||||
void __sanitizer_cov_trace_div8(uint64_t Val) {
|
||||
fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint64_t)0);
|
||||
}
|
||||
__attribute__((visibility("default")))
|
||||
void __sanitizer_cov_trace_gep(uintptr_t Idx) {
|
||||
fuzzer::TPC.HandleCmp(__builtin_return_address(0), Idx, (uintptr_t)0);
|
||||
}
|
||||
|
||||
} // extern "C"
|
158
test/fuzz_test/Fuzzer/FuzzerTracePC.h
Normal file
158
test/fuzz_test/Fuzzer/FuzzerTracePC.h
Normal file
|
@ -0,0 +1,158 @@
|
|||
//===- FuzzerTracePC.h - Internal header for the Fuzzer ---------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::TracePC
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_TRACE_PC
|
||||
#define LLVM_FUZZER_TRACE_PC
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerValueBitMap.h"
|
||||
#include <set>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// TableOfRecentCompares (TORC) remembers the most recently performed
|
||||
// comparisons of type T.
|
||||
// We record the arguments of CMP instructions in this table unconditionally
|
||||
// because it seems cheaper this way than to compute some expensive
|
||||
// conditions inside __sanitizer_cov_trace_cmp*.
|
||||
// After the unit has been executed we may decide to use the contents of
|
||||
// this table to populate a Dictionary.
|
||||
template<class T, size_t kSizeT>
|
||||
struct TableOfRecentCompares {
|
||||
static const size_t kSize = kSizeT;
|
||||
struct Pair {
|
||||
T A, B;
|
||||
};
|
||||
void Insert(size_t Idx, T Arg1, T Arg2) {
|
||||
Idx = Idx % kSize;
|
||||
Table[Idx].A = Arg1;
|
||||
Table[Idx].B = Arg2;
|
||||
}
|
||||
|
||||
Pair Get(size_t I) { return Table[I % kSize]; }
|
||||
|
||||
Pair Table[kSize];
|
||||
};
|
||||
|
||||
class TracePC {
|
||||
public:
|
||||
static const size_t kFeatureSetSize = ValueBitMap::kNumberOfItems;
|
||||
|
||||
void HandleTrace(uint32_t *guard, uintptr_t PC);
|
||||
void HandleInit(uint32_t *start, uint32_t *stop);
|
||||
void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee);
|
||||
void HandleValueProfile(size_t Value) { ValueProfileMap.AddValue(Value); }
|
||||
template <class T> void HandleCmp(void *PC, T Arg1, T Arg2);
|
||||
size_t GetTotalPCCoverage();
|
||||
void SetUseCounters(bool UC) { UseCounters = UC; }
|
||||
void SetUseValueProfile(bool VP) { UseValueProfile = VP; }
|
||||
void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; }
|
||||
template <class Callback> size_t CollectFeatures(Callback CB);
|
||||
bool UpdateValueProfileMap(ValueBitMap *MaxValueProfileMap) {
|
||||
return UseValueProfile && MaxValueProfileMap->MergeFrom(ValueProfileMap);
|
||||
}
|
||||
|
||||
void ResetMaps() {
|
||||
ValueProfileMap.Reset();
|
||||
memset(Counters, 0, sizeof(Counters));
|
||||
}
|
||||
|
||||
void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize);
|
||||
void PrintFeatureSet();
|
||||
|
||||
void PrintModuleInfo();
|
||||
|
||||
void PrintCoverage();
|
||||
void DumpCoverage();
|
||||
|
||||
void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
|
||||
size_t n);
|
||||
void AddValueForStrcmp(void *caller_pc, const char *s1, const char *s2,
|
||||
size_t n);
|
||||
|
||||
bool UsingTracePcGuard() const {return NumModules; }
|
||||
|
||||
static const size_t kTORCSize = 1 << 5;
|
||||
TableOfRecentCompares<uint32_t, kTORCSize> TORC4;
|
||||
TableOfRecentCompares<uint64_t, kTORCSize> TORC8;
|
||||
|
||||
void PrintNewPCs();
|
||||
size_t GetNumPCs() const { return Min(kNumPCs, NumGuards + 1); }
|
||||
uintptr_t GetPC(size_t Idx) {
|
||||
assert(Idx < GetNumPCs());
|
||||
return PCs[Idx];
|
||||
}
|
||||
|
||||
private:
|
||||
bool UseCounters = false;
|
||||
bool UseValueProfile = false;
|
||||
bool DoPrintNewPCs = false;
|
||||
|
||||
struct Module {
|
||||
uint32_t *Start, *Stop;
|
||||
};
|
||||
|
||||
Module Modules[4096];
|
||||
size_t NumModules; // linker-initialized.
|
||||
size_t NumGuards; // linker-initialized.
|
||||
|
||||
static const size_t kNumCounters = 1 << 14;
|
||||
alignas(8) uint8_t Counters[kNumCounters];
|
||||
|
||||
static const size_t kNumPCs = 1 << 24;
|
||||
uintptr_t PCs[kNumPCs];
|
||||
|
||||
std::set<uintptr_t> *PrintedPCs;
|
||||
|
||||
ValueBitMap ValueProfileMap;
|
||||
};
|
||||
|
||||
template <class Callback>
|
||||
size_t TracePC::CollectFeatures(Callback CB) {
|
||||
if (!UsingTracePcGuard()) return 0;
|
||||
size_t Res = 0;
|
||||
const size_t Step = 8;
|
||||
assert(reinterpret_cast<uintptr_t>(Counters) % Step == 0);
|
||||
size_t N = Min(kNumCounters, NumGuards + 1);
|
||||
N = (N + Step - 1) & ~(Step - 1); // Round up.
|
||||
for (size_t Idx = 0; Idx < N; Idx += Step) {
|
||||
uint64_t Bundle = *reinterpret_cast<uint64_t*>(&Counters[Idx]);
|
||||
if (!Bundle) continue;
|
||||
for (size_t i = Idx; i < Idx + Step; i++) {
|
||||
uint8_t Counter = (Bundle >> ((i - Idx) * 8)) & 0xff;
|
||||
if (!Counter) continue;
|
||||
Counters[i] = 0;
|
||||
unsigned Bit = 0;
|
||||
/**/ if (Counter >= 128) Bit = 7;
|
||||
else if (Counter >= 32) Bit = 6;
|
||||
else if (Counter >= 16) Bit = 5;
|
||||
else if (Counter >= 8) Bit = 4;
|
||||
else if (Counter >= 4) Bit = 3;
|
||||
else if (Counter >= 3) Bit = 2;
|
||||
else if (Counter >= 2) Bit = 1;
|
||||
size_t Feature = (i * 8 + Bit);
|
||||
if (CB(Feature))
|
||||
Res++;
|
||||
}
|
||||
}
|
||||
if (UseValueProfile)
|
||||
ValueProfileMap.ForEach([&](size_t Idx) {
|
||||
if (CB(NumGuards * 8 + Idx))
|
||||
Res++;
|
||||
});
|
||||
return Res;
|
||||
}
|
||||
|
||||
extern TracePC TPC;
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_TRACE_PC
|
325
test/fuzz_test/Fuzzer/FuzzerTraceState.cpp
Normal file
325
test/fuzz_test/Fuzzer/FuzzerTraceState.cpp
Normal file
|
@ -0,0 +1,325 @@
|
|||
//===- FuzzerTraceState.cpp - Trace-based fuzzer mutator ------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Data tracing.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerDictionary.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// For now, very simple: put Size bytes of Data at position Pos.
|
||||
struct TraceBasedMutation {
|
||||
uint32_t Pos;
|
||||
Word W;
|
||||
};
|
||||
|
||||
// Declared as static globals for faster checks inside the hooks.
|
||||
static bool RecordingMemcmp = false;
|
||||
static bool RecordingMemmem = false;
|
||||
static bool DoingMyOwnMemmem = false;
|
||||
|
||||
ScopedDoingMyOwnMemmem::ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = true; }
|
||||
ScopedDoingMyOwnMemmem::~ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = false; }
|
||||
|
||||
class TraceState {
|
||||
public:
|
||||
TraceState(MutationDispatcher &MD, const FuzzingOptions &Options,
|
||||
const Fuzzer *F)
|
||||
: MD(MD), Options(Options), F(F) {}
|
||||
|
||||
void TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
|
||||
const uint8_t *Data2);
|
||||
|
||||
void TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits, uint64_t Val,
|
||||
size_t NumCases, uint64_t *Cases);
|
||||
int TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData,
|
||||
size_t DataSize);
|
||||
int TryToAddDesiredData(const uint8_t *PresentData,
|
||||
const uint8_t *DesiredData, size_t DataSize);
|
||||
|
||||
void StartTraceRecording() {
|
||||
if (!Options.UseMemcmp)
|
||||
return;
|
||||
RecordingMemcmp = Options.UseMemcmp;
|
||||
RecordingMemmem = Options.UseMemmem;
|
||||
NumMutations = 0;
|
||||
InterestingWords.clear();
|
||||
MD.ClearAutoDictionary();
|
||||
}
|
||||
|
||||
void StopTraceRecording() {
|
||||
if (!RecordingMemcmp)
|
||||
return;
|
||||
RecordingMemcmp = false;
|
||||
for (size_t i = 0; i < NumMutations; i++) {
|
||||
auto &M = Mutations[i];
|
||||
if (Options.Verbosity >= 2) {
|
||||
AutoDictUnitCounts[M.W]++;
|
||||
AutoDictAdds++;
|
||||
if ((AutoDictAdds & (AutoDictAdds - 1)) == 0) {
|
||||
typedef std::pair<size_t, Word> CU;
|
||||
std::vector<CU> CountedUnits;
|
||||
for (auto &I : AutoDictUnitCounts)
|
||||
CountedUnits.push_back(std::make_pair(I.second, I.first));
|
||||
std::sort(CountedUnits.begin(), CountedUnits.end(),
|
||||
[](const CU &a, const CU &b) { return a.first > b.first; });
|
||||
Printf("AutoDict:\n");
|
||||
for (auto &I : CountedUnits) {
|
||||
Printf(" %zd ", I.first);
|
||||
PrintASCII(I.second.data(), I.second.size());
|
||||
Printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
MD.AddWordToAutoDictionary({M.W, M.Pos});
|
||||
}
|
||||
for (auto &W : InterestingWords)
|
||||
MD.AddWordToAutoDictionary({W});
|
||||
}
|
||||
|
||||
void AddMutation(uint32_t Pos, uint32_t Size, const uint8_t *Data) {
|
||||
if (NumMutations >= kMaxMutations) return;
|
||||
auto &M = Mutations[NumMutations++];
|
||||
M.Pos = Pos;
|
||||
M.W.Set(Data, Size);
|
||||
}
|
||||
|
||||
void AddMutation(uint32_t Pos, uint32_t Size, uint64_t Data) {
|
||||
assert(Size <= sizeof(Data));
|
||||
AddMutation(Pos, Size, reinterpret_cast<uint8_t*>(&Data));
|
||||
}
|
||||
|
||||
void AddInterestingWord(const uint8_t *Data, size_t Size) {
|
||||
if (!RecordingMemmem || !F->InFuzzingThread()) return;
|
||||
if (Size <= 1) return;
|
||||
Size = std::min(Size, Word::GetMaxSize());
|
||||
Word W(Data, Size);
|
||||
InterestingWords.insert(W);
|
||||
}
|
||||
|
||||
private:
|
||||
bool IsTwoByteData(uint64_t Data) {
|
||||
int64_t Signed = static_cast<int64_t>(Data);
|
||||
Signed >>= 16;
|
||||
return Signed == 0 || Signed == -1L;
|
||||
}
|
||||
|
||||
// We don't want to create too many trace-based mutations as it is both
|
||||
// expensive and useless. So after some number of mutations is collected,
|
||||
// start rejecting some of them. The more there are mutations the more we
|
||||
// reject.
|
||||
bool WantToHandleOneMoreMutation() {
|
||||
const size_t FirstN = 64;
|
||||
// Gladly handle first N mutations.
|
||||
if (NumMutations <= FirstN) return true;
|
||||
size_t Diff = NumMutations - FirstN;
|
||||
size_t DiffLog = sizeof(long) * 8 - __builtin_clzl((long)Diff);
|
||||
assert(DiffLog > 0 && DiffLog < 64);
|
||||
bool WantThisOne = MD.GetRand()(1 << DiffLog) == 0; // 1 out of DiffLog.
|
||||
return WantThisOne;
|
||||
}
|
||||
|
||||
static const size_t kMaxMutations = 1 << 16;
|
||||
size_t NumMutations;
|
||||
TraceBasedMutation Mutations[kMaxMutations];
|
||||
// TODO: std::set is too inefficient, need to have a custom DS here.
|
||||
std::set<Word> InterestingWords;
|
||||
MutationDispatcher &MD;
|
||||
const FuzzingOptions Options;
|
||||
const Fuzzer *F;
|
||||
std::map<Word, size_t> AutoDictUnitCounts;
|
||||
size_t AutoDictAdds = 0;
|
||||
};
|
||||
|
||||
int TraceState::TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData,
|
||||
size_t DataSize) {
|
||||
if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0;
|
||||
ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem;
|
||||
const uint8_t *UnitData;
|
||||
auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData);
|
||||
int Res = 0;
|
||||
const uint8_t *Beg = UnitData;
|
||||
const uint8_t *End = Beg + UnitSize;
|
||||
for (const uint8_t *Cur = Beg; Cur < End; Cur++) {
|
||||
Cur = (uint8_t *)SearchMemory(Cur, End - Cur, &PresentData, DataSize);
|
||||
if (!Cur)
|
||||
break;
|
||||
size_t Pos = Cur - Beg;
|
||||
assert(Pos < UnitSize);
|
||||
AddMutation(Pos, DataSize, DesiredData);
|
||||
AddMutation(Pos, DataSize, DesiredData + 1);
|
||||
AddMutation(Pos, DataSize, DesiredData - 1);
|
||||
Res++;
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
int TraceState::TryToAddDesiredData(const uint8_t *PresentData,
|
||||
const uint8_t *DesiredData,
|
||||
size_t DataSize) {
|
||||
if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0;
|
||||
ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem;
|
||||
const uint8_t *UnitData;
|
||||
auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData);
|
||||
int Res = 0;
|
||||
const uint8_t *Beg = UnitData;
|
||||
const uint8_t *End = Beg + UnitSize;
|
||||
for (const uint8_t *Cur = Beg; Cur < End; Cur++) {
|
||||
Cur = (uint8_t *)SearchMemory(Cur, End - Cur, PresentData, DataSize);
|
||||
if (!Cur)
|
||||
break;
|
||||
size_t Pos = Cur - Beg;
|
||||
assert(Pos < UnitSize);
|
||||
AddMutation(Pos, DataSize, DesiredData);
|
||||
Res++;
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
void TraceState::TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
|
||||
const uint8_t *Data2) {
|
||||
if (!RecordingMemcmp || !F->InFuzzingThread()) return;
|
||||
CmpSize = std::min(CmpSize, Word::GetMaxSize());
|
||||
int Added2 = TryToAddDesiredData(Data1, Data2, CmpSize);
|
||||
int Added1 = TryToAddDesiredData(Data2, Data1, CmpSize);
|
||||
if ((Added1 || Added2) && Options.Verbosity >= 3) {
|
||||
Printf("MemCmp Added %d%d: ", Added1, Added2);
|
||||
if (Added1) PrintASCII(Data1, CmpSize);
|
||||
if (Added2) PrintASCII(Data2, CmpSize);
|
||||
Printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void TraceState::TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits,
|
||||
uint64_t Val, size_t NumCases,
|
||||
uint64_t *Cases) {
|
||||
if (F->InFuzzingThread()) return;
|
||||
size_t ValSize = ValSizeInBits / 8;
|
||||
bool TryShort = IsTwoByteData(Val);
|
||||
for (size_t i = 0; i < NumCases; i++)
|
||||
TryShort &= IsTwoByteData(Cases[i]);
|
||||
|
||||
if (Options.Verbosity >= 3)
|
||||
Printf("TraceSwitch: %p %zd # %zd; TryShort %d\n", PC, Val, NumCases,
|
||||
TryShort);
|
||||
|
||||
for (size_t i = 0; i < NumCases; i++) {
|
||||
TryToAddDesiredData(Val, Cases[i], ValSize);
|
||||
if (TryShort)
|
||||
TryToAddDesiredData(Val, Cases[i], 2);
|
||||
}
|
||||
}
|
||||
|
||||
static TraceState *TS;
|
||||
|
||||
void Fuzzer::StartTraceRecording() {
|
||||
if (!TS) return;
|
||||
TS->StartTraceRecording();
|
||||
}
|
||||
|
||||
void Fuzzer::StopTraceRecording() {
|
||||
if (!TS) return;
|
||||
TS->StopTraceRecording();
|
||||
}
|
||||
|
||||
void Fuzzer::InitializeTraceState() {
|
||||
if (!Options.UseMemcmp) return;
|
||||
TS = new TraceState(MD, Options, this);
|
||||
}
|
||||
|
||||
static size_t InternalStrnlen(const char *S, size_t MaxLen) {
|
||||
size_t Len = 0;
|
||||
for (; Len < MaxLen && S[Len]; Len++) {}
|
||||
return Len;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
using fuzzer::TS;
|
||||
using fuzzer::RecordingMemcmp;
|
||||
|
||||
extern "C" {
|
||||
|
||||
// We may need to avoid defining weak hooks to stay compatible with older clang.
|
||||
#ifndef LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
|
||||
# define LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS 1
|
||||
#endif
|
||||
|
||||
#if LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
|
||||
void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
|
||||
const void *s2, size_t n, int result) {
|
||||
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n);
|
||||
if (!RecordingMemcmp) return;
|
||||
if (result == 0) return; // No reason to mutate.
|
||||
if (n <= 1) return; // Not interesting.
|
||||
TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
|
||||
reinterpret_cast<const uint8_t *>(s2));
|
||||
}
|
||||
|
||||
void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1,
|
||||
const char *s2, size_t n, int result) {
|
||||
fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, n);
|
||||
if (!RecordingMemcmp) return;
|
||||
if (result == 0) return; // No reason to mutate.
|
||||
size_t Len1 = fuzzer::InternalStrnlen(s1, n);
|
||||
size_t Len2 = fuzzer::InternalStrnlen(s2, n);
|
||||
n = std::min(n, Len1);
|
||||
n = std::min(n, Len2);
|
||||
if (n <= 1) return; // Not interesting.
|
||||
TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
|
||||
reinterpret_cast<const uint8_t *>(s2));
|
||||
}
|
||||
|
||||
void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
|
||||
const char *s2, int result) {
|
||||
fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, 64);
|
||||
if (!RecordingMemcmp) return;
|
||||
if (result == 0) return; // No reason to mutate.
|
||||
size_t Len1 = strlen(s1);
|
||||
size_t Len2 = strlen(s2);
|
||||
size_t N = std::min(Len1, Len2);
|
||||
if (N <= 1) return; // Not interesting.
|
||||
TS->TraceMemcmpCallback(N, reinterpret_cast<const uint8_t *>(s1),
|
||||
reinterpret_cast<const uint8_t *>(s2));
|
||||
}
|
||||
|
||||
void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1,
|
||||
const char *s2, size_t n, int result) {
|
||||
return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result);
|
||||
}
|
||||
void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1,
|
||||
const char *s2, int result) {
|
||||
return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result);
|
||||
}
|
||||
void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1,
|
||||
const char *s2, char *result) {
|
||||
TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
|
||||
}
|
||||
void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1,
|
||||
const char *s2, char *result) {
|
||||
TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
|
||||
}
|
||||
void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1,
|
||||
const void *s2, size_t len2, void *result) {
|
||||
if (fuzzer::DoingMyOwnMemmem) return;
|
||||
TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), len2);
|
||||
}
|
||||
|
||||
#endif // LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
|
||||
} // extern "C"
|
218
test/fuzz_test/Fuzzer/FuzzerUtil.cpp
Normal file
218
test/fuzz_test/Fuzzer/FuzzerUtil.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
//===- FuzzerUtil.cpp - Misc utils ----------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Misc utils.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerUtil.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <thread>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
void PrintHexArray(const uint8_t *Data, size_t Size,
|
||||
const char *PrintAfter) {
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
Printf("0x%x,", (unsigned)Data[i]);
|
||||
Printf("%s", PrintAfter);
|
||||
}
|
||||
|
||||
void Print(const Unit &v, const char *PrintAfter) {
|
||||
PrintHexArray(v.data(), v.size(), PrintAfter);
|
||||
}
|
||||
|
||||
void PrintASCIIByte(uint8_t Byte) {
|
||||
if (Byte == '\\')
|
||||
Printf("\\\\");
|
||||
else if (Byte == '"')
|
||||
Printf("\\\"");
|
||||
else if (Byte >= 32 && Byte < 127)
|
||||
Printf("%c", Byte);
|
||||
else
|
||||
Printf("\\x%02x", Byte);
|
||||
}
|
||||
|
||||
void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter) {
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
PrintASCIIByte(Data[i]);
|
||||
Printf("%s", PrintAfter);
|
||||
}
|
||||
|
||||
void PrintASCII(const Unit &U, const char *PrintAfter) {
|
||||
PrintASCII(U.data(), U.size(), PrintAfter);
|
||||
}
|
||||
|
||||
bool ToASCII(uint8_t *Data, size_t Size) {
|
||||
bool Changed = false;
|
||||
for (size_t i = 0; i < Size; i++) {
|
||||
uint8_t &X = Data[i];
|
||||
auto NewX = X;
|
||||
NewX &= 127;
|
||||
if (!isspace(NewX) && !isprint(NewX))
|
||||
NewX = ' ';
|
||||
Changed |= NewX != X;
|
||||
X = NewX;
|
||||
}
|
||||
return Changed;
|
||||
}
|
||||
|
||||
bool IsASCII(const Unit &U) { return IsASCII(U.data(), U.size()); }
|
||||
|
||||
bool IsASCII(const uint8_t *Data, size_t Size) {
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
if (!(isprint(Data[i]) || isspace(Data[i]))) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) {
|
||||
U->clear();
|
||||
if (Str.empty()) return false;
|
||||
size_t L = 0, R = Str.size() - 1; // We are parsing the range [L,R].
|
||||
// Skip spaces from both sides.
|
||||
while (L < R && isspace(Str[L])) L++;
|
||||
while (R > L && isspace(Str[R])) R--;
|
||||
if (R - L < 2) return false;
|
||||
// Check the closing "
|
||||
if (Str[R] != '"') return false;
|
||||
R--;
|
||||
// Find the opening "
|
||||
while (L < R && Str[L] != '"') L++;
|
||||
if (L >= R) return false;
|
||||
assert(Str[L] == '\"');
|
||||
L++;
|
||||
assert(L <= R);
|
||||
for (size_t Pos = L; Pos <= R; Pos++) {
|
||||
uint8_t V = (uint8_t)Str[Pos];
|
||||
if (!isprint(V) && !isspace(V)) return false;
|
||||
if (V =='\\') {
|
||||
// Handle '\\'
|
||||
if (Pos + 1 <= R && (Str[Pos + 1] == '\\' || Str[Pos + 1] == '"')) {
|
||||
U->push_back(Str[Pos + 1]);
|
||||
Pos++;
|
||||
continue;
|
||||
}
|
||||
// Handle '\xAB'
|
||||
if (Pos + 3 <= R && Str[Pos + 1] == 'x'
|
||||
&& isxdigit(Str[Pos + 2]) && isxdigit(Str[Pos + 3])) {
|
||||
char Hex[] = "0xAA";
|
||||
Hex[2] = Str[Pos + 2];
|
||||
Hex[3] = Str[Pos + 3];
|
||||
U->push_back(strtol(Hex, nullptr, 16));
|
||||
Pos += 3;
|
||||
continue;
|
||||
}
|
||||
return false; // Invalid escape.
|
||||
} else {
|
||||
// Any other character.
|
||||
U->push_back(V);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseDictionaryFile(const std::string &Text, std::vector<Unit> *Units) {
|
||||
if (Text.empty()) {
|
||||
Printf("ParseDictionaryFile: file does not exist or is empty\n");
|
||||
return false;
|
||||
}
|
||||
std::istringstream ISS(Text);
|
||||
Units->clear();
|
||||
Unit U;
|
||||
int LineNo = 0;
|
||||
std::string S;
|
||||
while (std::getline(ISS, S, '\n')) {
|
||||
LineNo++;
|
||||
size_t Pos = 0;
|
||||
while (Pos < S.size() && isspace(S[Pos])) Pos++; // Skip spaces.
|
||||
if (Pos == S.size()) continue; // Empty line.
|
||||
if (S[Pos] == '#') continue; // Comment line.
|
||||
if (ParseOneDictionaryEntry(S, &U)) {
|
||||
Units->push_back(U);
|
||||
} else {
|
||||
Printf("ParseDictionaryFile: error in line %d\n\t\t%s\n", LineNo,
|
||||
S.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Base64(const Unit &U) {
|
||||
static const char Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
std::string Res;
|
||||
size_t i;
|
||||
for (i = 0; i + 2 < U.size(); i += 3) {
|
||||
uint32_t x = (U[i] << 16) + (U[i + 1] << 8) + U[i + 2];
|
||||
Res += Table[(x >> 18) & 63];
|
||||
Res += Table[(x >> 12) & 63];
|
||||
Res += Table[(x >> 6) & 63];
|
||||
Res += Table[x & 63];
|
||||
}
|
||||
if (i + 1 == U.size()) {
|
||||
uint32_t x = (U[i] << 16);
|
||||
Res += Table[(x >> 18) & 63];
|
||||
Res += Table[(x >> 12) & 63];
|
||||
Res += "==";
|
||||
} else if (i + 2 == U.size()) {
|
||||
uint32_t x = (U[i] << 16) + (U[i + 1] << 8);
|
||||
Res += Table[(x >> 18) & 63];
|
||||
Res += Table[(x >> 12) & 63];
|
||||
Res += Table[(x >> 6) & 63];
|
||||
Res += "=";
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) {
|
||||
if (!EF->__sanitizer_symbolize_pc) return "<can not symbolize>";
|
||||
char PcDescr[1024];
|
||||
EF->__sanitizer_symbolize_pc(reinterpret_cast<void*>(PC),
|
||||
SymbolizedFMT, PcDescr, sizeof(PcDescr));
|
||||
PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case.
|
||||
return PcDescr;
|
||||
}
|
||||
|
||||
void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) {
|
||||
if (EF->__sanitizer_symbolize_pc)
|
||||
Printf("%s", DescribePC(SymbolizedFMT, PC).c_str());
|
||||
else
|
||||
Printf(FallbackFMT, PC);
|
||||
}
|
||||
|
||||
unsigned NumberOfCpuCores() {
|
||||
unsigned N = std::thread::hardware_concurrency();
|
||||
if (!N) {
|
||||
Printf("WARNING: std::thread::hardware_concurrency not well defined for "
|
||||
"your platform. Assuming CPU count of 1.\n");
|
||||
N = 1;
|
||||
}
|
||||
return N;
|
||||
}
|
||||
|
||||
bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out) {
|
||||
FILE *Pipe = OpenProcessPipe(Command.c_str(), "r");
|
||||
if (!Pipe) return false;
|
||||
char Buff[1024];
|
||||
size_t N;
|
||||
while ((N = fread(Buff, 1, sizeof(Buff), Pipe)) > 0)
|
||||
Out->append(Buff, N);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
72
test/fuzz_test/Fuzzer/FuzzerUtil.h
Normal file
72
test/fuzz_test/Fuzzer/FuzzerUtil.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
//===- FuzzerUtil.h - Internal header for the Fuzzer Utils ------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Util functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_UTIL_H
|
||||
#define LLVM_FUZZER_UTIL_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
void PrintHexArray(const Unit &U, const char *PrintAfter = "");
|
||||
|
||||
void PrintHexArray(const uint8_t *Data, size_t Size,
|
||||
const char *PrintAfter = "");
|
||||
|
||||
void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter = "");
|
||||
|
||||
void PrintASCII(const Unit &U, const char *PrintAfter = "");
|
||||
|
||||
// Changes U to contain only ASCII (isprint+isspace) characters.
|
||||
// Returns true iff U has been changed.
|
||||
bool ToASCII(uint8_t *Data, size_t Size);
|
||||
|
||||
bool IsASCII(const Unit &U);
|
||||
|
||||
bool IsASCII(const uint8_t *Data, size_t Size);
|
||||
|
||||
std::string Base64(const Unit &U);
|
||||
|
||||
void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC);
|
||||
|
||||
std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC);
|
||||
|
||||
unsigned NumberOfCpuCores();
|
||||
|
||||
bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out);
|
||||
|
||||
// Platform specific functions.
|
||||
void SetSignalHandler(const FuzzingOptions& Options);
|
||||
|
||||
void SleepSeconds(int Seconds);
|
||||
|
||||
unsigned long GetPid();
|
||||
|
||||
size_t GetPeakRSSMb();
|
||||
|
||||
int ExecuteCommand(const std::string &Command);
|
||||
|
||||
FILE *OpenProcessPipe(const char *Command, const char *Mode);
|
||||
|
||||
const void *SearchMemory(const void *haystack, size_t haystacklen,
|
||||
const void *needle, size_t needlelen);
|
||||
|
||||
std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
|
||||
const char *X1, const char *X2);
|
||||
|
||||
inline std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
|
||||
const char *X) {
|
||||
return CloneArgsWithoutX(Args, X, X);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_UTIL_H
|
152
test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp
Normal file
152
test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp
Normal file
|
@ -0,0 +1,152 @@
|
|||
//===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Misc utils for Darwin.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_APPLE
|
||||
|
||||
#include "FuzzerIO.h"
|
||||
#include <mutex>
|
||||
#include <signal.h>
|
||||
#include <spawn.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
// There is no header for this on macOS so declare here
|
||||
extern "C" char **environ;
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
static std::mutex SignalMutex;
|
||||
// Global variables used to keep track of how signal handling should be
|
||||
// restored. They should **not** be accessed without holding `SignalMutex`.
|
||||
static int ActiveThreadCount = 0;
|
||||
static struct sigaction OldSigIntAction;
|
||||
static struct sigaction OldSigQuitAction;
|
||||
static sigset_t OldBlockedSignalsSet;
|
||||
|
||||
// This is a reimplementation of Libc's `system()`. On Darwin the Libc
|
||||
// implementation contains a mutex which prevents it from being used
|
||||
// concurrently. This implementation **can** be used concurrently. It sets the
|
||||
// signal handlers when the first thread enters and restores them when the last
|
||||
// thread finishes execution of the function and ensures this is not racey by
|
||||
// using a mutex.
|
||||
int ExecuteCommand(const std::string &Command) {
|
||||
posix_spawnattr_t SpawnAttributes;
|
||||
if (posix_spawnattr_init(&SpawnAttributes))
|
||||
return -1;
|
||||
// Block and ignore signals of the current process when the first thread
|
||||
// enters.
|
||||
{
|
||||
std::lock_guard<std::mutex> Lock(SignalMutex);
|
||||
if (ActiveThreadCount == 0) {
|
||||
static struct sigaction IgnoreSignalAction;
|
||||
sigset_t BlockedSignalsSet;
|
||||
memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction));
|
||||
IgnoreSignalAction.sa_handler = SIG_IGN;
|
||||
|
||||
if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) {
|
||||
Printf("Failed to ignore SIGINT\n");
|
||||
(void)posix_spawnattr_destroy(&SpawnAttributes);
|
||||
return -1;
|
||||
}
|
||||
if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) {
|
||||
Printf("Failed to ignore SIGQUIT\n");
|
||||
// Try our best to restore the signal handlers.
|
||||
(void)sigaction(SIGINT, &OldSigIntAction, NULL);
|
||||
(void)posix_spawnattr_destroy(&SpawnAttributes);
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void)sigemptyset(&BlockedSignalsSet);
|
||||
(void)sigaddset(&BlockedSignalsSet, SIGCHLD);
|
||||
if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) ==
|
||||
-1) {
|
||||
Printf("Failed to block SIGCHLD\n");
|
||||
// Try our best to restore the signal handlers.
|
||||
(void)sigaction(SIGQUIT, &OldSigQuitAction, NULL);
|
||||
(void)sigaction(SIGINT, &OldSigIntAction, NULL);
|
||||
(void)posix_spawnattr_destroy(&SpawnAttributes);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
++ActiveThreadCount;
|
||||
}
|
||||
|
||||
// NOTE: Do not introduce any new `return` statements past this
|
||||
// point. It is important that `ActiveThreadCount` always be decremented
|
||||
// when leaving this function.
|
||||
|
||||
// Make sure the child process uses the default handlers for the
|
||||
// following signals rather than inheriting what the parent has.
|
||||
sigset_t DefaultSigSet;
|
||||
(void)sigemptyset(&DefaultSigSet);
|
||||
(void)sigaddset(&DefaultSigSet, SIGQUIT);
|
||||
(void)sigaddset(&DefaultSigSet, SIGINT);
|
||||
(void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet);
|
||||
// Make sure the child process doesn't block SIGCHLD
|
||||
(void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet);
|
||||
short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
|
||||
(void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags);
|
||||
|
||||
pid_t Pid;
|
||||
char **Environ = environ; // Read from global
|
||||
const char *CommandCStr = Command.c_str();
|
||||
const char *Argv[] = {"sh", "-c", CommandCStr, NULL};
|
||||
int ErrorCode = 0, ProcessStatus = 0;
|
||||
// FIXME: We probably shouldn't hardcode the shell path.
|
||||
ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes,
|
||||
(char *const *)Argv, Environ);
|
||||
(void)posix_spawnattr_destroy(&SpawnAttributes);
|
||||
if (!ErrorCode) {
|
||||
pid_t SavedPid = Pid;
|
||||
do {
|
||||
// Repeat until call completes uninterrupted.
|
||||
Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0);
|
||||
} while (Pid == -1 && errno == EINTR);
|
||||
if (Pid == -1) {
|
||||
// Fail for some other reason.
|
||||
ProcessStatus = -1;
|
||||
}
|
||||
} else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) {
|
||||
// Fork failure.
|
||||
ProcessStatus = -1;
|
||||
} else {
|
||||
// Shell execution failure.
|
||||
ProcessStatus = W_EXITCODE(127, 0);
|
||||
}
|
||||
|
||||
// Restore the signal handlers of the current process when the last thread
|
||||
// using this function finishes.
|
||||
{
|
||||
std::lock_guard<std::mutex> Lock(SignalMutex);
|
||||
--ActiveThreadCount;
|
||||
if (ActiveThreadCount == 0) {
|
||||
bool FailedRestore = false;
|
||||
if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) {
|
||||
Printf("Failed to restore SIGINT handling\n");
|
||||
FailedRestore = true;
|
||||
}
|
||||
if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) {
|
||||
Printf("Failed to restore SIGQUIT handling\n");
|
||||
FailedRestore = true;
|
||||
}
|
||||
if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) {
|
||||
Printf("Failed to unblock SIGCHLD\n");
|
||||
FailedRestore = true;
|
||||
}
|
||||
if (FailedRestore)
|
||||
ProcessStatus = -1;
|
||||
}
|
||||
}
|
||||
return ProcessStatus;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_APPLE
|
24
test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp
Normal file
24
test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
//===- FuzzerUtilLinux.cpp - Misc utils for Linux. ------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Misc utils for Linux.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_LINUX
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
int ExecuteCommand(const std::string &Command) {
|
||||
return system(Command.c_str());
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_LINUX
|
117
test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp
Normal file
117
test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
//===- FuzzerUtilPosix.cpp - Misc utils for Posix. ------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Misc utils implementation using Posix API.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_POSIX
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <iomanip>
|
||||
#include <signal.h>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
static void AlarmHandler(int, siginfo_t *, void *) {
|
||||
Fuzzer::StaticAlarmCallback();
|
||||
}
|
||||
|
||||
static void CrashHandler(int, siginfo_t *, void *) {
|
||||
Fuzzer::StaticCrashSignalCallback();
|
||||
}
|
||||
|
||||
static void InterruptHandler(int, siginfo_t *, void *) {
|
||||
Fuzzer::StaticInterruptCallback();
|
||||
}
|
||||
|
||||
static void SetSigaction(int signum,
|
||||
void (*callback)(int, siginfo_t *, void *)) {
|
||||
struct sigaction sigact;
|
||||
memset(&sigact, 0, sizeof(sigact));
|
||||
sigact.sa_sigaction = callback;
|
||||
if (sigaction(signum, &sigact, 0)) {
|
||||
Printf("libFuzzer: sigaction failed with %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void SetTimer(int Seconds) {
|
||||
struct itimerval T {
|
||||
{Seconds, 0}, { Seconds, 0 }
|
||||
};
|
||||
if (setitimer(ITIMER_REAL, &T, nullptr)) {
|
||||
Printf("libFuzzer: setitimer failed with %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
SetSigaction(SIGALRM, AlarmHandler);
|
||||
}
|
||||
|
||||
void SetSignalHandler(const FuzzingOptions& Options) {
|
||||
if (Options.UnitTimeoutSec > 0)
|
||||
SetTimer(Options.UnitTimeoutSec / 2 + 1);
|
||||
if (Options.HandleInt)
|
||||
SetSigaction(SIGINT, InterruptHandler);
|
||||
if (Options.HandleTerm)
|
||||
SetSigaction(SIGTERM, InterruptHandler);
|
||||
if (Options.HandleSegv)
|
||||
SetSigaction(SIGSEGV, CrashHandler);
|
||||
if (Options.HandleBus)
|
||||
SetSigaction(SIGBUS, CrashHandler);
|
||||
if (Options.HandleAbrt)
|
||||
SetSigaction(SIGABRT, CrashHandler);
|
||||
if (Options.HandleIll)
|
||||
SetSigaction(SIGILL, CrashHandler);
|
||||
if (Options.HandleFpe)
|
||||
SetSigaction(SIGFPE, CrashHandler);
|
||||
}
|
||||
|
||||
void SleepSeconds(int Seconds) {
|
||||
sleep(Seconds); // Use C API to avoid coverage from instrumented libc++.
|
||||
}
|
||||
|
||||
unsigned long GetPid() { return (unsigned long)getpid(); }
|
||||
|
||||
size_t GetPeakRSSMb() {
|
||||
struct rusage usage;
|
||||
if (getrusage(RUSAGE_SELF, &usage))
|
||||
return 0;
|
||||
if (LIBFUZZER_LINUX) {
|
||||
// ru_maxrss is in KiB
|
||||
return usage.ru_maxrss >> 10;
|
||||
} else if (LIBFUZZER_APPLE) {
|
||||
// ru_maxrss is in bytes
|
||||
return usage.ru_maxrss >> 20;
|
||||
}
|
||||
assert(0 && "GetPeakRSSMb() is not implemented for your platform");
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE *OpenProcessPipe(const char *Command, const char *Mode) {
|
||||
return popen(Command, Mode);
|
||||
}
|
||||
|
||||
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
|
||||
size_t PattLen) {
|
||||
return memmem(Data, DataLen, Patt, PattLen);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_POSIX
|
182
test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp
Normal file
182
test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp
Normal file
|
@ -0,0 +1,182 @@
|
|||
//===- FuzzerUtilWindows.cpp - Misc utils for Windows. --------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Misc utils implementation for Windows.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <iomanip>
|
||||
#include <signal.h>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <windows.h>
|
||||
#include <Psapi.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
static const FuzzingOptions* HandlerOpt = nullptr;
|
||||
|
||||
LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
|
||||
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||
case EXCEPTION_STACK_OVERFLOW:
|
||||
if (HandlerOpt->HandleSegv)
|
||||
Fuzzer::StaticCrashSignalCallback();
|
||||
break;
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||
case EXCEPTION_IN_PAGE_ERROR:
|
||||
if (HandlerOpt->HandleBus)
|
||||
Fuzzer::StaticCrashSignalCallback();
|
||||
break;
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
case EXCEPTION_PRIV_INSTRUCTION:
|
||||
if (HandlerOpt->HandleIll)
|
||||
Fuzzer::StaticCrashSignalCallback();
|
||||
break;
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||
case EXCEPTION_FLT_OVERFLOW:
|
||||
case EXCEPTION_FLT_STACK_CHECK:
|
||||
case EXCEPTION_FLT_UNDERFLOW:
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
case EXCEPTION_INT_OVERFLOW:
|
||||
if (HandlerOpt->HandleFpe)
|
||||
Fuzzer::StaticCrashSignalCallback();
|
||||
break;
|
||||
}
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
BOOL WINAPI CtrlHandler(DWORD dwCtrlType) {
|
||||
switch (dwCtrlType) {
|
||||
case CTRL_C_EVENT:
|
||||
if (HandlerOpt->HandleInt)
|
||||
Fuzzer::StaticInterruptCallback();
|
||||
return TRUE;
|
||||
case CTRL_BREAK_EVENT:
|
||||
if (HandlerOpt->HandleTerm)
|
||||
Fuzzer::StaticInterruptCallback();
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CALLBACK AlarmHandler(PVOID, BOOLEAN) {
|
||||
Fuzzer::StaticAlarmCallback();
|
||||
}
|
||||
|
||||
class TimerQ {
|
||||
HANDLE TimerQueue;
|
||||
public:
|
||||
TimerQ() : TimerQueue(NULL) {};
|
||||
~TimerQ() {
|
||||
if (TimerQueue)
|
||||
DeleteTimerQueueEx(TimerQueue, NULL);
|
||||
};
|
||||
void SetTimer(int Seconds) {
|
||||
if (!TimerQueue) {
|
||||
TimerQueue = CreateTimerQueue();
|
||||
if (!TimerQueue) {
|
||||
Printf("libFuzzer: CreateTimerQueue failed.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
HANDLE Timer;
|
||||
if (!CreateTimerQueueTimer(&Timer, TimerQueue, AlarmHandler, NULL,
|
||||
Seconds*1000, Seconds*1000, 0)) {
|
||||
Printf("libFuzzer: CreateTimerQueueTimer failed.\n");
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
static TimerQ Timer;
|
||||
|
||||
static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); }
|
||||
|
||||
void SetSignalHandler(const FuzzingOptions& Options) {
|
||||
HandlerOpt = &Options;
|
||||
|
||||
if (Options.UnitTimeoutSec > 0)
|
||||
Timer.SetTimer(Options.UnitTimeoutSec / 2 + 1);
|
||||
|
||||
if (Options.HandleInt || Options.HandleTerm)
|
||||
if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) {
|
||||
DWORD LastError = GetLastError();
|
||||
Printf("libFuzzer: SetConsoleCtrlHandler failed (Error code: %lu).\n",
|
||||
LastError);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (Options.HandleSegv || Options.HandleBus || Options.HandleIll ||
|
||||
Options.HandleFpe)
|
||||
if (!AddVectoredExceptionHandler(1, ExceptionHandler)) {
|
||||
Printf("libFuzzer: AddVectoredExceptionHandler failed.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (Options.HandleAbrt)
|
||||
if (SIG_ERR == signal(SIGABRT, CrashHandler)) {
|
||||
Printf("libFuzzer: signal failed with %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void SleepSeconds(int Seconds) { Sleep(Seconds * 1000); }
|
||||
|
||||
unsigned long GetPid() { return GetCurrentProcessId(); }
|
||||
|
||||
size_t GetPeakRSSMb() {
|
||||
PROCESS_MEMORY_COUNTERS info;
|
||||
if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)))
|
||||
return 0;
|
||||
return info.PeakWorkingSetSize >> 20;
|
||||
}
|
||||
|
||||
FILE *OpenProcessPipe(const char *Command, const char *Mode) {
|
||||
return _popen(Command, Mode);
|
||||
}
|
||||
|
||||
int ExecuteCommand(const std::string &Command) {
|
||||
return system(Command.c_str());
|
||||
}
|
||||
|
||||
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
|
||||
size_t PattLen) {
|
||||
// TODO: make this implementation more efficient.
|
||||
const char *Cdata = (const char *)Data;
|
||||
const char *Cpatt = (const char *)Patt;
|
||||
|
||||
if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen)
|
||||
return NULL;
|
||||
|
||||
if (PattLen == 1)
|
||||
return memchr(Data, *Cpatt, DataLen);
|
||||
|
||||
const char *End = Cdata + DataLen - PattLen + 1;
|
||||
|
||||
for (const char *It = Cdata; It < End; ++It)
|
||||
if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0)
|
||||
return It;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
87
test/fuzz_test/Fuzzer/FuzzerValueBitMap.h
Normal file
87
test/fuzz_test/Fuzzer/FuzzerValueBitMap.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
//===- FuzzerValueBitMap.h - INTERNAL - Bit map -----------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ValueBitMap.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_VALUE_BIT_MAP_H
|
||||
#define LLVM_FUZZER_VALUE_BIT_MAP_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// A bit map containing kMapSizeInWords bits.
|
||||
struct ValueBitMap {
|
||||
static const size_t kMapSizeInBits = 65371; // Prime.
|
||||
static const size_t kMapSizeInBitsAligned = 65536; // 2^16
|
||||
static const size_t kBitsInWord = (sizeof(uintptr_t) * 8);
|
||||
static const size_t kMapSizeInWords = kMapSizeInBitsAligned / kBitsInWord;
|
||||
public:
|
||||
static const size_t kNumberOfItems = kMapSizeInBits;
|
||||
// Clears all bits.
|
||||
void Reset() { memset(Map, 0, sizeof(Map)); }
|
||||
|
||||
// Computes a hash function of Value and sets the corresponding bit.
|
||||
// Returns true if the bit was changed from 0 to 1.
|
||||
inline bool AddValue(uintptr_t Value) {
|
||||
uintptr_t Idx = Value < kMapSizeInBits ? Value : Value % kMapSizeInBits;
|
||||
uintptr_t WordIdx = Idx / kBitsInWord;
|
||||
uintptr_t BitIdx = Idx % kBitsInWord;
|
||||
uintptr_t Old = Map[WordIdx];
|
||||
uintptr_t New = Old | (1UL << BitIdx);
|
||||
Map[WordIdx] = New;
|
||||
return New != Old;
|
||||
}
|
||||
|
||||
inline bool Get(uintptr_t Idx) {
|
||||
assert(Idx < kMapSizeInBits);
|
||||
uintptr_t WordIdx = Idx / kBitsInWord;
|
||||
uintptr_t BitIdx = Idx % kBitsInWord;
|
||||
return Map[WordIdx] & (1UL << BitIdx);
|
||||
}
|
||||
|
||||
size_t GetNumBitsSinceLastMerge() const { return NumBits; }
|
||||
|
||||
// Merges 'Other' into 'this', clears 'Other', updates NumBits,
|
||||
// returns true if new bits were added.
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
bool MergeFrom(ValueBitMap &Other) {
|
||||
uintptr_t Res = 0;
|
||||
size_t OldNumBits = NumBits;
|
||||
for (size_t i = 0; i < kMapSizeInWords; i++) {
|
||||
auto O = Other.Map[i];
|
||||
auto M = Map[i];
|
||||
if (O) {
|
||||
Map[i] = (M |= O);
|
||||
Other.Map[i] = 0;
|
||||
}
|
||||
if (M)
|
||||
Res += __builtin_popcountl(M);
|
||||
}
|
||||
NumBits = Res;
|
||||
return OldNumBits < NumBits;
|
||||
}
|
||||
|
||||
template <class Callback>
|
||||
void ForEach(Callback CB) {
|
||||
for (size_t i = 0; i < kMapSizeInWords; i++)
|
||||
if (uintptr_t M = Map[i])
|
||||
for (size_t j = 0; j < sizeof(M) * 8; j++)
|
||||
if (M & ((uintptr_t)1 << j))
|
||||
CB(i * sizeof(M) * 8 + j);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t NumBits = 0;
|
||||
uintptr_t Map[kMapSizeInWords] __attribute__((aligned(512)));
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_VALUE_BIT_MAP_H
|
2
test/fuzz_test/Fuzzer/README.txt
Normal file
2
test/fuzz_test/Fuzzer/README.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Move to http://llvm.org/docs/LibFuzzer.html
|
||||
|
295
test/fuzz_test/Fuzzer/afl/afl_driver.cpp
Normal file
295
test/fuzz_test/Fuzzer/afl/afl_driver.cpp
Normal file
|
@ -0,0 +1,295 @@
|
|||
//===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/* This file allows to fuzz libFuzzer-style target functions
|
||||
(LLVMFuzzerTestOneInput) with AFL using AFL's persistent (in-process) mode.
|
||||
|
||||
Usage:
|
||||
################################################################################
|
||||
cat << EOF > test_fuzzer.cc
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
if (size > 0 && data[0] == 'H')
|
||||
if (size > 1 && data[1] == 'I')
|
||||
if (size > 2 && data[2] == '!')
|
||||
__builtin_trap();
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
# Build your target with -fsanitize-coverage=trace-pc using fresh clang.
|
||||
clang -g -fsanitize-coverage=trace-pc test_fuzzer.cc -c
|
||||
# Build afl-llvm-rt.o.c from the AFL distribution.
|
||||
clang -c -w $AFL_HOME/llvm_mode/afl-llvm-rt.o.c
|
||||
# Build this file, link it with afl-llvm-rt.o.o and the target code.
|
||||
clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o
|
||||
# Run AFL:
|
||||
rm -rf IN OUT; mkdir IN OUT; echo z > IN/z;
|
||||
$AFL_HOME/afl-fuzz -i IN -o OUT ./a.out
|
||||
################################################################################
|
||||
Environment Variables:
|
||||
There are a few environment variables that can be set to use features that
|
||||
afl-fuzz doesn't have.
|
||||
|
||||
AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file
|
||||
specified. If the file does not exist, it is created. This is useful for getting
|
||||
stack traces (when using ASAN for example) or original error messages on hard to
|
||||
reproduce bugs.
|
||||
|
||||
AFL_DRIVER_EXTRA_STATS_FILENAME: Setting this causes afl_driver to write extra
|
||||
statistics to the file specified. Currently these are peak_rss_mb
|
||||
(the peak amount of virtual memory used in MB) and slowest_unit_time_secs. If
|
||||
the file does not exist it is created. If the file does exist then
|
||||
afl_driver assumes it was restarted by afl-fuzz and will try to read old
|
||||
statistics from the file. If that fails then the process will quit.
|
||||
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
// Platform detection. Copied from FuzzerInternal.h
|
||||
#ifdef __linux__
|
||||
#define LIBFUZZER_LINUX 1
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#elif __APPLE__
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_APPLE 1
|
||||
#else
|
||||
#error "Support for your platform has not been implemented"
|
||||
#endif
|
||||
|
||||
// Used to avoid repeating error checking boilerplate. If cond is false, a
|
||||
// fatal error has occured in the program. In this event print error_message
|
||||
// to stderr and abort(). Otherwise do nothing. Note that setting
|
||||
// AFL_DRIVER_STDERR_DUPLICATE_FILENAME may cause error_message to be appended
|
||||
// to the file as well, if the error occurs after the duplication is performed.
|
||||
#define CHECK_ERROR(cond, error_message) \
|
||||
if (!(cond)) { \
|
||||
fprintf(stderr, (error_message)); \
|
||||
abort(); \
|
||||
}
|
||||
|
||||
// libFuzzer interface is thin, so we don't include any libFuzzer headers.
|
||||
extern "C" {
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||
__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv);
|
||||
}
|
||||
|
||||
// Notify AFL about persistent mode.
|
||||
static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##";
|
||||
extern "C" int __afl_persistent_loop(unsigned int);
|
||||
static volatile char suppress_warning2 = AFL_PERSISTENT[0];
|
||||
|
||||
// Notify AFL about deferred forkserver.
|
||||
static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##";
|
||||
extern "C" void __afl_manual_init();
|
||||
static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0];
|
||||
|
||||
// Input buffer.
|
||||
static const size_t kMaxAflInputSize = 1 << 20;
|
||||
static uint8_t AflInputBuf[kMaxAflInputSize];
|
||||
|
||||
// Variables we need for writing to the extra stats file.
|
||||
static FILE *extra_stats_file = NULL;
|
||||
static uint32_t previous_peak_rss = 0;
|
||||
static time_t slowest_unit_time_secs = 0;
|
||||
static const int kNumExtraStats = 2;
|
||||
static const char *kExtraStatsFormatString = "peak_rss_mb : %u\n"
|
||||
"slowest_unit_time_sec : %u\n";
|
||||
|
||||
// Copied from FuzzerUtil.cpp.
|
||||
size_t GetPeakRSSMb() {
|
||||
struct rusage usage;
|
||||
if (getrusage(RUSAGE_SELF, &usage))
|
||||
return 0;
|
||||
if (LIBFUZZER_LINUX) {
|
||||
// ru_maxrss is in KiB
|
||||
return usage.ru_maxrss >> 10;
|
||||
} else if (LIBFUZZER_APPLE) {
|
||||
// ru_maxrss is in bytes
|
||||
return usage.ru_maxrss >> 20;
|
||||
}
|
||||
assert(0 && "GetPeakRSSMb() is not implemented for your platform");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Based on SetSigaction in FuzzerUtil.cpp
|
||||
static void SetSigaction(int signum,
|
||||
void (*callback)(int, siginfo_t *, void *)) {
|
||||
struct sigaction sigact;
|
||||
memset(&sigact, 0, sizeof(sigact));
|
||||
sigact.sa_sigaction = callback;
|
||||
if (sigaction(signum, &sigact, 0)) {
|
||||
fprintf(stderr, "libFuzzer: sigaction failed with %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Write extra stats to the file specified by the user. If none is specified
|
||||
// this function will never be called.
|
||||
static void write_extra_stats() {
|
||||
uint32_t peak_rss = GetPeakRSSMb();
|
||||
|
||||
if (peak_rss < previous_peak_rss)
|
||||
peak_rss = previous_peak_rss;
|
||||
|
||||
int chars_printed = fprintf(extra_stats_file, kExtraStatsFormatString,
|
||||
peak_rss, slowest_unit_time_secs);
|
||||
|
||||
CHECK_ERROR(chars_printed != 0, "Failed to write extra_stats_file");
|
||||
|
||||
CHECK_ERROR(fclose(extra_stats_file) == 0,
|
||||
"Failed to close extra_stats_file");
|
||||
}
|
||||
|
||||
// Call write_extra_stats before we exit.
|
||||
static void crash_handler(int, siginfo_t *, void *) {
|
||||
// Make sure we don't try calling write_extra_stats again if we crashed while
|
||||
// trying to call it.
|
||||
static bool first_crash = true;
|
||||
CHECK_ERROR(first_crash,
|
||||
"Crashed in crash signal handler. This is a bug in the fuzzer.");
|
||||
|
||||
first_crash = false;
|
||||
write_extra_stats();
|
||||
}
|
||||
|
||||
// If the user has specified an extra_stats_file through the environment
|
||||
// variable AFL_DRIVER_EXTRA_STATS_FILENAME, then perform necessary set up
|
||||
// to write stats to it on exit. If no file is specified, do nothing. Otherwise
|
||||
// install signal and exit handlers to write to the file when the process exits.
|
||||
// Then if the file doesn't exist create it and set extra stats to 0. But if it
|
||||
// does exist then read the initial values of the extra stats from the file
|
||||
// and check that the file is writable.
|
||||
static void maybe_initialize_extra_stats() {
|
||||
// If AFL_DRIVER_EXTRA_STATS_FILENAME isn't set then we have nothing to do.
|
||||
char *extra_stats_filename = getenv("AFL_DRIVER_EXTRA_STATS_FILENAME");
|
||||
if (!extra_stats_filename)
|
||||
return;
|
||||
|
||||
// Open the file and find the previous peak_rss_mb value.
|
||||
// This is necessary because the fuzzing process is restarted after N
|
||||
// iterations are completed. So we may need to get this value from a previous
|
||||
// process to be accurate.
|
||||
extra_stats_file = fopen(extra_stats_filename, "r");
|
||||
|
||||
// If extra_stats_file already exists: read old stats from it.
|
||||
if (extra_stats_file) {
|
||||
int matches = fscanf(extra_stats_file, kExtraStatsFormatString,
|
||||
&previous_peak_rss, &slowest_unit_time_secs);
|
||||
|
||||
// Make sure we have read a real extra stats file and that we have used it
|
||||
// to set slowest_unit_time_secs and previous_peak_rss.
|
||||
CHECK_ERROR(matches == kNumExtraStats, "Extra stats file is corrupt");
|
||||
|
||||
CHECK_ERROR(fclose(extra_stats_file) == 0, "Failed to close file");
|
||||
|
||||
// Now open the file for writing.
|
||||
extra_stats_file = fopen(extra_stats_filename, "w");
|
||||
CHECK_ERROR(extra_stats_file,
|
||||
"Failed to open extra stats file for writing");
|
||||
} else {
|
||||
// Looks like this is the first time in a fuzzing job this is being called.
|
||||
extra_stats_file = fopen(extra_stats_filename, "w+");
|
||||
CHECK_ERROR(extra_stats_file, "failed to create extra stats file");
|
||||
}
|
||||
|
||||
// Make sure that crash_handler gets called on any kind of fatal error.
|
||||
int crash_signals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGILL, SIGFPE, SIGINT,
|
||||
SIGTERM};
|
||||
|
||||
const size_t num_signals = sizeof(crash_signals) / sizeof(crash_signals[0]);
|
||||
|
||||
for (size_t idx = 0; idx < num_signals; idx++)
|
||||
SetSigaction(crash_signals[idx], crash_handler);
|
||||
|
||||
// Make sure it gets called on other kinds of exits.
|
||||
atexit(write_extra_stats);
|
||||
}
|
||||
|
||||
// If the user asks us to duplicate stderr, then do it.
|
||||
static void maybe_duplicate_stderr() {
|
||||
char* stderr_duplicate_filename =
|
||||
getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
|
||||
|
||||
if (!stderr_duplicate_filename)
|
||||
return;
|
||||
|
||||
FILE* stderr_duplicate_stream =
|
||||
freopen(stderr_duplicate_filename, "a+", stderr);
|
||||
|
||||
if (!stderr_duplicate_stream) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
fprintf(stderr, "======================= INFO =========================\n"
|
||||
"This binary is built for AFL-fuzz.\n"
|
||||
"To run the target function on a single input execute this:\n"
|
||||
" %s < INPUT_FILE\n"
|
||||
"To run the fuzzing execute this:\n"
|
||||
" afl-fuzz [afl-flags] %s [N] "
|
||||
"-- run N fuzzing iterations before "
|
||||
"re-spawning the process (default: 1000)\n"
|
||||
"======================================================\n",
|
||||
argv[0], argv[0]);
|
||||
if (LLVMFuzzerInitialize)
|
||||
LLVMFuzzerInitialize(&argc, &argv);
|
||||
// Do any other expensive one-time initialization here.
|
||||
|
||||
maybe_duplicate_stderr();
|
||||
maybe_initialize_extra_stats();
|
||||
|
||||
__afl_manual_init();
|
||||
|
||||
int N = 1000;
|
||||
if (argc >= 2)
|
||||
N = atoi(argv[1]);
|
||||
assert(N > 0);
|
||||
time_t unit_time_secs;
|
||||
int num_runs = 0;
|
||||
while (__afl_persistent_loop(N)) {
|
||||
ssize_t n_read = read(0, AflInputBuf, kMaxAflInputSize);
|
||||
if (n_read > 0) {
|
||||
// Copy AflInputBuf into a separate buffer to let asan find buffer
|
||||
// overflows. Don't use unique_ptr/etc to avoid extra dependencies.
|
||||
uint8_t *copy = new uint8_t[n_read];
|
||||
memcpy(copy, AflInputBuf, n_read);
|
||||
|
||||
struct timeval unit_start_time;
|
||||
CHECK_ERROR(gettimeofday(&unit_start_time, NULL) == 0,
|
||||
"Calling gettimeofday failed");
|
||||
|
||||
num_runs++;
|
||||
LLVMFuzzerTestOneInput(copy, n_read);
|
||||
|
||||
struct timeval unit_stop_time;
|
||||
CHECK_ERROR(gettimeofday(&unit_stop_time, NULL) == 0,
|
||||
"Calling gettimeofday failed");
|
||||
|
||||
// Update slowest_unit_time_secs if we see a new max.
|
||||
unit_time_secs = unit_stop_time.tv_sec - unit_start_time.tv_sec;
|
||||
if (slowest_unit_time_secs < unit_time_secs)
|
||||
slowest_unit_time_secs = unit_time_secs;
|
||||
|
||||
delete[] copy;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "%s: successfully executed %d input(s)\n", argv[0], num_runs);
|
||||
}
|
10
test/fuzz_test/Fuzzer/build.sh
Executable file
10
test/fuzz_test/Fuzzer/build.sh
Executable file
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash
|
||||
LIBFUZZER_SRC_DIR=$(dirname $0)
|
||||
for f in $LIBFUZZER_SRC_DIR/*.cpp; do
|
||||
clang -g -O2 -fno-omit-frame-pointer -std=c++11 $f -c &
|
||||
done
|
||||
wait
|
||||
rm -f libFuzzer.a
|
||||
ar ru libFuzzer.a Fuzzer*.o
|
||||
rm -f Fuzzer*.o
|
||||
|
122
test/fuzz_test/Fuzzer/cxx.dict
Normal file
122
test/fuzz_test/Fuzzer/cxx.dict
Normal file
|
@ -0,0 +1,122 @@
|
|||
"++"
|
||||
"--"
|
||||
"<<"
|
||||
">>"
|
||||
"+="
|
||||
"-="
|
||||
"*="
|
||||
"/="
|
||||
">>="
|
||||
"<<="
|
||||
"&="
|
||||
"|="
|
||||
"^="
|
||||
"%="
|
||||
"!="
|
||||
"&&"
|
||||
"||"
|
||||
"=="
|
||||
">="
|
||||
"<="
|
||||
"->"
|
||||
"alignas"
|
||||
"alignof"
|
||||
"and"
|
||||
"and_eq"
|
||||
"asm"
|
||||
"auto"
|
||||
"bitand"
|
||||
"bitor"
|
||||
"bool"
|
||||
"break"
|
||||
"case"
|
||||
"catch"
|
||||
"char"
|
||||
"char16_t"
|
||||
"char32_t"
|
||||
"class"
|
||||
"compl"
|
||||
"concept"
|
||||
"const"
|
||||
"constexpr"
|
||||
"const_cast"
|
||||
"continue"
|
||||
"decltype"
|
||||
"default"
|
||||
"delete"
|
||||
"do"
|
||||
"double"
|
||||
"dynamic_cast"
|
||||
"else"
|
||||
"enum"
|
||||
"explicit"
|
||||
"export"
|
||||
"extern"
|
||||
"false"
|
||||
"float"
|
||||
"for"
|
||||
"friend"
|
||||
"goto"
|
||||
"if"
|
||||
"inline"
|
||||
"int"
|
||||
"long"
|
||||
"mutable"
|
||||
"namespace"
|
||||
"new"
|
||||
"noexcept"
|
||||
"not"
|
||||
"not_eq"
|
||||
"nullptr"
|
||||
"operator"
|
||||
"or"
|
||||
"or_eq"
|
||||
"private"
|
||||
"protected"
|
||||
"public"
|
||||
"register"
|
||||
"reinterpret_cast"
|
||||
"requires"
|
||||
"return"
|
||||
"short"
|
||||
"signed"
|
||||
"sizeof"
|
||||
"static"
|
||||
"static_assert"
|
||||
"static_cast"
|
||||
"struct"
|
||||
"switch"
|
||||
"template"
|
||||
"this"
|
||||
"thread_local"
|
||||
"throw"
|
||||
"true"
|
||||
"try"
|
||||
"typedef"
|
||||
"typeid"
|
||||
"typename"
|
||||
"union"
|
||||
"unsigned"
|
||||
"using"
|
||||
"virtual"
|
||||
"void"
|
||||
"volatile"
|
||||
"wchar_t"
|
||||
"while"
|
||||
"xor"
|
||||
"xor_eq"
|
||||
"if"
|
||||
"elif"
|
||||
"else"
|
||||
"endif"
|
||||
"defined"
|
||||
"ifdef"
|
||||
"ifndef"
|
||||
"define"
|
||||
"undef"
|
||||
"include"
|
||||
"line"
|
||||
"error"
|
||||
"pragma"
|
||||
"override"
|
||||
"final"
|
41
test/fuzz_test/Fuzzer/standalone/StandaloneFuzzTargetMain.c
Normal file
41
test/fuzz_test/Fuzzer/standalone/StandaloneFuzzTargetMain.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*===- StandaloneFuzzTargetMain.c - standalone main() for fuzz targets. ---===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This main() function can be linked to a fuzz target (i.e. a library
|
||||
// that exports LLVMFuzzerTestOneInput() and possibly LLVMFuzzerInitialize())
|
||||
// instead of libFuzzer. This main() function will not perform any fuzzing
|
||||
// but will simply feed all input files one by one to the fuzz target.
|
||||
//
|
||||
// Use this file to provide reproducers for bugs when linking against libFuzzer
|
||||
// or other fuzzing engine is undesirable.
|
||||
//===----------------------------------------------------------------------===*/
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size);
|
||||
__attribute__((weak)) extern int LLVMFuzzerInitialize(int *argc, char ***argv);
|
||||
int main(int argc, char **argv) {
|
||||
fprintf(stderr, "StandaloneFuzzTargetMain: running %d inputs\n", argc - 1);
|
||||
if (LLVMFuzzerInitialize)
|
||||
LLVMFuzzerInitialize(&argc, &argv);
|
||||
for (int i = 1; i < argc; i++) {
|
||||
fprintf(stderr, "Running: %s\n", argv[i]);
|
||||
FILE *f = fopen(argv[i], "r");
|
||||
assert(f);
|
||||
fseek(f, 0, SEEK_END);
|
||||
size_t len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
unsigned char *buf = (unsigned char*)malloc(len);
|
||||
size_t n_read = fread(buf, 1, len, f);
|
||||
assert(n_read == len);
|
||||
LLVMFuzzerTestOneInput(buf, len);
|
||||
free(buf);
|
||||
fprintf(stderr, "Done: %s: (%zd bytes)\n", argv[i], n_read);
|
||||
}
|
||||
}
|
22
test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp
Normal file
22
test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Contains dummy functions used to avoid dependency on AFL.
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern "C" void __afl_manual_init() {}
|
||||
|
||||
extern "C" int __afl_persistent_loop(unsigned int) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This declaration exists to prevent the Darwin linker
|
||||
// from complaining about this being a missing weak symbol.
|
||||
extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
return 0;
|
||||
}
|
23
test/fuzz_test/Fuzzer/test/AbsNegAndConstant64Test.cpp
Normal file
23
test/fuzz_test/Fuzzer/test/AbsNegAndConstant64Test.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// abs(x) < 0 and y == Const puzzle, 64-bit variant.
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < 16) return 0;
|
||||
int64_t x;
|
||||
uint64_t y;
|
||||
memcpy(&x, Data, sizeof(x));
|
||||
memcpy(&y, Data + sizeof(x), sizeof(y));
|
||||
if (labs(x) < 0 && y == 0xbaddcafedeadbeefUL) {
|
||||
printf("BINGO; Found the target, exiting; x = 0x%lx y 0x%lx\n", x, y);
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
23
test/fuzz_test/Fuzzer/test/AbsNegAndConstantTest.cpp
Normal file
23
test/fuzz_test/Fuzzer/test/AbsNegAndConstantTest.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// abs(x) < 0 and y == Const puzzle.
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < 8) return 0;
|
||||
int x;
|
||||
unsigned y;
|
||||
memcpy(&x, Data, sizeof(x));
|
||||
memcpy(&y, Data + sizeof(x), sizeof(y));
|
||||
if (abs(x) < 0 && y == 0xbaddcafe) {
|
||||
printf("BINGO; Found the target, exiting; x = 0x%x y 0x%x\n", x, y);
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
17
test/fuzz_test/Fuzzer/test/AccumulateAllocationsTest.cpp
Normal file
17
test/fuzz_test/Fuzzer/test/AccumulateAllocationsTest.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Test with a more mallocs than frees, but no leak.
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
const int kAllocatedPointersSize = 10000;
|
||||
int NumAllocatedPointers = 0;
|
||||
int *AllocatedPointers[kAllocatedPointersSize];
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (NumAllocatedPointers < kAllocatedPointersSize)
|
||||
AllocatedPointers[NumAllocatedPointers++] = new int;
|
||||
return 0;
|
||||
}
|
||||
|
23
test/fuzz_test/Fuzzer/test/BufferOverflowOnInput.cpp
Normal file
23
test/fuzz_test/Fuzzer/test/BufferOverflowOnInput.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find the string "Hi!".
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
static volatile bool SeedLargeBuffer;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
assert(Data);
|
||||
if (Size >= 4)
|
||||
SeedLargeBuffer = true;
|
||||
if (Size == 3 && SeedLargeBuffer && Data[3]) {
|
||||
std::cout << "Woops, reading Data[3] w/o crashing\n";
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
217
test/fuzz_test/Fuzzer/test/CMakeLists.txt
Normal file
217
test/fuzz_test/Fuzzer/test/CMakeLists.txt
Normal file
|
@ -0,0 +1,217 @@
|
|||
# Build all these tests with -O0, otherwise optimizations may merge some
|
||||
# basic blocks and we'll fail to discover the targets.
|
||||
# We change the flags for every build type because we might be doing
|
||||
# a multi-configuration build (e.g. Xcode) where CMAKE_BUILD_TYPE doesn't
|
||||
# mean anything.
|
||||
set(variables_to_filter
|
||||
CMAKE_CXX_FLAGS_RELEASE
|
||||
CMAKE_CXX_FLAGS_DEBUG
|
||||
CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_CXX_FLAGS_MINSIZEREL
|
||||
LIBFUZZER_FLAGS_BASE
|
||||
)
|
||||
foreach (VARNAME ${variables_to_filter})
|
||||
string(REPLACE " " ";" BUILD_FLAGS_AS_LIST "${${VARNAME}}")
|
||||
set(new_flags "")
|
||||
foreach (flag ${BUILD_FLAGS_AS_LIST})
|
||||
# NOTE: Use of XX here is to avoid a CMake warning due to CMP0054
|
||||
if (NOT ("XX${flag}" MATCHES "XX-O[0123s]"))
|
||||
set(new_flags "${new_flags} ${flag}")
|
||||
else()
|
||||
set(new_flags "${new_flags} -O0")
|
||||
endif()
|
||||
endforeach()
|
||||
set(${VARNAME} "${new_flags}")
|
||||
endforeach()
|
||||
|
||||
# Enable the coverage instrumentation (it is disabled for the Fuzzer lib).
|
||||
set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp,trace-div,trace-gep -g")
|
||||
|
||||
# add_libfuzzer_test(<name>
|
||||
# SOURCES source0.cpp [source1.cpp ...]
|
||||
# )
|
||||
#
|
||||
# Declares a LibFuzzer test executable with target name LLVMFuzzer-<name>.
|
||||
#
|
||||
# One or more source files to be compiled into the binary must be declared
|
||||
# after the SOURCES keyword.
|
||||
function(add_libfuzzer_test name)
|
||||
set(multi_arg_options "SOURCES")
|
||||
cmake_parse_arguments(
|
||||
"add_libfuzzer_test" "" "" "${multi_arg_options}" ${ARGN})
|
||||
if ("${add_libfuzzer_test_SOURCES}" STREQUAL "")
|
||||
message(FATAL_ERROR "Source files must be specified")
|
||||
endif()
|
||||
add_executable(LLVMFuzzer-${name}
|
||||
${add_libfuzzer_test_SOURCES}
|
||||
)
|
||||
target_link_libraries(LLVMFuzzer-${name} LLVMFuzzer)
|
||||
# Place binary where llvm-lit expects to find it
|
||||
set_target_properties(LLVMFuzzer-${name}
|
||||
PROPERTIES RUNTIME_OUTPUT_DIRECTORY
|
||||
"${CMAKE_BINARY_DIR}/lib/Fuzzer/test"
|
||||
)
|
||||
set(TestBinaries ${TestBinaries} LLVMFuzzer-${name} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Variable to keep track of all test targets
|
||||
set(TestBinaries)
|
||||
|
||||
###############################################################################
|
||||
# Basic tests
|
||||
###############################################################################
|
||||
|
||||
set(Tests
|
||||
AbsNegAndConstantTest
|
||||
AbsNegAndConstant64Test
|
||||
AccumulateAllocationsTest
|
||||
BufferOverflowOnInput
|
||||
CallerCalleeTest
|
||||
CounterTest
|
||||
CustomCrossOverTest
|
||||
CustomMutatorTest
|
||||
DivTest
|
||||
EmptyTest
|
||||
FourIndependentBranchesTest
|
||||
FullCoverageSetTest
|
||||
InitializeTest
|
||||
MemcmpTest
|
||||
LeakTest
|
||||
LeakTimeoutTest
|
||||
LoadTest
|
||||
NullDerefTest
|
||||
NullDerefOnEmptyTest
|
||||
NthRunCrashTest
|
||||
OneHugeAllocTest
|
||||
OutOfMemoryTest
|
||||
OutOfMemorySingleLargeMallocTest
|
||||
RepeatedMemcmp
|
||||
RepeatedBytesTest
|
||||
SimpleCmpTest
|
||||
SimpleDictionaryTest
|
||||
SimpleHashTest
|
||||
SimpleTest
|
||||
SimpleThreadedTest
|
||||
SingleMemcmpTest
|
||||
SingleStrcmpTest
|
||||
SingleStrncmpTest
|
||||
SpamyTest
|
||||
ShrinkControlFlowTest
|
||||
ShrinkValueProfileTest
|
||||
StrcmpTest
|
||||
StrncmpOOBTest
|
||||
StrncmpTest
|
||||
StrstrTest
|
||||
SwapCmpTest
|
||||
SwitchTest
|
||||
Switch2Test
|
||||
ThreadedLeakTest
|
||||
ThreadedTest
|
||||
TimeoutTest
|
||||
TimeoutEmptyTest
|
||||
TraceMallocTest
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
# LeakSanitizer is not supported on OSX right now
|
||||
set(HAS_LSAN 0)
|
||||
message(WARNING "LeakSanitizer is not supported on Apple platforms."
|
||||
" Building and running LibFuzzer LeakSanitizer tests is disabled."
|
||||
)
|
||||
else()
|
||||
set(HAS_LSAN 1)
|
||||
endif()
|
||||
|
||||
foreach(Test ${Tests})
|
||||
add_libfuzzer_test(${Test} SOURCES ${Test}.cpp)
|
||||
endforeach()
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Unit tests
|
||||
###############################################################################
|
||||
|
||||
add_executable(LLVMFuzzer-Unittest
|
||||
FuzzerUnittest.cpp
|
||||
)
|
||||
|
||||
add_executable(LLVMFuzzer-StandaloneInitializeTest
|
||||
InitializeTest.cpp
|
||||
../standalone/StandaloneFuzzTargetMain.c
|
||||
)
|
||||
|
||||
target_link_libraries(LLVMFuzzer-Unittest
|
||||
gtest
|
||||
gtest_main
|
||||
LLVMFuzzerNoMain
|
||||
)
|
||||
|
||||
target_include_directories(LLVMFuzzer-Unittest PRIVATE
|
||||
"${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include"
|
||||
)
|
||||
|
||||
set(TestBinaries ${TestBinaries} LLVMFuzzer-Unittest)
|
||||
set_target_properties(LLVMFuzzer-Unittest
|
||||
PROPERTIES RUNTIME_OUTPUT_DIRECTORY
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
|
||||
set(TestBinaries ${TestBinaries} LLVMFuzzer-StandaloneInitializeTest)
|
||||
set_target_properties(LLVMFuzzer-StandaloneInitializeTest
|
||||
PROPERTIES RUNTIME_OUTPUT_DIRECTORY
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# Additional tests
|
||||
###############################################################################
|
||||
|
||||
include_directories(..)
|
||||
|
||||
# add_subdirectory(uninstrumented)
|
||||
add_subdirectory(no-coverage)
|
||||
add_subdirectory(ubsan)
|
||||
|
||||
add_library(LLVMFuzzer-DSO1 SHARED DSO1.cpp)
|
||||
add_library(LLVMFuzzer-DSO2 SHARED DSO2.cpp)
|
||||
|
||||
add_executable(LLVMFuzzer-DSOTest
|
||||
DSOTestMain.cpp
|
||||
DSOTestExtra.cpp)
|
||||
|
||||
target_link_libraries(LLVMFuzzer-DSOTest
|
||||
LLVMFuzzer-DSO1
|
||||
LLVMFuzzer-DSO2
|
||||
LLVMFuzzer
|
||||
)
|
||||
|
||||
set_target_properties(LLVMFuzzer-DSOTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY
|
||||
"${CMAKE_BINARY_DIR}/lib/Fuzzer/test")
|
||||
set_target_properties(LLVMFuzzer-DSO1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||
"${CMAKE_BINARY_DIR}/lib/Fuzzer/lib")
|
||||
set_target_properties(LLVMFuzzer-DSO2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||
"${CMAKE_BINARY_DIR}/lib/Fuzzer/lib")
|
||||
|
||||
set(TestBinaries ${TestBinaries} LLVMFuzzer-DSOTest)
|
||||
|
||||
###############################################################################
|
||||
# Configure lit to run the tests
|
||||
#
|
||||
# Note this is done after declaring all tests so we can inform lit if any tests
|
||||
# need to be disabled.
|
||||
###############################################################################
|
||||
|
||||
configure_lit_site_cfg(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
|
||||
)
|
||||
|
||||
configure_lit_site_cfg(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/unit/lit.site.cfg.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/unit/lit.site.cfg
|
||||
)
|
||||
|
||||
add_lit_testsuite(check-fuzzer "Running Fuzzer tests"
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS ${TestBinaries} FileCheck not
|
||||
)
|
59
test/fuzz_test/Fuzzer/test/CallerCalleeTest.cpp
Normal file
59
test/fuzz_test/Fuzzer/test/CallerCalleeTest.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer.
|
||||
// Try to find the target using the indirect caller-callee pairs.
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
typedef void (*F)();
|
||||
static F t[256];
|
||||
|
||||
void f34() {
|
||||
std::cerr << "BINGO\n";
|
||||
exit(1);
|
||||
}
|
||||
void f23() { t[(unsigned)'d'] = f34;}
|
||||
void f12() { t[(unsigned)'c'] = f23;}
|
||||
void f01() { t[(unsigned)'b'] = f12;}
|
||||
void f00() {}
|
||||
|
||||
static F t0[256] = {
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
|
||||
};
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < 4) return 0;
|
||||
// Spoof the counters.
|
||||
for (int i = 0; i < 200; i++) {
|
||||
f23();
|
||||
f12();
|
||||
f01();
|
||||
}
|
||||
memcpy(t, t0, sizeof(t));
|
||||
t[(unsigned)'a'] = f01;
|
||||
t[Data[0]]();
|
||||
t[Data[1]]();
|
||||
t[Data[2]]();
|
||||
t[Data[3]]();
|
||||
return 0;
|
||||
}
|
||||
|
18
test/fuzz_test/Fuzzer/test/CounterTest.cpp
Normal file
18
test/fuzz_test/Fuzzer/test/CounterTest.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Test for a fuzzer: must find the case where a particular basic block is
|
||||
// executed many times.
|
||||
#include <iostream>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
int Num = 0;
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
if (Data[i] == 'A' + i)
|
||||
Num++;
|
||||
if (Num >= 4) {
|
||||
std::cerr << "BINGO!\n";
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
63
test/fuzz_test/Fuzzer/test/CustomCrossOverTest.cpp
Normal file
63
test/fuzz_test/Fuzzer/test/CustomCrossOverTest.cpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a cutom mutator.
|
||||
#include <assert.h>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <string.h>
|
||||
|
||||
#include "FuzzerInterface.h"
|
||||
|
||||
static const char *Separator = "-_^_-";
|
||||
static const char *Target = "012-_^_-abc";
|
||||
|
||||
static volatile int sink;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
assert(Data);
|
||||
std::string Str(reinterpret_cast<const char *>(Data), Size);
|
||||
|
||||
// Ensure that two different elements exist in the corpus.
|
||||
if (Size && Data[0] == '0') sink++;
|
||||
if (Size && Data[0] == 'a') sink--;
|
||||
|
||||
if (Str.find(Target) != std::string::npos) {
|
||||
std::cout << "BINGO; Found the target, exiting\n";
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
|
||||
const uint8_t *Data2, size_t Size2,
|
||||
uint8_t *Out, size_t MaxOutSize,
|
||||
unsigned int Seed) {
|
||||
static bool Printed;
|
||||
static size_t SeparatorLen = strlen(Separator);
|
||||
|
||||
if (!Printed) {
|
||||
std::cerr << "In LLVMFuzzerCustomCrossover\n";
|
||||
Printed = true;
|
||||
}
|
||||
|
||||
std::mt19937 R(Seed);
|
||||
|
||||
size_t Offset1 = 0;
|
||||
size_t Len1 = R() % (Size1 - Offset1);
|
||||
size_t Offset2 = 0;
|
||||
size_t Len2 = R() % (Size2 - Offset2);
|
||||
size_t Size = Len1 + Len2 + SeparatorLen;
|
||||
|
||||
if (Size > MaxOutSize)
|
||||
return 0;
|
||||
|
||||
memcpy(Out, Data1 + Offset1, Len1);
|
||||
memcpy(Out + Len1, Separator, SeparatorLen);
|
||||
memcpy(Out + Len1 + SeparatorLen, Data2 + Offset2, Len2);
|
||||
|
||||
return Len1 + Len2 + SeparatorLen;
|
||||
}
|
38
test/fuzz_test/Fuzzer/test/CustomMutatorTest.cpp
Normal file
38
test/fuzz_test/Fuzzer/test/CustomMutatorTest.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a cutom mutator.
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
#include "FuzzerInterface.h"
|
||||
|
||||
static volatile int Sink;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
assert(Data);
|
||||
if (Size > 0 && Data[0] == 'H') {
|
||||
Sink = 1;
|
||||
if (Size > 1 && Data[1] == 'i') {
|
||||
Sink = 2;
|
||||
if (Size > 2 && Data[2] == '!') {
|
||||
std::cout << "BINGO; Found the target, exiting\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize, unsigned int Seed) {
|
||||
static bool Printed;
|
||||
if (!Printed) {
|
||||
std::cerr << "In LLVMFuzzerCustomMutator\n";
|
||||
Printed = true;
|
||||
}
|
||||
return LLVMFuzzerMutate(Data, Size, MaxSize);
|
||||
}
|
12
test/fuzz_test/Fuzzer/test/DSO1.cpp
Normal file
12
test/fuzz_test/Fuzzer/test/DSO1.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Source code for a simple DSO.
|
||||
|
||||
int DSO1(int a) {
|
||||
if (a < 123456)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Uncovered1() { }
|
12
test/fuzz_test/Fuzzer/test/DSO2.cpp
Normal file
12
test/fuzz_test/Fuzzer/test/DSO2.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Source code for a simple DSO.
|
||||
|
||||
int DSO2(int a) {
|
||||
if (a < 3598235)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Uncovered2() {}
|
11
test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp
Normal file
11
test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Source code for a simple DSO.
|
||||
|
||||
int DSOTestExtra(int a) {
|
||||
if (a < 452345)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
31
test/fuzz_test/Fuzzer/test/DSOTestMain.cpp
Normal file
31
test/fuzz_test/Fuzzer/test/DSOTestMain.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Source code for a simple DSO.
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
extern int DSO1(int a);
|
||||
extern int DSO2(int a);
|
||||
extern int DSOTestExtra(int a);
|
||||
|
||||
static volatile int *nil = 0;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
int x, y, z;
|
||||
if (Size < sizeof(int) * 3) {
|
||||
x = y = z = 0;
|
||||
} else {
|
||||
memcpy(&x, Data + 0 * sizeof(int), sizeof(int));
|
||||
memcpy(&y, Data + 1 * sizeof(int), sizeof(int));
|
||||
memcpy(&z, Data + 2 * sizeof(int), sizeof(int));
|
||||
}
|
||||
int sum = DSO1(x) + DSO2(y) + (z ? DSOTestExtra(z) : 0);
|
||||
if (sum == 3) {
|
||||
fprintf(stderr, "BINGO %d %d %d\n", x, y, z);
|
||||
*nil = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
20
test/fuzz_test/Fuzzer/test/DivTest.cpp
Normal file
20
test/fuzz_test/Fuzzer/test/DivTest.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer: find the interesting argument for div.
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
static volatile int Sink;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < 4) return 0;
|
||||
int a;
|
||||
memcpy(&a, Data, 4);
|
||||
Sink = 12345678 / (987654 - a);
|
||||
return 0;
|
||||
}
|
||||
|
11
test/fuzz_test/Fuzzer/test/EmptyTest.cpp
Normal file
11
test/fuzz_test/Fuzzer/test/EmptyTest.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
// A fuzzer with empty target function.
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
return 0;
|
||||
}
|
22
test/fuzz_test/Fuzzer/test/FourIndependentBranchesTest.cpp
Normal file
22
test/fuzz_test/Fuzzer/test/FourIndependentBranchesTest.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find the string "FUZZ".
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
int bits = 0;
|
||||
if (Size > 0 && Data[0] == 'F') bits |= 1;
|
||||
if (Size > 1 && Data[1] == 'U') bits |= 2;
|
||||
if (Size > 2 && Data[2] == 'Z') bits |= 4;
|
||||
if (Size > 3 && Data[3] == 'Z') bits |= 8;
|
||||
if (bits == 15) {
|
||||
std::cerr << "BINGO!\n";
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
24
test/fuzz_test/Fuzzer/test/FullCoverageSetTest.cpp
Normal file
24
test/fuzz_test/Fuzzer/test/FullCoverageSetTest.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find the string "FUZZER".
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
int bits = 0;
|
||||
if (Size > 0 && Data[0] == 'F') bits |= 1;
|
||||
if (Size > 1 && Data[1] == 'U') bits |= 2;
|
||||
if (Size > 2 && Data[2] == 'Z') bits |= 4;
|
||||
if (Size > 3 && Data[3] == 'Z') bits |= 8;
|
||||
if (Size > 4 && Data[4] == 'E') bits |= 16;
|
||||
if (Size > 5 && Data[5] == 'R') bits |= 32;
|
||||
if (bits == 63) {
|
||||
std::cerr << "BINGO!\n";
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
738
test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp
Normal file
738
test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp
Normal file
|
@ -0,0 +1,738 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Avoid ODR violations (LibFuzzer is built without ASan and this test is built
|
||||
// with ASan) involving C++ standard library types when using libcxx.
|
||||
#define _LIBCPP_HAS_NO_ASAN
|
||||
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerDictionary.h"
|
||||
#include "FuzzerMerge.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
using namespace fuzzer;
|
||||
|
||||
// For now, have LLVMFuzzerTestOneInput just to make it link.
|
||||
// Later we may want to make unittests that actually call LLVMFuzzerTestOneInput.
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
abort();
|
||||
}
|
||||
|
||||
TEST(Fuzzer, CrossOver) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
MutationDispatcher MD(Rand, {});
|
||||
Unit A({0, 1, 2}), B({5, 6, 7});
|
||||
Unit C;
|
||||
Unit Expected[] = {
|
||||
{ 0 },
|
||||
{ 0, 1 },
|
||||
{ 0, 5 },
|
||||
{ 0, 1, 2 },
|
||||
{ 0, 1, 5 },
|
||||
{ 0, 5, 1 },
|
||||
{ 0, 5, 6 },
|
||||
{ 0, 1, 2, 5 },
|
||||
{ 0, 1, 5, 2 },
|
||||
{ 0, 1, 5, 6 },
|
||||
{ 0, 5, 1, 2 },
|
||||
{ 0, 5, 1, 6 },
|
||||
{ 0, 5, 6, 1 },
|
||||
{ 0, 5, 6, 7 },
|
||||
{ 0, 1, 2, 5, 6 },
|
||||
{ 0, 1, 5, 2, 6 },
|
||||
{ 0, 1, 5, 6, 2 },
|
||||
{ 0, 1, 5, 6, 7 },
|
||||
{ 0, 5, 1, 2, 6 },
|
||||
{ 0, 5, 1, 6, 2 },
|
||||
{ 0, 5, 1, 6, 7 },
|
||||
{ 0, 5, 6, 1, 2 },
|
||||
{ 0, 5, 6, 1, 7 },
|
||||
{ 0, 5, 6, 7, 1 },
|
||||
{ 0, 1, 2, 5, 6, 7 },
|
||||
{ 0, 1, 5, 2, 6, 7 },
|
||||
{ 0, 1, 5, 6, 2, 7 },
|
||||
{ 0, 1, 5, 6, 7, 2 },
|
||||
{ 0, 5, 1, 2, 6, 7 },
|
||||
{ 0, 5, 1, 6, 2, 7 },
|
||||
{ 0, 5, 1, 6, 7, 2 },
|
||||
{ 0, 5, 6, 1, 2, 7 },
|
||||
{ 0, 5, 6, 1, 7, 2 },
|
||||
{ 0, 5, 6, 7, 1, 2 }
|
||||
};
|
||||
for (size_t Len = 1; Len < 8; Len++) {
|
||||
std::set<Unit> FoundUnits, ExpectedUnitsWitThisLength;
|
||||
for (int Iter = 0; Iter < 3000; Iter++) {
|
||||
C.resize(Len);
|
||||
size_t NewSize = MD.CrossOver(A.data(), A.size(), B.data(), B.size(),
|
||||
C.data(), C.size());
|
||||
C.resize(NewSize);
|
||||
FoundUnits.insert(C);
|
||||
}
|
||||
for (const Unit &U : Expected)
|
||||
if (U.size() <= Len)
|
||||
ExpectedUnitsWitThisLength.insert(U);
|
||||
EXPECT_EQ(ExpectedUnitsWitThisLength, FoundUnits);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Fuzzer, Hash) {
|
||||
uint8_t A[] = {'a', 'b', 'c'};
|
||||
fuzzer::Unit U(A, A + sizeof(A));
|
||||
EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d", fuzzer::Hash(U));
|
||||
U.push_back('d');
|
||||
EXPECT_EQ("81fe8bfe87576c3ecb22426f8e57847382917acf", fuzzer::Hash(U));
|
||||
}
|
||||
|
||||
typedef size_t (MutationDispatcher::*Mutator)(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
|
||||
void TestEraseBytes(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
uint8_t REM0[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM1[8] = {0x00, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM2[8] = {0x00, 0x11, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM3[8] = {0x00, 0x11, 0x22, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM4[8] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x66, 0x77};
|
||||
uint8_t REM5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x66, 0x77};
|
||||
uint8_t REM6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x77};
|
||||
uint8_t REM7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
|
||||
uint8_t REM8[6] = {0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM9[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
|
||||
uint8_t REM10[6] = {0x00, 0x11, 0x22, 0x55, 0x66, 0x77};
|
||||
|
||||
uint8_t REM11[5] = {0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM12[5] = {0x00, 0x11, 0x22, 0x33, 0x44};
|
||||
uint8_t REM13[5] = {0x00, 0x44, 0x55, 0x66, 0x77};
|
||||
|
||||
|
||||
Random Rand(0);
|
||||
MutationDispatcher MD(Rand, {});
|
||||
int FoundMask = 0;
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (MD.*M)(T, sizeof(T), sizeof(T));
|
||||
if (NewSize == 7 && !memcmp(REM0, T, 7)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(REM1, T, 7)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(REM2, T, 7)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(REM3, T, 7)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 7 && !memcmp(REM4, T, 7)) FoundMask |= 1 << 4;
|
||||
if (NewSize == 7 && !memcmp(REM5, T, 7)) FoundMask |= 1 << 5;
|
||||
if (NewSize == 7 && !memcmp(REM6, T, 7)) FoundMask |= 1 << 6;
|
||||
if (NewSize == 7 && !memcmp(REM7, T, 7)) FoundMask |= 1 << 7;
|
||||
|
||||
if (NewSize == 6 && !memcmp(REM8, T, 6)) FoundMask |= 1 << 8;
|
||||
if (NewSize == 6 && !memcmp(REM9, T, 6)) FoundMask |= 1 << 9;
|
||||
if (NewSize == 6 && !memcmp(REM10, T, 6)) FoundMask |= 1 << 10;
|
||||
|
||||
if (NewSize == 5 && !memcmp(REM11, T, 5)) FoundMask |= 1 << 11;
|
||||
if (NewSize == 5 && !memcmp(REM12, T, 5)) FoundMask |= 1 << 12;
|
||||
if (NewSize == 5 && !memcmp(REM13, T, 5)) FoundMask |= 1 << 13;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, (1 << 14) - 1);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, EraseBytes1) {
|
||||
TestEraseBytes(&MutationDispatcher::Mutate_EraseBytes, 200);
|
||||
}
|
||||
TEST(FuzzerMutate, EraseBytes2) {
|
||||
TestEraseBytes(&MutationDispatcher::Mutate, 2000);
|
||||
}
|
||||
|
||||
void TestInsertByte(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
MutationDispatcher MD(Rand, {});
|
||||
int FoundMask = 0;
|
||||
uint8_t INS0[8] = {0xF1, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t INS1[8] = {0x00, 0xF2, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t INS2[8] = {0x00, 0x11, 0xF3, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t INS3[8] = {0x00, 0x11, 0x22, 0xF4, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t INS4[8] = {0x00, 0x11, 0x22, 0x33, 0xF5, 0x44, 0x55, 0x66};
|
||||
uint8_t INS5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xF6, 0x55, 0x66};
|
||||
uint8_t INS6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xF7, 0x66};
|
||||
uint8_t INS7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF8};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
size_t NewSize = (MD.*M)(T, 7, 8);
|
||||
if (NewSize == 8 && !memcmp(INS0, T, 8)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 8 && !memcmp(INS1, T, 8)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 8 && !memcmp(INS2, T, 8)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 8 && !memcmp(INS3, T, 8)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 8 && !memcmp(INS4, T, 8)) FoundMask |= 1 << 4;
|
||||
if (NewSize == 8 && !memcmp(INS5, T, 8)) FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(INS6, T, 8)) FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(INS7, T, 8)) FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, InsertByte1) {
|
||||
TestInsertByte(&MutationDispatcher::Mutate_InsertByte, 1 << 15);
|
||||
}
|
||||
TEST(FuzzerMutate, InsertByte2) {
|
||||
TestInsertByte(&MutationDispatcher::Mutate, 1 << 17);
|
||||
}
|
||||
|
||||
void TestInsertRepeatedBytes(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
MutationDispatcher MD(Rand, {});
|
||||
int FoundMask = 0;
|
||||
uint8_t INS0[7] = {0x00, 0x11, 0x22, 0x33, 'a', 'a', 'a'};
|
||||
uint8_t INS1[7] = {0x00, 0x11, 0x22, 'a', 'a', 'a', 0x33};
|
||||
uint8_t INS2[7] = {0x00, 0x11, 'a', 'a', 'a', 0x22, 0x33};
|
||||
uint8_t INS3[7] = {0x00, 'a', 'a', 'a', 0x11, 0x22, 0x33};
|
||||
uint8_t INS4[7] = {'a', 'a', 'a', 0x00, 0x11, 0x22, 0x33};
|
||||
|
||||
uint8_t INS5[8] = {0x00, 0x11, 0x22, 0x33, 'b', 'b', 'b', 'b'};
|
||||
uint8_t INS6[8] = {0x00, 0x11, 0x22, 'b', 'b', 'b', 'b', 0x33};
|
||||
uint8_t INS7[8] = {0x00, 0x11, 'b', 'b', 'b', 'b', 0x22, 0x33};
|
||||
uint8_t INS8[8] = {0x00, 'b', 'b', 'b', 'b', 0x11, 0x22, 0x33};
|
||||
uint8_t INS9[8] = {'b', 'b', 'b', 'b', 0x00, 0x11, 0x22, 0x33};
|
||||
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33};
|
||||
size_t NewSize = (MD.*M)(T, 4, 8);
|
||||
if (NewSize == 7 && !memcmp(INS0, T, 7)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(INS1, T, 7)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(INS2, T, 7)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(INS3, T, 7)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 7 && !memcmp(INS4, T, 7)) FoundMask |= 1 << 4;
|
||||
|
||||
if (NewSize == 8 && !memcmp(INS5, T, 8)) FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(INS6, T, 8)) FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(INS7, T, 8)) FoundMask |= 1 << 7;
|
||||
if (NewSize == 8 && !memcmp(INS8, T, 8)) FoundMask |= 1 << 8;
|
||||
if (NewSize == 8 && !memcmp(INS9, T, 8)) FoundMask |= 1 << 9;
|
||||
|
||||
}
|
||||
EXPECT_EQ(FoundMask, (1 << 10) - 1);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, InsertRepeatedBytes1) {
|
||||
TestInsertRepeatedBytes(&MutationDispatcher::Mutate_InsertRepeatedBytes, 10000);
|
||||
}
|
||||
TEST(FuzzerMutate, InsertRepeatedBytes2) {
|
||||
TestInsertRepeatedBytes(&MutationDispatcher::Mutate, 300000);
|
||||
}
|
||||
|
||||
void TestChangeByte(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
MutationDispatcher MD(Rand, {});
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[8] = {0xF0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH1[8] = {0x00, 0xF1, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH2[8] = {0x00, 0x11, 0xF2, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH3[8] = {0x00, 0x11, 0x22, 0xF3, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0xF4, 0x55, 0x66, 0x77};
|
||||
uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xF5, 0x66, 0x77};
|
||||
uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xF5, 0x77};
|
||||
uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (MD.*M)(T, 8, 9);
|
||||
if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4;
|
||||
if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, ChangeByte1) {
|
||||
TestChangeByte(&MutationDispatcher::Mutate_ChangeByte, 1 << 15);
|
||||
}
|
||||
TEST(FuzzerMutate, ChangeByte2) {
|
||||
TestChangeByte(&MutationDispatcher::Mutate, 1 << 17);
|
||||
}
|
||||
|
||||
void TestChangeBit(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
MutationDispatcher MD(Rand, {});
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[8] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH1[8] = {0x00, 0x13, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH2[8] = {0x00, 0x11, 0x02, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH3[8] = {0x00, 0x11, 0x22, 0x37, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0x54, 0x55, 0x66, 0x77};
|
||||
uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x54, 0x66, 0x77};
|
||||
uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x76, 0x77};
|
||||
uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (MD.*M)(T, 8, 9);
|
||||
if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4;
|
||||
if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, ChangeBit1) {
|
||||
TestChangeBit(&MutationDispatcher::Mutate_ChangeBit, 1 << 16);
|
||||
}
|
||||
TEST(FuzzerMutate, ChangeBit2) {
|
||||
TestChangeBit(&MutationDispatcher::Mutate, 1 << 18);
|
||||
}
|
||||
|
||||
void TestShuffleBytes(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
MutationDispatcher MD(Rand, {});
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[7] = {0x00, 0x22, 0x11, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t CH1[7] = {0x11, 0x00, 0x33, 0x22, 0x44, 0x55, 0x66};
|
||||
uint8_t CH2[7] = {0x00, 0x33, 0x11, 0x22, 0x44, 0x55, 0x66};
|
||||
uint8_t CH3[7] = {0x00, 0x11, 0x22, 0x44, 0x55, 0x66, 0x33};
|
||||
uint8_t CH4[7] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x44, 0x66};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
size_t NewSize = (MD.*M)(T, 7, 7);
|
||||
if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 7 && !memcmp(CH4, T, 7)) FoundMask |= 1 << 4;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 31);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, ShuffleBytes1) {
|
||||
TestShuffleBytes(&MutationDispatcher::Mutate_ShuffleBytes, 1 << 16);
|
||||
}
|
||||
TEST(FuzzerMutate, ShuffleBytes2) {
|
||||
TestShuffleBytes(&MutationDispatcher::Mutate, 1 << 20);
|
||||
}
|
||||
|
||||
void TestCopyPart(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
MutationDispatcher MD(Rand, {});
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11};
|
||||
uint8_t CH1[7] = {0x55, 0x66, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t CH2[7] = {0x00, 0x55, 0x66, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t CH3[7] = {0x00, 0x11, 0x22, 0x00, 0x11, 0x22, 0x66};
|
||||
uint8_t CH4[7] = {0x00, 0x11, 0x11, 0x22, 0x33, 0x55, 0x66};
|
||||
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
size_t NewSize = (MD.*M)(T, 7, 7);
|
||||
if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 7 && !memcmp(CH4, T, 7)) FoundMask |= 1 << 4;
|
||||
}
|
||||
|
||||
uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11, 0x22};
|
||||
uint8_t CH6[8] = {0x22, 0x33, 0x44, 0x00, 0x11, 0x22, 0x33, 0x44};
|
||||
uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x00, 0x11, 0x22, 0x33, 0x44};
|
||||
uint8_t CH8[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x22, 0x33, 0x44};
|
||||
uint8_t CH9[8] = {0x00, 0x11, 0x22, 0x22, 0x33, 0x44, 0x33, 0x44};
|
||||
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (MD.*M)(T, 5, 8);
|
||||
if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
|
||||
if (NewSize == 8 && !memcmp(CH8, T, 8)) FoundMask |= 1 << 8;
|
||||
if (NewSize == 8 && !memcmp(CH9, T, 8)) FoundMask |= 1 << 9;
|
||||
}
|
||||
|
||||
EXPECT_EQ(FoundMask, 1023);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, CopyPart1) {
|
||||
TestCopyPart(&MutationDispatcher::Mutate_CopyPart, 1 << 10);
|
||||
}
|
||||
TEST(FuzzerMutate, CopyPart2) {
|
||||
TestCopyPart(&MutationDispatcher::Mutate, 1 << 13);
|
||||
}
|
||||
|
||||
void TestAddWordFromDictionary(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
MutationDispatcher MD(Rand, {});
|
||||
uint8_t Word1[4] = {0xAA, 0xBB, 0xCC, 0xDD};
|
||||
uint8_t Word2[3] = {0xFF, 0xEE, 0xEF};
|
||||
MD.AddWordToManualDictionary(Word(Word1, sizeof(Word1)));
|
||||
MD.AddWordToManualDictionary(Word(Word2, sizeof(Word2)));
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[7] = {0x00, 0x11, 0x22, 0xAA, 0xBB, 0xCC, 0xDD};
|
||||
uint8_t CH1[7] = {0x00, 0x11, 0xAA, 0xBB, 0xCC, 0xDD, 0x22};
|
||||
uint8_t CH2[7] = {0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22};
|
||||
uint8_t CH3[7] = {0xAA, 0xBB, 0xCC, 0xDD, 0x00, 0x11, 0x22};
|
||||
uint8_t CH4[6] = {0x00, 0x11, 0x22, 0xFF, 0xEE, 0xEF};
|
||||
uint8_t CH5[6] = {0x00, 0x11, 0xFF, 0xEE, 0xEF, 0x22};
|
||||
uint8_t CH6[6] = {0x00, 0xFF, 0xEE, 0xEF, 0x11, 0x22};
|
||||
uint8_t CH7[6] = {0xFF, 0xEE, 0xEF, 0x00, 0x11, 0x22};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[7] = {0x00, 0x11, 0x22};
|
||||
size_t NewSize = (MD.*M)(T, 3, 7);
|
||||
if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 6 && !memcmp(CH4, T, 6)) FoundMask |= 1 << 4;
|
||||
if (NewSize == 6 && !memcmp(CH5, T, 6)) FoundMask |= 1 << 5;
|
||||
if (NewSize == 6 && !memcmp(CH6, T, 6)) FoundMask |= 1 << 6;
|
||||
if (NewSize == 6 && !memcmp(CH7, T, 6)) FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, AddWordFromDictionary1) {
|
||||
TestAddWordFromDictionary(
|
||||
&MutationDispatcher::Mutate_AddWordFromManualDictionary, 1 << 15);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, AddWordFromDictionary2) {
|
||||
TestAddWordFromDictionary(&MutationDispatcher::Mutate, 1 << 15);
|
||||
}
|
||||
|
||||
void TestAddWordFromDictionaryWithHint(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
MutationDispatcher MD(Rand, {});
|
||||
uint8_t W[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xFF, 0xEE, 0xEF};
|
||||
size_t PosHint = 7777;
|
||||
MD.AddWordToAutoDictionary({Word(W, sizeof(W)), PosHint});
|
||||
int FoundMask = 0;
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[10000];
|
||||
memset(T, 0, sizeof(T));
|
||||
size_t NewSize = (MD.*M)(T, 9000, 10000);
|
||||
if (NewSize >= PosHint + sizeof(W) &&
|
||||
!memcmp(W, T + PosHint, sizeof(W)))
|
||||
FoundMask = 1;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 1);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, AddWordFromDictionaryWithHint1) {
|
||||
TestAddWordFromDictionaryWithHint(
|
||||
&MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary, 1 << 5);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, AddWordFromDictionaryWithHint2) {
|
||||
TestAddWordFromDictionaryWithHint(&MutationDispatcher::Mutate, 1 << 10);
|
||||
}
|
||||
|
||||
void TestChangeASCIIInteger(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
MutationDispatcher MD(Rand, {});
|
||||
|
||||
uint8_t CH0[8] = {'1', '2', '3', '4', '5', '6', '7', '7'};
|
||||
uint8_t CH1[8] = {'1', '2', '3', '4', '5', '6', '7', '9'};
|
||||
uint8_t CH2[8] = {'2', '4', '6', '9', '1', '3', '5', '6'};
|
||||
uint8_t CH3[8] = {'0', '6', '1', '7', '2', '8', '3', '9'};
|
||||
int FoundMask = 0;
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
|
||||
size_t NewSize = (MD.*M)(T, 8, 8);
|
||||
/**/ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
|
||||
else if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
|
||||
else if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
|
||||
else if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
|
||||
else if (NewSize == 8) FoundMask |= 1 << 4;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 31);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, ChangeASCIIInteger1) {
|
||||
TestChangeASCIIInteger(&MutationDispatcher::Mutate_ChangeASCIIInteger,
|
||||
1 << 15);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, ChangeASCIIInteger2) {
|
||||
TestChangeASCIIInteger(&MutationDispatcher::Mutate, 1 << 15);
|
||||
}
|
||||
|
||||
void TestChangeBinaryInteger(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
MutationDispatcher MD(Rand, {});
|
||||
|
||||
uint8_t CH0[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x79};
|
||||
uint8_t CH1[8] = {0x00, 0x11, 0x22, 0x31, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH2[8] = {0xff, 0x10, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH3[8] = {0x00, 0x11, 0x2a, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x4f, 0x66, 0x77};
|
||||
uint8_t CH5[8] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88};
|
||||
uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x00, 0x00, 0x00, 0x08, 0x77}; // Size
|
||||
uint8_t CH7[8] = {0x00, 0x08, 0x00, 0x33, 0x44, 0x55, 0x66, 0x77}; // Sw(Size)
|
||||
|
||||
int FoundMask = 0;
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (MD.*M)(T, 8, 8);
|
||||
/**/ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
|
||||
else if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
|
||||
else if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
|
||||
else if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
|
||||
else if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4;
|
||||
else if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
|
||||
else if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
|
||||
else if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, ChangeBinaryInteger1) {
|
||||
TestChangeBinaryInteger(&MutationDispatcher::Mutate_ChangeBinaryInteger,
|
||||
1 << 12);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, ChangeBinaryInteger2) {
|
||||
TestChangeBinaryInteger(&MutationDispatcher::Mutate, 1 << 15);
|
||||
}
|
||||
|
||||
|
||||
TEST(FuzzerDictionary, ParseOneDictionaryEntry) {
|
||||
Unit U;
|
||||
EXPECT_FALSE(ParseOneDictionaryEntry("", &U));
|
||||
EXPECT_FALSE(ParseOneDictionaryEntry(" ", &U));
|
||||
EXPECT_FALSE(ParseOneDictionaryEntry("\t ", &U));
|
||||
EXPECT_FALSE(ParseOneDictionaryEntry(" \" ", &U));
|
||||
EXPECT_FALSE(ParseOneDictionaryEntry(" zz\" ", &U));
|
||||
EXPECT_FALSE(ParseOneDictionaryEntry(" \"zz ", &U));
|
||||
EXPECT_FALSE(ParseOneDictionaryEntry(" \"\" ", &U));
|
||||
EXPECT_TRUE(ParseOneDictionaryEntry("\"a\"", &U));
|
||||
EXPECT_EQ(U, Unit({'a'}));
|
||||
EXPECT_TRUE(ParseOneDictionaryEntry("\"abc\"", &U));
|
||||
EXPECT_EQ(U, Unit({'a', 'b', 'c'}));
|
||||
EXPECT_TRUE(ParseOneDictionaryEntry("abc=\"abc\"", &U));
|
||||
EXPECT_EQ(U, Unit({'a', 'b', 'c'}));
|
||||
EXPECT_FALSE(ParseOneDictionaryEntry("\"\\\"", &U));
|
||||
EXPECT_TRUE(ParseOneDictionaryEntry("\"\\\\\"", &U));
|
||||
EXPECT_EQ(U, Unit({'\\'}));
|
||||
EXPECT_TRUE(ParseOneDictionaryEntry("\"\\xAB\"", &U));
|
||||
EXPECT_EQ(U, Unit({0xAB}));
|
||||
EXPECT_TRUE(ParseOneDictionaryEntry("\"\\xABz\\xDE\"", &U));
|
||||
EXPECT_EQ(U, Unit({0xAB, 'z', 0xDE}));
|
||||
EXPECT_TRUE(ParseOneDictionaryEntry("\"#\"", &U));
|
||||
EXPECT_EQ(U, Unit({'#'}));
|
||||
EXPECT_TRUE(ParseOneDictionaryEntry("\"\\\"\"", &U));
|
||||
EXPECT_EQ(U, Unit({'"'}));
|
||||
}
|
||||
|
||||
TEST(FuzzerDictionary, ParseDictionaryFile) {
|
||||
std::vector<Unit> Units;
|
||||
EXPECT_FALSE(ParseDictionaryFile("zzz\n", &Units));
|
||||
EXPECT_FALSE(ParseDictionaryFile("", &Units));
|
||||
EXPECT_TRUE(ParseDictionaryFile("\n", &Units));
|
||||
EXPECT_EQ(Units.size(), 0U);
|
||||
EXPECT_TRUE(ParseDictionaryFile("#zzzz a b c d\n", &Units));
|
||||
EXPECT_EQ(Units.size(), 0U);
|
||||
EXPECT_TRUE(ParseDictionaryFile(" #zzzz\n", &Units));
|
||||
EXPECT_EQ(Units.size(), 0U);
|
||||
EXPECT_TRUE(ParseDictionaryFile(" #zzzz\n", &Units));
|
||||
EXPECT_EQ(Units.size(), 0U);
|
||||
EXPECT_TRUE(ParseDictionaryFile(" #zzzz\naaa=\"aa\"", &Units));
|
||||
EXPECT_EQ(Units, std::vector<Unit>({Unit({'a', 'a'})}));
|
||||
EXPECT_TRUE(
|
||||
ParseDictionaryFile(" #zzzz\naaa=\"aa\"\n\nabc=\"abc\"", &Units));
|
||||
EXPECT_EQ(Units,
|
||||
std::vector<Unit>({Unit({'a', 'a'}), Unit({'a', 'b', 'c'})}));
|
||||
}
|
||||
|
||||
TEST(FuzzerUtil, Base64) {
|
||||
EXPECT_EQ("", Base64({}));
|
||||
EXPECT_EQ("YQ==", Base64({'a'}));
|
||||
EXPECT_EQ("eA==", Base64({'x'}));
|
||||
EXPECT_EQ("YWI=", Base64({'a', 'b'}));
|
||||
EXPECT_EQ("eHk=", Base64({'x', 'y'}));
|
||||
EXPECT_EQ("YWJj", Base64({'a', 'b', 'c'}));
|
||||
EXPECT_EQ("eHl6", Base64({'x', 'y', 'z'}));
|
||||
EXPECT_EQ("YWJjeA==", Base64({'a', 'b', 'c', 'x'}));
|
||||
EXPECT_EQ("YWJjeHk=", Base64({'a', 'b', 'c', 'x', 'y'}));
|
||||
EXPECT_EQ("YWJjeHl6", Base64({'a', 'b', 'c', 'x', 'y', 'z'}));
|
||||
}
|
||||
|
||||
TEST(Corpus, Distribution) {
|
||||
Random Rand(0);
|
||||
InputCorpus C("");
|
||||
size_t N = 10;
|
||||
size_t TriesPerUnit = 1<<16;
|
||||
for (size_t i = 0; i < N; i++)
|
||||
C.AddToCorpus(Unit{ static_cast<uint8_t>(i) }, 0);
|
||||
|
||||
std::vector<size_t> Hist(N);
|
||||
for (size_t i = 0; i < N * TriesPerUnit; i++) {
|
||||
Hist[C.ChooseUnitIdxToMutate(Rand)]++;
|
||||
}
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
// A weak sanity check that every unit gets invoked.
|
||||
EXPECT_GT(Hist[i], TriesPerUnit / N / 3);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Merge, Bad) {
|
||||
const char *kInvalidInputs[] = {
|
||||
"",
|
||||
"x",
|
||||
"3\nx",
|
||||
"2\n3",
|
||||
"2\n2",
|
||||
"2\n2\nA\n",
|
||||
"2\n2\nA\nB\nC\n",
|
||||
"0\n0\n",
|
||||
"1\n1\nA\nDONE 0",
|
||||
"1\n1\nA\nSTARTED 1",
|
||||
};
|
||||
Merger M;
|
||||
for (auto S : kInvalidInputs) {
|
||||
// fprintf(stderr, "TESTING:\n%s\n", S);
|
||||
EXPECT_FALSE(M.Parse(S, false));
|
||||
}
|
||||
}
|
||||
|
||||
void EQ(const std::vector<uint32_t> &A, const std::vector<uint32_t> &B) {
|
||||
EXPECT_EQ(A, B);
|
||||
}
|
||||
|
||||
void EQ(const std::vector<std::string> &A, const std::vector<std::string> &B) {
|
||||
std::set<std::string> a(A.begin(), A.end());
|
||||
std::set<std::string> b(B.begin(), B.end());
|
||||
EXPECT_EQ(a, b);
|
||||
}
|
||||
|
||||
static void Merge(const std::string &Input,
|
||||
const std::vector<std::string> Result,
|
||||
size_t NumNewFeatures) {
|
||||
Merger M;
|
||||
std::vector<std::string> NewFiles;
|
||||
EXPECT_TRUE(M.Parse(Input, true));
|
||||
EXPECT_EQ(NumNewFeatures, M.Merge(&NewFiles));
|
||||
EQ(NewFiles, Result);
|
||||
}
|
||||
|
||||
TEST(Merge, Good) {
|
||||
Merger M;
|
||||
|
||||
EXPECT_TRUE(M.Parse("1\n0\nAA\n", false));
|
||||
EXPECT_EQ(M.Files.size(), 1U);
|
||||
EXPECT_EQ(M.NumFilesInFirstCorpus, 0U);
|
||||
EXPECT_EQ(M.Files[0].Name, "AA");
|
||||
EXPECT_TRUE(M.LastFailure.empty());
|
||||
EXPECT_EQ(M.FirstNotProcessedFile, 0U);
|
||||
|
||||
EXPECT_TRUE(M.Parse("2\n1\nAA\nBB\nSTARTED 0 42\n", false));
|
||||
EXPECT_EQ(M.Files.size(), 2U);
|
||||
EXPECT_EQ(M.NumFilesInFirstCorpus, 1U);
|
||||
EXPECT_EQ(M.Files[0].Name, "AA");
|
||||
EXPECT_EQ(M.Files[1].Name, "BB");
|
||||
EXPECT_EQ(M.LastFailure, "AA");
|
||||
EXPECT_EQ(M.FirstNotProcessedFile, 1U);
|
||||
|
||||
EXPECT_TRUE(M.Parse("3\n1\nAA\nBB\nC\n"
|
||||
"STARTED 0 1000\n"
|
||||
"DONE 0 1 2 3\n"
|
||||
"STARTED 1 1001\n"
|
||||
"DONE 1 4 5 6 \n"
|
||||
"STARTED 2 1002\n"
|
||||
"", true));
|
||||
EXPECT_EQ(M.Files.size(), 3U);
|
||||
EXPECT_EQ(M.NumFilesInFirstCorpus, 1U);
|
||||
EXPECT_EQ(M.Files[0].Name, "AA");
|
||||
EXPECT_EQ(M.Files[0].Size, 1000U);
|
||||
EXPECT_EQ(M.Files[1].Name, "BB");
|
||||
EXPECT_EQ(M.Files[1].Size, 1001U);
|
||||
EXPECT_EQ(M.Files[2].Name, "C");
|
||||
EXPECT_EQ(M.Files[2].Size, 1002U);
|
||||
EXPECT_EQ(M.LastFailure, "C");
|
||||
EXPECT_EQ(M.FirstNotProcessedFile, 3U);
|
||||
EQ(M.Files[0].Features, {1, 2, 3});
|
||||
EQ(M.Files[1].Features, {4, 5, 6});
|
||||
|
||||
|
||||
std::vector<std::string> NewFiles;
|
||||
|
||||
EXPECT_TRUE(M.Parse("3\n2\nAA\nBB\nC\n"
|
||||
"STARTED 0 1000\nDONE 0 1 2 3\n"
|
||||
"STARTED 1 1001\nDONE 1 4 5 6 \n"
|
||||
"STARTED 2 1002\nDONE 2 6 1 3 \n"
|
||||
"", true));
|
||||
EXPECT_EQ(M.Files.size(), 3U);
|
||||
EXPECT_EQ(M.NumFilesInFirstCorpus, 2U);
|
||||
EXPECT_TRUE(M.LastFailure.empty());
|
||||
EXPECT_EQ(M.FirstNotProcessedFile, 3U);
|
||||
EQ(M.Files[0].Features, {1, 2, 3});
|
||||
EQ(M.Files[1].Features, {4, 5, 6});
|
||||
EQ(M.Files[2].Features, {1, 3, 6});
|
||||
EXPECT_EQ(0U, M.Merge(&NewFiles));
|
||||
EQ(NewFiles, {});
|
||||
|
||||
EXPECT_TRUE(M.Parse("3\n1\nA\nB\nC\n"
|
||||
"STARTED 0 1000\nDONE 0 1 2 3\n"
|
||||
"STARTED 1 1001\nDONE 1 4 5 6 \n"
|
||||
"STARTED 2 1002\nDONE 2 6 1 3\n"
|
||||
"", true));
|
||||
EQ(M.Files[0].Features, {1, 2, 3});
|
||||
EQ(M.Files[1].Features, {4, 5, 6});
|
||||
EQ(M.Files[2].Features, {1, 3, 6});
|
||||
EXPECT_EQ(3U, M.Merge(&NewFiles));
|
||||
EQ(NewFiles, {"B"});
|
||||
}
|
||||
|
||||
TEST(Merge, Merge) {
|
||||
|
||||
Merge("3\n1\nA\nB\nC\n"
|
||||
"STARTED 0 1000\nDONE 0 1 2 3\n"
|
||||
"STARTED 1 1001\nDONE 1 4 5 6 \n"
|
||||
"STARTED 2 1002\nDONE 2 6 1 3 \n",
|
||||
{"B"}, 3);
|
||||
|
||||
Merge("3\n0\nA\nB\nC\n"
|
||||
"STARTED 0 2000\nDONE 0 1 2 3\n"
|
||||
"STARTED 1 1001\nDONE 1 4 5 6 \n"
|
||||
"STARTED 2 1002\nDONE 2 6 1 3 \n",
|
||||
{"A", "B", "C"}, 6);
|
||||
|
||||
Merge("4\n0\nA\nB\nC\nD\n"
|
||||
"STARTED 0 2000\nDONE 0 1 2 3\n"
|
||||
"STARTED 1 1101\nDONE 1 4 5 6 \n"
|
||||
"STARTED 2 1102\nDONE 2 6 1 3 100 \n"
|
||||
"STARTED 3 1000\nDONE 3 1 \n",
|
||||
{"A", "B", "C", "D"}, 7);
|
||||
|
||||
Merge("4\n1\nA\nB\nC\nD\n"
|
||||
"STARTED 0 2000\nDONE 0 4 5 6 7 8\n"
|
||||
"STARTED 1 1100\nDONE 1 1 2 3 \n"
|
||||
"STARTED 2 1100\nDONE 2 2 3 \n"
|
||||
"STARTED 3 1000\nDONE 3 1 \n",
|
||||
{"B", "D"}, 3);
|
||||
}
|
28
test/fuzz_test/Fuzzer/test/InitializeTest.cpp
Normal file
28
test/fuzz_test/Fuzzer/test/InitializeTest.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Make sure LLVMFuzzerInitialize is called.
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static char *argv0;
|
||||
|
||||
extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||
assert(*argc > 0);
|
||||
argv0 = **argv;
|
||||
fprintf(stderr, "LLVMFuzzerInitialize: %s\n", argv0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size == strlen(argv0) &&
|
||||
!strncmp(reinterpret_cast<const char *>(Data), argv0, Size)) {
|
||||
fprintf(stderr, "BINGO %s\n", argv0);
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
17
test/fuzz_test/Fuzzer/test/LeakTest.cpp
Normal file
17
test/fuzz_test/Fuzzer/test/LeakTest.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Test with a leak.
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
static volatile void *Sink;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size > 0 && *Data == 'H') {
|
||||
Sink = new int;
|
||||
Sink = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
17
test/fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp
Normal file
17
test/fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Test with a leak.
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
static volatile int *Sink;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (!Size) return 0;
|
||||
Sink = new int;
|
||||
Sink = new int;
|
||||
while (Sink) *Sink = 0; // Infinite loop.
|
||||
return 0;
|
||||
}
|
||||
|
22
test/fuzz_test/Fuzzer/test/LoadTest.cpp
Normal file
22
test/fuzz_test/Fuzzer/test/LoadTest.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer: find interesting value of array index.
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
static volatile int Sink;
|
||||
const int kArraySize = 1234567;
|
||||
int array[kArraySize];
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < 8) return 0;
|
||||
size_t a = 0;
|
||||
memcpy(&a, Data, 8);
|
||||
Sink = array[a % (kArraySize + 1)];
|
||||
return 0;
|
||||
}
|
||||
|
31
test/fuzz_test/Fuzzer/test/MemcmpTest.cpp
Normal file
31
test/fuzz_test/Fuzzer/test/MemcmpTest.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find a particular string.
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
// TODO: check other sizes.
|
||||
if (Size >= 8 && memcmp(Data, "01234567", 8) == 0) {
|
||||
if (Size >= 12 && memcmp(Data + 8, "ABCD", 4) == 0) {
|
||||
if (Size >= 14 && memcmp(Data + 12, "XY", 2) == 0) {
|
||||
if (Size >= 17 && memcmp(Data + 14, "KLM", 3) == 0) {
|
||||
if (Size >= 27 && memcmp(Data + 17, "ABCDE-GHIJ", 10) == 0){
|
||||
fprintf(stderr, "BINGO %zd\n", Size);
|
||||
for (size_t i = 0; i < Size; i++) {
|
||||
uint8_t C = Data[i];
|
||||
if (C >= 32 && C < 127)
|
||||
fprintf(stderr, "%c", C);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
18
test/fuzz_test/Fuzzer/test/NthRunCrashTest.cpp
Normal file
18
test/fuzz_test/Fuzzer/test/NthRunCrashTest.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Crash on the N-th execution.
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
static int Counter;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Counter++ == 1000) {
|
||||
std::cout << "BINGO; Found the target, exiting\n";
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
19
test/fuzz_test/Fuzzer/test/NullDerefOnEmptyTest.cpp
Normal file
19
test/fuzz_test/Fuzzer/test/NullDerefOnEmptyTest.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find the empty string.
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
static volatile int *Null = 0;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size == 0) {
|
||||
std::cout << "Found the target, dereferencing NULL\n";
|
||||
*Null = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
26
test/fuzz_test/Fuzzer/test/NullDerefTest.cpp
Normal file
26
test/fuzz_test/Fuzzer/test/NullDerefTest.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find the string "Hi!".
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
static volatile int Sink;
|
||||
static volatile int *Null = 0;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size > 0 && Data[0] == 'H') {
|
||||
Sink = 1;
|
||||
if (Size > 1 && Data[1] == 'i') {
|
||||
Sink = 2;
|
||||
if (Size > 2 && Data[2] == '!') {
|
||||
std::cout << "Found the target, dereferencing NULL\n";
|
||||
*Null = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
28
test/fuzz_test/Fuzzer/test/OneHugeAllocTest.cpp
Normal file
28
test/fuzz_test/Fuzzer/test/OneHugeAllocTest.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Tests OOM handling when there is a single large allocation.
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
static volatile char *SinkPtr;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size > 0 && Data[0] == 'H') {
|
||||
if (Size > 1 && Data[1] == 'i') {
|
||||
if (Size > 2 && Data[2] == '!') {
|
||||
size_t kSize = (size_t)1 << 31;
|
||||
char *p = new char[kSize];
|
||||
memset(p, 0, kSize);
|
||||
SinkPtr = p;
|
||||
delete [] p;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Tests OOM handling.
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
static volatile char *SinkPtr;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size > 0 && Data[0] == 'H') {
|
||||
if (Size > 1 && Data[1] == 'i') {
|
||||
if (Size > 2 && Data[2] == '!') {
|
||||
size_t kSize = 0xff000000U;
|
||||
char *p = new char[kSize];
|
||||
SinkPtr = p;
|
||||
delete [] p;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
31
test/fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp
Normal file
31
test/fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Tests OOM handling.
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
static volatile char *SinkPtr;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size > 0 && Data[0] == 'H') {
|
||||
if (Size > 1 && Data[1] == 'i') {
|
||||
if (Size > 2 && Data[2] == '!') {
|
||||
while (true) {
|
||||
size_t kSize = 1 << 28;
|
||||
char *p = new char[kSize];
|
||||
memset(p, 0, kSize);
|
||||
SinkPtr = p;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
29
test/fuzz_test/Fuzzer/test/RepeatedBytesTest.cpp
Normal file
29
test/fuzz_test/Fuzzer/test/RepeatedBytesTest.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find repeated bytes.
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
assert(Data);
|
||||
// Looking for AAAAAAAAAAAAAAAAAAAAAA or some such.
|
||||
size_t CurA = 0, MaxA = 0;
|
||||
for (size_t i = 0; i < Size; i++) {
|
||||
// Make sure there are no conditionals in the loop so that
|
||||
// coverage can't help the fuzzer.
|
||||
int EQ = Data[i] == 'A';
|
||||
CurA = EQ * (CurA + 1);
|
||||
int GT = CurA > MaxA;
|
||||
MaxA = GT * CurA + (!GT) * MaxA;
|
||||
}
|
||||
if (MaxA >= 20) {
|
||||
std::cout << "BINGO; Found the target (Max: " << MaxA << "), exiting\n";
|
||||
exit(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
22
test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp
Normal file
22
test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
int Matches = 0;
|
||||
for (size_t i = 0; i + 2 < Size; i += 3) {
|
||||
const char *Pat = i % 2 ? "foo" : "bar";
|
||||
if (!memcmp(Data + i, Pat, 3))
|
||||
Matches++;
|
||||
}
|
||||
if (Matches > 20) {
|
||||
fprintf(stderr, "BINGO!\n");
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
28
test/fuzz_test/Fuzzer/test/ShrinkControlFlowTest.cpp
Normal file
28
test/fuzz_test/Fuzzer/test/ShrinkControlFlowTest.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Test that we can find the minimal item in the corpus (3 bytes: "FUZ").
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
static volatile int Sink;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
int8_t Ids[256];
|
||||
memset(Ids, -1, sizeof(Ids));
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
if (Ids[Data[i]] == -1)
|
||||
Ids[Data[i]] = i;
|
||||
int F = Ids[(unsigned char)'F'];
|
||||
int U = Ids[(unsigned char)'U'];
|
||||
int Z = Ids[(unsigned char)'Z'];
|
||||
if (F >= 0 && U > F && Z > U) {
|
||||
Sink++;
|
||||
//fprintf(stderr, "IDS: %d %d %d\n", F, U, Z);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
22
test/fuzz_test/Fuzzer/test/ShrinkValueProfileTest.cpp
Normal file
22
test/fuzz_test/Fuzzer/test/ShrinkValueProfileTest.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Test that we can find the minimal item in the corpus (3 bytes: "FUZ").
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
static volatile uint32_t Sink;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < sizeof(uint32_t)) return 0;
|
||||
uint32_t X, Y;
|
||||
size_t Offset = Size < 8 ? 0 : Size / 2;
|
||||
memcpy(&X, Data + Offset, sizeof(uint32_t));
|
||||
memcpy(&Y, "FUZZ", sizeof(uint32_t));
|
||||
Sink = X == Y;
|
||||
return 0;
|
||||
}
|
||||
|
28
test/fuzz_test/Fuzzer/test/SignedIntOverflowTest.cpp
Normal file
28
test/fuzz_test/Fuzzer/test/SignedIntOverflowTest.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Test for signed-integer-overflow.
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <climits>
|
||||
|
||||
static volatile int Sink;
|
||||
static int Large = INT_MAX;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
assert(Data);
|
||||
if (Size > 0 && Data[0] == 'H') {
|
||||
Sink = 1;
|
||||
if (Size > 1 && Data[1] == 'i') {
|
||||
Sink = 2;
|
||||
if (Size > 2 && Data[2] == '!') {
|
||||
Large++; // int overflow.
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
46
test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp
Normal file
46
test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find several narrow ranges.
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
extern int AllLines[];
|
||||
|
||||
bool PrintOnce(int Line) {
|
||||
if (!AllLines[Line])
|
||||
fprintf(stderr, "Seen line %d\n", Line);
|
||||
AllLines[Line] = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size != 22) return 0;
|
||||
uint64_t x = 0;
|
||||
int64_t y = 0;
|
||||
int32_t z = 0;
|
||||
uint16_t a = 0;
|
||||
memcpy(&x, Data, 8); // 8
|
||||
memcpy(&y, Data + 8, 8); // 16
|
||||
memcpy(&z, Data + 16, sizeof(z)); // 20
|
||||
memcpy(&a, Data + 20, sizeof(a)); // 22
|
||||
|
||||
if (x > 1234567890 && PrintOnce(__LINE__) &&
|
||||
x < 1234567895 && PrintOnce(__LINE__) &&
|
||||
a == 0x4242 && PrintOnce(__LINE__) &&
|
||||
y >= 987654321 && PrintOnce(__LINE__) &&
|
||||
y <= 987654325 && PrintOnce(__LINE__) &&
|
||||
z < -10000 && PrintOnce(__LINE__) &&
|
||||
z >= -10005 && PrintOnce(__LINE__) &&
|
||||
z != -10003 && PrintOnce(__LINE__) &&
|
||||
true) {
|
||||
fprintf(stderr, "BINGO; Found the target: size %zd (%zd, %zd, %d, %d), exiting.\n",
|
||||
Size, x, y, z, a);
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AllLines[__LINE__ + 1]; // Must be the last line.
|
29
test/fuzz_test/Fuzzer/test/SimpleDictionaryTest.cpp
Normal file
29
test/fuzz_test/Fuzzer/test/SimpleDictionaryTest.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer.
|
||||
// The fuzzer must find a string based on dictionary words:
|
||||
// "Elvis"
|
||||
// "Presley"
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
static volatile int Zero = 0;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
const char *Expected = "ElvisPresley";
|
||||
if (Size < strlen(Expected)) return 0;
|
||||
size_t Match = 0;
|
||||
for (size_t i = 0; Expected[i]; i++)
|
||||
if (Expected[i] + Zero == Data[i])
|
||||
Match++;
|
||||
if (Match == strlen(Expected)) {
|
||||
std::cout << "BINGO; Found the target, exiting\n";
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
40
test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp
Normal file
40
test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// This test computes a checksum of the data (all but the last 4 bytes),
|
||||
// and then compares the last 4 bytes with the computed value.
|
||||
// A fuzzer with cmp traces is expected to defeat this check.
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
// A modified jenkins_one_at_a_time_hash initialized by non-zero,
|
||||
// so that simple_hash(0) != 0. See also
|
||||
// https://en.wikipedia.org/wiki/Jenkins_hash_function
|
||||
static uint32_t simple_hash(const uint8_t *Data, size_t Size) {
|
||||
uint32_t Hash = 0x12039854;
|
||||
for (uint32_t i = 0; i < Size; i++) {
|
||||
Hash += Data[i];
|
||||
Hash += (Hash << 10);
|
||||
Hash ^= (Hash >> 6);
|
||||
}
|
||||
Hash += (Hash << 3);
|
||||
Hash ^= (Hash >> 11);
|
||||
Hash += (Hash << 15);
|
||||
return Hash;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < 14)
|
||||
return 0;
|
||||
|
||||
uint32_t Hash = simple_hash(&Data[0], Size - 4);
|
||||
uint32_t Want = reinterpret_cast<const uint32_t *>(&Data[Size - 4])[0];
|
||||
if (Hash != Want)
|
||||
return 0;
|
||||
fprintf(stderr, "BINGO; simple_hash defeated: %x == %x\n", (unsigned int)Hash,
|
||||
(unsigned int)Want);
|
||||
exit(1);
|
||||
return 0;
|
||||
}
|
27
test/fuzz_test/Fuzzer/test/SimpleTest.cpp
Normal file
27
test/fuzz_test/Fuzzer/test/SimpleTest.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find the string "Hi!".
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
static volatile int Sink;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
assert(Data);
|
||||
if (Size > 0 && Data[0] == 'H') {
|
||||
Sink = 1;
|
||||
if (Size > 1 && Data[1] == 'i') {
|
||||
Sink = 2;
|
||||
if (Size > 2 && Data[2] == '!') {
|
||||
std::cout << "BINGO; Found the target, exiting\n";
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
25
test/fuzz_test/Fuzzer/test/SimpleThreadedTest.cpp
Normal file
25
test/fuzz_test/Fuzzer/test/SimpleThreadedTest.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Threaded test for a fuzzer. The fuzzer should find "H"
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
auto C = [&] {
|
||||
if (Size >= 2 && Data[0] == 'H') {
|
||||
std::cout << "BINGO; Found the target, exiting\n";
|
||||
abort();
|
||||
}
|
||||
};
|
||||
std::thread T[] = {std::thread(C), std::thread(C), std::thread(C),
|
||||
std::thread(C), std::thread(C), std::thread(C)};
|
||||
for (auto &X : T)
|
||||
X.join();
|
||||
return 0;
|
||||
}
|
||||
|
17
test/fuzz_test/Fuzzer/test/SingleMemcmpTest.cpp
Normal file
17
test/fuzz_test/Fuzzer/test/SingleMemcmpTest.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find a particular string.
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
char *S = (char*)Data;
|
||||
if (Size >= 6 && !memcmp(S, "qwerty", 6)) {
|
||||
fprintf(stderr, "BINGO\n");
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
17
test/fuzz_test/Fuzzer/test/SingleStrcmpTest.cpp
Normal file
17
test/fuzz_test/Fuzzer/test/SingleStrcmpTest.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find a particular string.
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
char *S = (char*)Data;
|
||||
if (Size >= 7 && !strcmp(S, "qwerty")) {
|
||||
fprintf(stderr, "BINGO\n");
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
17
test/fuzz_test/Fuzzer/test/SingleStrncmpTest.cpp
Normal file
17
test/fuzz_test/Fuzzer/test/SingleStrncmpTest.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find a particular string.
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
char *S = (char*)Data;
|
||||
if (Size >= 6 && !strncmp(S, "qwerty", 6)) {
|
||||
fprintf(stderr, "BINGO\n");
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
21
test/fuzz_test/Fuzzer/test/SpamyTest.cpp
Normal file
21
test/fuzz_test/Fuzzer/test/SpamyTest.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// The test spams to stderr and stdout.
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
assert(Data);
|
||||
printf("PRINTF_STDOUT\n");
|
||||
fflush(stdout);
|
||||
fprintf(stderr, "PRINTF_STDERR\n");
|
||||
std::cout << "STREAM_COUT\n";
|
||||
std::cout.flush();
|
||||
std::cerr << "STREAM_CERR\n";
|
||||
return 0;
|
||||
}
|
||||
|
32
test/fuzz_test/Fuzzer/test/StrcmpTest.cpp
Normal file
32
test/fuzz_test/Fuzzer/test/StrcmpTest.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Break through a series of strcmp.
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
|
||||
bool Eq(const uint8_t *Data, size_t Size, const char *Str) {
|
||||
char Buff[1024];
|
||||
size_t Len = strlen(Str);
|
||||
if (Size < Len) return false;
|
||||
if (Len >= sizeof(Buff)) return false;
|
||||
memcpy(Buff, (char*)Data, Len);
|
||||
Buff[Len] = 0;
|
||||
int res = strcmp(Buff, Str);
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Eq(Data, Size, "ABC") &&
|
||||
Size >= 3 && Eq(Data + 3, Size - 3, "QWER") &&
|
||||
Size >= 7 && Eq(Data + 7, Size - 7, "ZXCVN") &&
|
||||
Size >= 14 && Data[13] == 42
|
||||
) {
|
||||
fprintf(stderr, "BINGO\n");
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
21
test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp
Normal file
21
test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Test that libFuzzer itself does not read out of bounds.
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
static volatile int Sink;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < 5) return 0;
|
||||
const char *Ch = reinterpret_cast<const char *>(Data);
|
||||
if (Ch[Size - 3] == 'a')
|
||||
Sink = strncmp(Ch + Size - 3, "abcdefg", 6);
|
||||
return 0;
|
||||
}
|
||||
|
28
test/fuzz_test/Fuzzer/test/StrncmpTest.cpp
Normal file
28
test/fuzz_test/Fuzzer/test/StrncmpTest.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find a particular string.
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
static volatile int sink;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
// TODO: check other sizes.
|
||||
char *S = (char*)Data;
|
||||
if (Size >= 8 && strncmp(S, "123", 8))
|
||||
sink = 1;
|
||||
if (Size >= 8 && strncmp(S, "01234567", 8) == 0) {
|
||||
if (Size >= 12 && strncmp(S + 8, "ABCD", 4) == 0) {
|
||||
if (Size >= 14 && strncmp(S + 12, "XY", 2) == 0) {
|
||||
if (Size >= 17 && strncmp(S + 14, "KLM", 3) == 0) {
|
||||
fprintf(stderr, "BINGO\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
28
test/fuzz_test/Fuzzer/test/StrstrTest.cpp
Normal file
28
test/fuzz_test/Fuzzer/test/StrstrTest.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Test strstr and strcasestr hooks.
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
// Windows does not have strcasestr and memmem, so we are not testing them.
|
||||
#ifdef _WIN32
|
||||
#define strcasestr strstr
|
||||
#define memmem(a, b, c, d) true
|
||||
#endif
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < 4) return 0;
|
||||
std::string s(reinterpret_cast<const char*>(Data), Size);
|
||||
if (strstr(s.c_str(), "FUZZ") &&
|
||||
strcasestr(s.c_str(), "aBcD") &&
|
||||
memmem(s.data(), s.size(), "kuku", 4)
|
||||
) {
|
||||
fprintf(stderr, "BINGO\n");
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
34
test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp
Normal file
34
test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// The fuzzer must find several constants with swapped bytes.
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < 14) return 0;
|
||||
uint64_t x = 0;
|
||||
uint32_t y = 0;
|
||||
uint16_t z = 0;
|
||||
memcpy(&x, Data, sizeof(x));
|
||||
memcpy(&y, Data + Size / 2, sizeof(y));
|
||||
memcpy(&z, Data + Size - sizeof(z), sizeof(z));
|
||||
|
||||
x = __builtin_bswap64(x);
|
||||
y = __builtin_bswap32(y);
|
||||
z = __builtin_bswap16(z);
|
||||
|
||||
if (x == 0x46555A5A5A5A5546ULL &&
|
||||
z == 0x4F4B &&
|
||||
y == 0x66757A7A &&
|
||||
true
|
||||
) {
|
||||
if (Data[Size - 3] == 'z') {
|
||||
fprintf(stderr, "BINGO; Found the target\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
35
test/fuzz_test/Fuzzer/test/Switch2Test.cpp
Normal file
35
test/fuzz_test/Fuzzer/test/Switch2Test.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find the interesting switch value.
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
|
||||
int Switch(int a) {
|
||||
switch(a) {
|
||||
case 100001: return 1;
|
||||
case 100002: return 2;
|
||||
case 100003: return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
const int N = 3;
|
||||
if (Size < N * sizeof(int)) return 0;
|
||||
int Res = 0;
|
||||
for (int i = 0; i < N; i++) {
|
||||
int X;
|
||||
memcpy(&X, Data + i * sizeof(int), sizeof(int));
|
||||
Res += Switch(X);
|
||||
}
|
||||
if (Res == 5 || Res == 3 || Res == 6 || Res == 7) {
|
||||
fprintf(stderr, "BINGO; Found the target, exiting; Res=%d\n", Res);
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
58
test/fuzz_test/Fuzzer/test/SwitchTest.cpp
Normal file
58
test/fuzz_test/Fuzzer/test/SwitchTest.cpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find the interesting switch value.
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
|
||||
static volatile int Sink;
|
||||
|
||||
template<class T>
|
||||
bool Switch(const uint8_t *Data, size_t Size) {
|
||||
T X;
|
||||
if (Size < sizeof(X)) return false;
|
||||
memcpy(&X, Data, sizeof(X));
|
||||
switch (X) {
|
||||
case 1: Sink = __LINE__; break;
|
||||
case 101: Sink = __LINE__; break;
|
||||
case 1001: Sink = __LINE__; break;
|
||||
case 10001: Sink = __LINE__; break;
|
||||
// case 100001: Sink = __LINE__; break;
|
||||
// case 1000001: Sink = __LINE__; break;
|
||||
case 10000001: Sink = __LINE__; break;
|
||||
case 100000001: return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShortSwitch(const uint8_t *Data, size_t Size) {
|
||||
short X;
|
||||
if (Size < sizeof(short)) return false;
|
||||
memcpy(&X, Data, sizeof(short));
|
||||
switch(X) {
|
||||
case 42: Sink = __LINE__; break;
|
||||
case 402: Sink = __LINE__; break;
|
||||
case 4002: Sink = __LINE__; break;
|
||||
case 5002: Sink = __LINE__; break;
|
||||
case 7002: Sink = __LINE__; break;
|
||||
case 9002: Sink = __LINE__; break;
|
||||
case 14002: Sink = __LINE__; break;
|
||||
case 21402: return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size >= 4 && Switch<int>(Data, Size) &&
|
||||
Size >= 12 && Switch<uint64_t>(Data + 4, Size - 4) &&
|
||||
Size >= 14 && ShortSwitch(Data + 12, 2)
|
||||
) {
|
||||
fprintf(stderr, "BINGO; Found the target, exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
18
test/fuzz_test/Fuzzer/test/ThreadedLeakTest.cpp
Normal file
18
test/fuzz_test/Fuzzer/test/ThreadedLeakTest.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// The fuzzer should find a leak in a non-main thread.
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <thread>
|
||||
|
||||
static volatile int *Sink;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size == 0) return 0;
|
||||
if (Data[0] != 'F') return 0;
|
||||
std::thread T([&] { Sink = new int; });
|
||||
T.join();
|
||||
return 0;
|
||||
}
|
||||
|
26
test/fuzz_test/Fuzzer/test/ThreadedTest.cpp
Normal file
26
test/fuzz_test/Fuzzer/test/ThreadedTest.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Threaded test for a fuzzer. The fuzzer should not crash.
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < 8) return 0;
|
||||
assert(Data);
|
||||
auto C = [&] {
|
||||
size_t Res = 0;
|
||||
for (size_t i = 0; i < Size / 2; i++)
|
||||
Res += memcmp(Data, Data + Size / 2, 4);
|
||||
return Res;
|
||||
};
|
||||
std::thread T[] = {std::thread(C), std::thread(C), std::thread(C),
|
||||
std::thread(C), std::thread(C), std::thread(C)};
|
||||
for (auto &X : T)
|
||||
X.join();
|
||||
return 0;
|
||||
}
|
||||
|
14
test/fuzz_test/Fuzzer/test/TimeoutEmptyTest.cpp
Normal file
14
test/fuzz_test/Fuzzer/test/TimeoutEmptyTest.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find the empty string.
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
static volatile int Zero = 0;
|
||||
if (!Size)
|
||||
while(!Zero)
|
||||
;
|
||||
return 0;
|
||||
}
|
26
test/fuzz_test/Fuzzer/test/TimeoutTest.cpp
Normal file
26
test/fuzz_test/Fuzzer/test/TimeoutTest.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Simple test for a fuzzer. The fuzzer must find the string "Hi!".
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
static volatile int Sink;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size > 0 && Data[0] == 'H') {
|
||||
Sink = 1;
|
||||
if (Size > 1 && Data[1] == 'i') {
|
||||
Sink = 2;
|
||||
if (Size > 2 && Data[2] == '!') {
|
||||
Sink = 2;
|
||||
while (Sink)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
27
test/fuzz_test/Fuzzer/test/TraceMallocTest.cpp
Normal file
27
test/fuzz_test/Fuzzer/test/TraceMallocTest.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
||||
// Tests -trace_malloc
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
int *Ptr;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (!Size) return 0;
|
||||
if (*Data == 1) {
|
||||
delete Ptr;
|
||||
Ptr = nullptr;
|
||||
} else if (*Data == 2) {
|
||||
delete Ptr;
|
||||
Ptr = new int;
|
||||
} else if (*Data == 3) {
|
||||
if (!Ptr)
|
||||
Ptr = new int;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue