Add compiler-rt's libFuzzer, not connected to buildworld yet.
For now, the libraries can be built and installed using: cd /usr/src/lib/libclang_rt/fuzzer && \ make obj && \ make depend && \ make && \ sudo make install cd /usr/src/lib/libclang_rt/fuzzer_no_main && \ make obj && \ make depend && \ make && \ sudo make install See https://llvm.org/docs/LibFuzzer.html for more information.
This commit is contained in:
commit
53d65e1842
179
contrib/compiler-rt/lib/fuzzer/FuzzerCommand.h
Normal file
179
contrib/compiler-rt/lib/fuzzer/FuzzerCommand.h
Normal file
@ -0,0 +1,179 @@
|
||||
//===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// FuzzerCommand represents a command to run in a subprocess. It allows callers
|
||||
// to manage command line arguments and output and error streams.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_COMMAND_H
|
||||
#define LLVM_FUZZER_COMMAND_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerIO.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
class Command final {
|
||||
public:
|
||||
// This command line flag is used to indicate that the remaining command line
|
||||
// is immutable, meaning this flag effectively marks the end of the mutable
|
||||
// argument list.
|
||||
static inline const char *ignoreRemainingArgs() {
|
||||
return "-ignore_remaining_args=1";
|
||||
}
|
||||
|
||||
Command() : CombinedOutAndErr(false) {}
|
||||
|
||||
explicit Command(const Vector<std::string> &ArgsToAdd)
|
||||
: Args(ArgsToAdd), CombinedOutAndErr(false) {}
|
||||
|
||||
explicit Command(const Command &Other)
|
||||
: Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr),
|
||||
OutputFile(Other.OutputFile) {}
|
||||
|
||||
Command &operator=(const Command &Other) {
|
||||
Args = Other.Args;
|
||||
CombinedOutAndErr = Other.CombinedOutAndErr;
|
||||
OutputFile = Other.OutputFile;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Command() {}
|
||||
|
||||
// Returns true if the given Arg is present in Args. Only checks up to
|
||||
// "-ignore_remaining_args=1".
|
||||
bool hasArgument(const std::string &Arg) const {
|
||||
auto i = endMutableArgs();
|
||||
return std::find(Args.begin(), i, Arg) != i;
|
||||
}
|
||||
|
||||
// Gets all of the current command line arguments, **including** those after
|
||||
// "-ignore-remaining-args=1".
|
||||
const Vector<std::string> &getArguments() const { return Args; }
|
||||
|
||||
// Adds the given argument before "-ignore_remaining_args=1", or at the end
|
||||
// if that flag isn't present.
|
||||
void addArgument(const std::string &Arg) {
|
||||
Args.insert(endMutableArgs(), Arg);
|
||||
}
|
||||
|
||||
// Adds all given arguments before "-ignore_remaining_args=1", or at the end
|
||||
// if that flag isn't present.
|
||||
void addArguments(const Vector<std::string> &ArgsToAdd) {
|
||||
Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end());
|
||||
}
|
||||
|
||||
// Removes the given argument from the command argument list. Ignores any
|
||||
// occurrences after "-ignore_remaining_args=1", if present.
|
||||
void removeArgument(const std::string &Arg) {
|
||||
auto i = endMutableArgs();
|
||||
Args.erase(std::remove(Args.begin(), i, Arg), i);
|
||||
}
|
||||
|
||||
// Like hasArgument, but checks for "-[Flag]=...".
|
||||
bool hasFlag(const std::string &Flag) {
|
||||
std::string Arg("-" + Flag + "=");
|
||||
auto IsMatch = [&](const std::string &Other) {
|
||||
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
|
||||
};
|
||||
return std::any_of(Args.begin(), endMutableArgs(), IsMatch);
|
||||
}
|
||||
|
||||
// Returns the value of the first instance of a given flag, or an empty string
|
||||
// if the flag isn't present. Ignores any occurrences after
|
||||
// "-ignore_remaining_args=1", if present.
|
||||
std::string getFlagValue(const std::string &Flag) {
|
||||
std::string Arg("-" + Flag + "=");
|
||||
auto IsMatch = [&](const std::string &Other) {
|
||||
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
|
||||
};
|
||||
auto i = endMutableArgs();
|
||||
auto j = std::find_if(Args.begin(), i, IsMatch);
|
||||
std::string result;
|
||||
if (j != i) {
|
||||
result = j->substr(Arg.length());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Like AddArgument, but adds "-[Flag]=[Value]".
|
||||
void addFlag(const std::string &Flag, const std::string &Value) {
|
||||
addArgument("-" + Flag + "=" + Value);
|
||||
}
|
||||
|
||||
// Like RemoveArgument, but removes "-[Flag]=...".
|
||||
void removeFlag(const std::string &Flag) {
|
||||
std::string Arg("-" + Flag + "=");
|
||||
auto IsMatch = [&](const std::string &Other) {
|
||||
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
|
||||
};
|
||||
auto i = endMutableArgs();
|
||||
Args.erase(std::remove_if(Args.begin(), i, IsMatch), i);
|
||||
}
|
||||
|
||||
// Returns whether the command's stdout is being written to an output file.
|
||||
bool hasOutputFile() const { return !OutputFile.empty(); }
|
||||
|
||||
// Returns the currently set output file.
|
||||
const std::string &getOutputFile() const { return OutputFile; }
|
||||
|
||||
// Configures the command to redirect its output to the name file.
|
||||
void setOutputFile(const std::string &FileName) { OutputFile = FileName; }
|
||||
|
||||
// Returns whether the command's stderr is redirected to stdout.
|
||||
bool isOutAndErrCombined() const { return CombinedOutAndErr; }
|
||||
|
||||
// Sets whether to redirect the command's stderr to its stdout.
|
||||
void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; }
|
||||
|
||||
// Returns a string representation of the command. On many systems this will
|
||||
// be the equivalent command line.
|
||||
std::string toString() const {
|
||||
std::stringstream SS;
|
||||
for (auto arg : getArguments())
|
||||
SS << arg << " ";
|
||||
if (hasOutputFile())
|
||||
SS << ">" << getOutputFile() << " ";
|
||||
if (isOutAndErrCombined())
|
||||
SS << "2>&1 ";
|
||||
std::string result = SS.str();
|
||||
if (!result.empty())
|
||||
result = result.substr(0, result.length() - 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
Command(Command &&Other) = delete;
|
||||
Command &operator=(Command &&Other) = delete;
|
||||
|
||||
Vector<std::string>::iterator endMutableArgs() {
|
||||
return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
|
||||
}
|
||||
|
||||
Vector<std::string>::const_iterator endMutableArgs() const {
|
||||
return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
|
||||
}
|
||||
|
||||
// The command arguments. Args[0] is the command name.
|
||||
Vector<std::string> Args;
|
||||
|
||||
// True indicates stderr is redirected to stdout.
|
||||
bool CombinedOutAndErr;
|
||||
|
||||
// If not empty, stdout is redirected to the named file.
|
||||
std::string OutputFile;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_COMMAND_H
|
315
contrib/compiler-rt/lib/fuzzer/FuzzerCorpus.h
Normal file
315
contrib/compiler-rt/lib/fuzzer/FuzzerCorpus.h
Normal file
@ -0,0 +1,315 @@
|
||||
//===- 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 "FuzzerDataFlowTrace.h"
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "FuzzerSHA1.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include <algorithm>
|
||||
#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;
|
||||
bool Reduced = false;
|
||||
bool HasFocusFunction = false;
|
||||
Vector<uint32_t> UniqFeatureSet;
|
||||
Vector<uint8_t> DataFlowTraceForFocusFunction;
|
||||
};
|
||||
|
||||
class InputCorpus {
|
||||
static const size_t kFeatureSetSize = 1 << 21;
|
||||
public:
|
||||
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;
|
||||
}
|
||||
size_t MaxInputSize() const {
|
||||
size_t Res = 0;
|
||||
for (auto II : Inputs)
|
||||
Res = std::max(Res, II->U.size());
|
||||
return Res;
|
||||
}
|
||||
|
||||
size_t NumInputsThatTouchFocusFunction() {
|
||||
return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) {
|
||||
return II->HasFocusFunction;
|
||||
});
|
||||
}
|
||||
|
||||
size_t NumInputsWithDataFlowTrace() {
|
||||
return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) {
|
||||
return !II->DataFlowTraceForFocusFunction.empty();
|
||||
});
|
||||
}
|
||||
|
||||
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,
|
||||
bool HasFocusFunction, const Vector<uint32_t> &FeatureSet,
|
||||
const DataFlowTrace &DFT, const InputInfo *BaseII) {
|
||||
assert(!U.empty());
|
||||
if (FeatureDebug)
|
||||
Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures);
|
||||
Inputs.push_back(new InputInfo());
|
||||
InputInfo &II = *Inputs.back();
|
||||
II.U = U;
|
||||
II.NumFeatures = NumFeatures;
|
||||
II.MayDeleteFile = MayDeleteFile;
|
||||
II.UniqFeatureSet = FeatureSet;
|
||||
II.HasFocusFunction = HasFocusFunction;
|
||||
std::sort(II.UniqFeatureSet.begin(), II.UniqFeatureSet.end());
|
||||
ComputeSHA1(U.data(), U.size(), II.Sha1);
|
||||
auto Sha1Str = Sha1ToString(II.Sha1);
|
||||
Hashes.insert(Sha1Str);
|
||||
if (HasFocusFunction)
|
||||
if (auto V = DFT.Get(Sha1Str))
|
||||
II.DataFlowTraceForFocusFunction = *V;
|
||||
// This is a gross heuristic.
|
||||
// Ideally, when we add an element to a corpus we need to know its DFT.
|
||||
// But if we don't, we'll use the DFT of its base input.
|
||||
if (II.DataFlowTraceForFocusFunction.empty() && BaseII)
|
||||
II.DataFlowTraceForFocusFunction = BaseII->DataFlowTraceForFocusFunction;
|
||||
UpdateCorpusDistribution();
|
||||
PrintCorpus();
|
||||
// ValidateFeatureSet();
|
||||
}
|
||||
|
||||
// Debug-only
|
||||
void PrintUnit(const Unit &U) {
|
||||
if (!FeatureDebug) return;
|
||||
for (uint8_t C : U) {
|
||||
if (C != 'F' && C != 'U' && C != 'Z')
|
||||
C = '.';
|
||||
Printf("%c", C);
|
||||
}
|
||||
}
|
||||
|
||||
// Debug-only
|
||||
void PrintFeatureSet(const Vector<uint32_t> &FeatureSet) {
|
||||
if (!FeatureDebug) return;
|
||||
Printf("{");
|
||||
for (uint32_t Feature: FeatureSet)
|
||||
Printf("%u,", Feature);
|
||||
Printf("}");
|
||||
}
|
||||
|
||||
// Debug-only
|
||||
void PrintCorpus() {
|
||||
if (!FeatureDebug) return;
|
||||
Printf("======= CORPUS:\n");
|
||||
int i = 0;
|
||||
for (auto II : Inputs) {
|
||||
if (std::find(II->U.begin(), II->U.end(), 'F') != II->U.end()) {
|
||||
Printf("[%2d] ", i);
|
||||
Printf("%s sz=%zd ", Sha1ToString(II->Sha1).c_str(), II->U.size());
|
||||
PrintUnit(II->U);
|
||||
Printf(" ");
|
||||
PrintFeatureSet(II->UniqFeatureSet);
|
||||
Printf("\n");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void Replace(InputInfo *II, const Unit &U) {
|
||||
assert(II->U.size() > U.size());
|
||||
Hashes.erase(Sha1ToString(II->Sha1));
|
||||
DeleteFile(*II);
|
||||
ComputeSHA1(U.data(), U.size(), II->Sha1);
|
||||
Hashes.insert(Sha1ToString(II->Sha1));
|
||||
II->U = U;
|
||||
II->Reduced = true;
|
||||
UpdateCorpusDistribution();
|
||||
}
|
||||
|
||||
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.
|
||||
size_t ChooseUnitIdxToMutate(Random &Rand) {
|
||||
size_t Idx = static_cast<size_t>(CorpusDistribution(Rand));
|
||||
assert(Idx < Inputs.size());
|
||||
return Idx;
|
||||
}
|
||||
|
||||
void PrintStats() {
|
||||
for (size_t i = 0; i < Inputs.size(); i++) {
|
||||
const auto &II = *Inputs[i];
|
||||
Printf(" [% 3zd %s] sz: % 5zd runs: % 5zd succ: % 5zd focus: %d\n", i,
|
||||
Sha1ToString(II.Sha1).c_str(), II.U.size(),
|
||||
II.NumExecutedMutations, II.NumSuccessfullMutations, II.HasFocusFunction);
|
||||
}
|
||||
}
|
||||
|
||||
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 DeleteFile(const InputInfo &II) {
|
||||
if (!OutputCorpus.empty() && II.MayDeleteFile)
|
||||
RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1)));
|
||||
}
|
||||
|
||||
void DeleteInput(size_t Idx) {
|
||||
InputInfo &II = *Inputs[Idx];
|
||||
DeleteFile(II);
|
||||
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);
|
||||
} else {
|
||||
NumAddedFeatures++;
|
||||
}
|
||||
NumUpdatedFeatures++;
|
||||
if (FeatureDebug)
|
||||
Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize);
|
||||
SmallestElementPerFeature[Idx] = Inputs.size();
|
||||
InputSizesPerFeature[Idx] = NewSize;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsFeatureNew(size_t Idx, uint32_t NewSize, bool Shrink) {
|
||||
assert(NewSize);
|
||||
uint32_t OldSize = GetFeature(Idx % kFeatureSetSize);
|
||||
return OldSize == 0 || (Shrink && OldSize > NewSize);
|
||||
}
|
||||
|
||||
size_t NumFeatures() const { return NumAddedFeatures; }
|
||||
size_t NumFeatureUpdates() const { return NumUpdatedFeatures; }
|
||||
|
||||
private:
|
||||
|
||||
static const bool FeatureDebug = false;
|
||||
|
||||
size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; }
|
||||
|
||||
void ValidateFeatureSet() {
|
||||
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.
|
||||
//
|
||||
// Hypothesis: units added to the corpus last are more interesting.
|
||||
//
|
||||
// Hypothesis: inputs with infrequent features are more interesting.
|
||||
void UpdateCorpusDistribution() {
|
||||
size_t N = Inputs.size();
|
||||
assert(N);
|
||||
Intervals.resize(N + 1);
|
||||
Weights.resize(N);
|
||||
std::iota(Intervals.begin(), Intervals.end(), 0);
|
||||
for (size_t i = 0; i < N; i++)
|
||||
Weights[i] = Inputs[i]->NumFeatures
|
||||
? (i + 1) * (Inputs[i]->HasFocusFunction ? 1000 : 1)
|
||||
: 0.;
|
||||
if (FeatureDebug) {
|
||||
for (size_t i = 0; i < N; i++)
|
||||
Printf("%zd ", Inputs[i]->NumFeatures);
|
||||
Printf("SCORE\n");
|
||||
for (size_t i = 0; i < N; i++)
|
||||
Printf("%f ", Weights[i]);
|
||||
Printf("Weights\n");
|
||||
}
|
||||
CorpusDistribution = std::piecewise_constant_distribution<double>(
|
||||
Intervals.begin(), Intervals.end(), Weights.begin());
|
||||
}
|
||||
std::piecewise_constant_distribution<double> CorpusDistribution;
|
||||
|
||||
Vector<double> Intervals;
|
||||
Vector<double> Weights;
|
||||
|
||||
std::unordered_set<std::string> Hashes;
|
||||
Vector<InputInfo*> Inputs;
|
||||
|
||||
size_t NumAddedFeatures = 0;
|
||||
size_t NumUpdatedFeatures = 0;
|
||||
uint32_t InputSizesPerFeature[kFeatureSetSize];
|
||||
uint32_t SmallestElementPerFeature[kFeatureSetSize];
|
||||
|
||||
std::string OutputCorpus;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_CORPUS
|
52
contrib/compiler-rt/lib/fuzzer/FuzzerCrossOver.cpp
Normal file
52
contrib/compiler-rt/lib/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
|
91
contrib/compiler-rt/lib/fuzzer/FuzzerDataFlowTrace.cpp
Normal file
91
contrib/compiler-rt/lib/fuzzer/FuzzerDataFlowTrace.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
//===- FuzzerDataFlowTrace.cpp - DataFlowTrace ---*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::DataFlowTrace
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerDataFlowTrace.h"
|
||||
#include "FuzzerIO.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
void DataFlowTrace::Init(const std::string &DirPath,
|
||||
const std::string &FocusFunction) {
|
||||
if (DirPath.empty()) return;
|
||||
const char *kFunctionsTxt = "functions.txt";
|
||||
Printf("INFO: DataFlowTrace: reading from '%s'\n", DirPath.c_str());
|
||||
Vector<SizedFile> Files;
|
||||
GetSizedFilesFromDir(DirPath, &Files);
|
||||
std::string L;
|
||||
|
||||
// Read functions.txt
|
||||
std::ifstream IF(DirPlusFile(DirPath, kFunctionsTxt));
|
||||
size_t FocusFuncIdx = SIZE_MAX;
|
||||
size_t NumFunctions = 0;
|
||||
while (std::getline(IF, L, '\n')) {
|
||||
NumFunctions++;
|
||||
if (FocusFunction == L)
|
||||
FocusFuncIdx = NumFunctions - 1;
|
||||
}
|
||||
if (!NumFunctions || FocusFuncIdx == SIZE_MAX || Files.size() <= 1)
|
||||
return;
|
||||
// Read traces.
|
||||
size_t NumTraceFiles = 0;
|
||||
size_t NumTracesWithFocusFunction = 0;
|
||||
for (auto &SF : Files) {
|
||||
auto Name = Basename(SF.File);
|
||||
if (Name == kFunctionsTxt) continue;
|
||||
auto ParseError = [&](const char *Err) {
|
||||
Printf("DataFlowTrace: parse error: %s\n File: %s\n Line: %s\n", Err,
|
||||
Name.c_str(), L.c_str());
|
||||
};
|
||||
NumTraceFiles++;
|
||||
// Printf("=== %s\n", Name.c_str());
|
||||
std::ifstream IF(SF.File);
|
||||
while (std::getline(IF, L, '\n')) {
|
||||
size_t SpacePos = L.find(' ');
|
||||
if (SpacePos == std::string::npos)
|
||||
return ParseError("no space in the trace line");
|
||||
if (L.empty() || L[0] != 'F')
|
||||
return ParseError("the trace line doesn't start with 'F'");
|
||||
size_t N = std::atol(L.c_str() + 1);
|
||||
if (N >= NumFunctions)
|
||||
return ParseError("N is greater than the number of functions");
|
||||
if (N == FocusFuncIdx) {
|
||||
NumTracesWithFocusFunction++;
|
||||
const char *Beg = L.c_str() + SpacePos + 1;
|
||||
const char *End = L.c_str() + L.size();
|
||||
assert(Beg < End);
|
||||
size_t Len = End - Beg;
|
||||
Vector<uint8_t> V(Len);
|
||||
for (size_t I = 0; I < Len; I++) {
|
||||
if (Beg[I] != '0' && Beg[I] != '1')
|
||||
ParseError("the trace should contain only 0 or 1");
|
||||
V[I] = Beg[I] == '1';
|
||||
}
|
||||
Traces[Name] = V;
|
||||
// Print just a few small traces.
|
||||
if (NumTracesWithFocusFunction <= 3 && Len <= 16)
|
||||
Printf("%s => |%s|\n", Name.c_str(), L.c_str() + SpacePos + 1);
|
||||
break; // No need to parse the following lines.
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(NumTraceFiles == Files.size() - 1);
|
||||
Printf("INFO: DataFlowTrace: %zd trace files, %zd functions, "
|
||||
"%zd traces with focus function\n",
|
||||
NumTraceFiles, NumFunctions, NumTracesWithFocusFunction);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
56
contrib/compiler-rt/lib/fuzzer/FuzzerDataFlowTrace.h
Normal file
56
contrib/compiler-rt/lib/fuzzer/FuzzerDataFlowTrace.h
Normal file
@ -0,0 +1,56 @@
|
||||
//===- FuzzerDataFlowTrace.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::DataFlowTrace; reads and handles a data-flow trace.
|
||||
//
|
||||
// A data flow trace is generated by e.g. dataflow/DataFlow.cpp
|
||||
// and is stored on disk in a separate directory.
|
||||
//
|
||||
// The trace dir contains a file 'functions.txt' which lists function names,
|
||||
// oner per line, e.g.
|
||||
// ==> functions.txt <==
|
||||
// Func2
|
||||
// LLVMFuzzerTestOneInput
|
||||
// Func1
|
||||
//
|
||||
// All other files in the dir are the traces, see dataflow/DataFlow.cpp.
|
||||
// The name of the file is sha1 of the input used to generate the trace.
|
||||
//
|
||||
// Current status:
|
||||
// the data is parsed and the summary is printed, but the data is not yet
|
||||
// used in any other way.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_DATA_FLOW_TRACE
|
||||
#define LLVM_FUZZER_DATA_FLOW_TRACE
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace fuzzer {
|
||||
class DataFlowTrace {
|
||||
public:
|
||||
void Init(const std::string &DirPath, const std::string &FocusFunction);
|
||||
void Clear() { Traces.clear(); }
|
||||
const Vector<uint8_t> *Get(const std::string &InputSha1) const {
|
||||
auto It = Traces.find(InputSha1);
|
||||
if (It != Traces.end())
|
||||
return &It->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
// Input's sha1 => DFT for the FocusFunction.
|
||||
std::unordered_map<std::string, Vector<uint8_t> > Traces;
|
||||
};
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_DATA_FLOW_TRACE
|
192
contrib/compiler-rt/lib/fuzzer/FuzzerDefs.h
Normal file
192
contrib/compiler-rt/lib/fuzzer/FuzzerDefs.h
Normal file
@ -0,0 +1,192 @@
|
||||
//===- 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>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
|
||||
// Platform detection.
|
||||
#ifdef __linux__
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_FUCHSIA 0
|
||||
#define LIBFUZZER_LINUX 1
|
||||
#define LIBFUZZER_NETBSD 0
|
||||
#define LIBFUZZER_FREEBSD 0
|
||||
#define LIBFUZZER_OPENBSD 0
|
||||
#define LIBFUZZER_WINDOWS 0
|
||||
#elif __APPLE__
|
||||
#define LIBFUZZER_APPLE 1
|
||||
#define LIBFUZZER_FUCHSIA 0
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_NETBSD 0
|
||||
#define LIBFUZZER_FREEBSD 0
|
||||
#define LIBFUZZER_OPENBSD 0
|
||||
#define LIBFUZZER_WINDOWS 0
|
||||
#elif __NetBSD__
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_FUCHSIA 0
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_NETBSD 1
|
||||
#define LIBFUZZER_FREEBSD 0
|
||||
#define LIBFUZZER_OPENBSD 0
|
||||
#define LIBFUZZER_WINDOWS 0
|
||||
#elif __FreeBSD__
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_FUCHSIA 0
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_NETBSD 0
|
||||
#define LIBFUZZER_FREEBSD 1
|
||||
#define LIBFUZZER_OPENBSD 0
|
||||
#define LIBFUZZER_WINDOWS 0
|
||||
#elif __OpenBSD__
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_FUCHSIA 0
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_NETBSD 0
|
||||
#define LIBFUZZER_FREEBSD 0
|
||||
#define LIBFUZZER_OPENBSD 1
|
||||
#define LIBFUZZER_WINDOWS 0
|
||||
#elif _WIN32
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_FUCHSIA 0
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_NETBSD 0
|
||||
#define LIBFUZZER_FREEBSD 0
|
||||
#define LIBFUZZER_OPENBSD 0
|
||||
#define LIBFUZZER_WINDOWS 1
|
||||
#elif __Fuchsia__
|
||||
#define LIBFUZZER_APPLE 0
|
||||
#define LIBFUZZER_FUCHSIA 1
|
||||
#define LIBFUZZER_LINUX 0
|
||||
#define LIBFUZZER_NETBSD 0
|
||||
#define LIBFUZZER_FREEBSD 0
|
||||
#define LIBFUZZER_OPENBSD 0
|
||||
#define LIBFUZZER_WINDOWS 0
|
||||
#else
|
||||
#error "Support for your platform has not been implemented"
|
||||
#endif
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#define LIBFUZZER_POSIX \
|
||||
(LIBFUZZER_APPLE || LIBFUZZER_LINUX || LIBFUZZER_NETBSD || \
|
||||
LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD)
|
||||
|
||||
#ifdef __x86_64
|
||||
# if __has_attribute(target)
|
||||
# define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt")))
|
||||
# else
|
||||
# define ATTRIBUTE_TARGET_POPCNT
|
||||
# endif
|
||||
#else
|
||||
# define ATTRIBUTE_TARGET_POPCNT
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __clang__ // avoid gcc warning.
|
||||
# if __has_attribute(no_sanitize)
|
||||
# define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory")))
|
||||
# else
|
||||
# define ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
# endif
|
||||
# define ALWAYS_INLINE __attribute__((always_inline))
|
||||
#else
|
||||
# define ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
# define ALWAYS_INLINE
|
||||
#endif // __clang__
|
||||
|
||||
#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
|
||||
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(address_sanitizer)
|
||||
# define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_ADDRESS
|
||||
# elif __has_feature(memory_sanitizer)
|
||||
# define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
# else
|
||||
# define ATTRIBUTE_NO_SANITIZE_ALL
|
||||
# endif
|
||||
#else
|
||||
# define ATTRIBUTE_NO_SANITIZE_ALL
|
||||
#endif
|
||||
|
||||
#if LIBFUZZER_WINDOWS
|
||||
#define ATTRIBUTE_INTERFACE __declspec(dllexport)
|
||||
#else
|
||||
#define ATTRIBUTE_INTERFACE __attribute__((visibility("default")))
|
||||
#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;
|
||||
|
||||
// We are using a custom allocator to give a different symbol name to STL
|
||||
// containers in order to avoid ODR violations.
|
||||
template<typename T>
|
||||
class fuzzer_allocator: public std::allocator<T> {
|
||||
public:
|
||||
fuzzer_allocator() = default;
|
||||
|
||||
template<class U>
|
||||
fuzzer_allocator(const fuzzer_allocator<U>&) {}
|
||||
|
||||
template<class Other>
|
||||
struct rebind { typedef fuzzer_allocator<Other> other; };
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using Vector = std::vector<T, fuzzer_allocator<T>>;
|
||||
|
||||
template<typename T>
|
||||
using Set = std::set<T, std::less<T>, fuzzer_allocator<T>>;
|
||||
|
||||
typedef Vector<uint8_t> Unit;
|
||||
typedef Vector<Unit> UnitVector;
|
||||
typedef int (*UserCallback)(const uint8_t *Data, size_t Size);
|
||||
|
||||
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback);
|
||||
|
||||
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); }
|
||||
|
||||
uint8_t *ExtraCountersBegin();
|
||||
uint8_t *ExtraCountersEnd();
|
||||
void ClearExtraCounters();
|
||||
|
||||
extern bool RunningUserCallback;
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_DEFS_H
|
119
contrib/compiler-rt/lib/fuzzer/FuzzerDictionary.h
Normal file
119
contrib/compiler-rt/lib/fuzzer/FuzzerDictionary.h
Normal file
@ -0,0 +1,119 @@
|
||||
//===- 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 kMaxSizeT> class FixedWord {
|
||||
public:
|
||||
static const size_t kMaxSize = kMaxSizeT;
|
||||
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);
|
||||
}
|
||||
|
||||
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<64> Word;
|
||||
|
||||
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 successful, 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 successfully.
|
||||
bool ParseDictionaryFile(const std::string &Text, Vector<Unit> *Units);
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_DICTIONARY_H
|
778
contrib/compiler-rt/lib/fuzzer/FuzzerDriver.cpp
Normal file
778
contrib/compiler-rt/lib/fuzzer/FuzzerDriver.cpp
Normal file
@ -0,0 +1,778 @@
|
||||
//===- 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 "FuzzerCommand.h"
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerInterface.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "FuzzerShmem.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#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 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 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 Vector<std::string>;
|
||||
for (size_t A = 1; A < Args.size(); A++) {
|
||||
if (ParseOneFlag(Args[A].c_str())) {
|
||||
if (Flags.ignore_remaining_args)
|
||||
break;
|
||||
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 Command &BaseCmd, 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";
|
||||
Command Cmd(BaseCmd);
|
||||
Cmd.setOutputFile(Log);
|
||||
Cmd.combineOutAndErr();
|
||||
if (Flags.verbosity) {
|
||||
std::string CommandLine = Cmd.toString();
|
||||
Printf("%s\n", CommandLine.c_str());
|
||||
}
|
||||
int ExitCode = ExecuteCommand(Cmd);
|
||||
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 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 Vector<std::string> &Args,
|
||||
unsigned NumWorkers, unsigned NumJobs) {
|
||||
std::atomic<unsigned> Counter(0);
|
||||
std::atomic<bool> HasErrors(false);
|
||||
Command Cmd(Args);
|
||||
Cmd.removeFlag("jobs");
|
||||
Cmd.removeFlag("workers");
|
||||
Vector<std::thread> V;
|
||||
std::thread Pulse(PulseThread);
|
||||
Pulse.detach();
|
||||
for (unsigned i = 0; i < NumWorkers; i++)
|
||||
V.push_back(std::thread(WorkerThread, std::ref(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->ExecuteCallback(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;
|
||||
}
|
||||
|
||||
static std::string GetDedupTokenFromFile(const std::string &Path) {
|
||||
auto S = FileToString(Path);
|
||||
auto Beg = S.find("DEDUP_TOKEN:");
|
||||
if (Beg == std::string::npos)
|
||||
return "";
|
||||
auto End = S.find('\n', Beg);
|
||||
if (End == std::string::npos)
|
||||
return "";
|
||||
return S.substr(Beg, End - Beg);
|
||||
}
|
||||
|
||||
int CleanseCrashInput(const Vector<std::string> &Args,
|
||||
const FuzzingOptions &Options) {
|
||||
if (Inputs->size() != 1 || !Flags.exact_artifact_path) {
|
||||
Printf("ERROR: -cleanse_crash should be given one input file and"
|
||||
" -exact_artifact_path\n");
|
||||
exit(1);
|
||||
}
|
||||
std::string InputFilePath = Inputs->at(0);
|
||||
std::string OutputFilePath = Flags.exact_artifact_path;
|
||||
Command Cmd(Args);
|
||||
Cmd.removeFlag("cleanse_crash");
|
||||
|
||||
assert(Cmd.hasArgument(InputFilePath));
|
||||
Cmd.removeArgument(InputFilePath);
|
||||
|
||||
auto LogFilePath = DirPlusFile(
|
||||
TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
|
||||
auto TmpFilePath = DirPlusFile(
|
||||
TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".repro");
|
||||
Cmd.addArgument(TmpFilePath);
|
||||
Cmd.setOutputFile(LogFilePath);
|
||||
Cmd.combineOutAndErr();
|
||||
|
||||
std::string CurrentFilePath = InputFilePath;
|
||||
auto U = FileToVector(CurrentFilePath);
|
||||
size_t Size = U.size();
|
||||
|
||||
const Vector<uint8_t> ReplacementBytes = {' ', 0xff};
|
||||
for (int NumAttempts = 0; NumAttempts < 5; NumAttempts++) {
|
||||
bool Changed = false;
|
||||
for (size_t Idx = 0; Idx < Size; Idx++) {
|
||||
Printf("CLEANSE[%d]: Trying to replace byte %zd of %zd\n", NumAttempts,
|
||||
Idx, Size);
|
||||
uint8_t OriginalByte = U[Idx];
|
||||
if (ReplacementBytes.end() != std::find(ReplacementBytes.begin(),
|
||||
ReplacementBytes.end(),
|
||||
OriginalByte))
|
||||
continue;
|
||||
for (auto NewByte : ReplacementBytes) {
|
||||
U[Idx] = NewByte;
|
||||
WriteToFile(U, TmpFilePath);
|
||||
auto ExitCode = ExecuteCommand(Cmd);
|
||||
RemoveFile(TmpFilePath);
|
||||
if (!ExitCode) {
|
||||
U[Idx] = OriginalByte;
|
||||
} else {
|
||||
Changed = true;
|
||||
Printf("CLEANSE: Replaced byte %zd with 0x%x\n", Idx, NewByte);
|
||||
WriteToFile(U, OutputFilePath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Changed) break;
|
||||
}
|
||||
RemoveFile(LogFilePath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MinimizeCrashInput(const Vector<std::string> &Args,
|
||||
const FuzzingOptions &Options) {
|
||||
if (Inputs->size() != 1) {
|
||||
Printf("ERROR: -minimize_crash should be given one input file\n");
|
||||
exit(1);
|
||||
}
|
||||
std::string InputFilePath = Inputs->at(0);
|
||||
Command BaseCmd(Args);
|
||||
BaseCmd.removeFlag("minimize_crash");
|
||||
BaseCmd.removeFlag("exact_artifact_path");
|
||||
assert(BaseCmd.hasArgument(InputFilePath));
|
||||
BaseCmd.removeArgument(InputFilePath);
|
||||
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.addFlag("max_total_time", "600");
|
||||
}
|
||||
|
||||
auto LogFilePath = DirPlusFile(
|
||||
TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
|
||||
BaseCmd.setOutputFile(LogFilePath);
|
||||
BaseCmd.combineOutAndErr();
|
||||
|
||||
std::string CurrentFilePath = InputFilePath;
|
||||
while (true) {
|
||||
Unit U = FileToVector(CurrentFilePath);
|
||||
Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n",
|
||||
CurrentFilePath.c_str(), U.size());
|
||||
|
||||
Command Cmd(BaseCmd);
|
||||
Cmd.addArgument(CurrentFilePath);
|
||||
|
||||
std::string CommandLine = Cmd.toString();
|
||||
Printf("CRASH_MIN: executing: %s\n", CommandLine.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());
|
||||
auto DedupToken1 = GetDedupTokenFromFile(LogFilePath);
|
||||
if (!DedupToken1.empty())
|
||||
Printf("CRASH_MIN: DedupToken1: %s\n", DedupToken1.c_str());
|
||||
|
||||
std::string ArtifactPath =
|
||||
Flags.exact_artifact_path
|
||||
? Flags.exact_artifact_path
|
||||
: Options.ArtifactPrefix + "minimized-from-" + Hash(U);
|
||||
Cmd.addFlag("minimize_crash_internal_step", "1");
|
||||
Cmd.addFlag("exact_artifact_path", ArtifactPath);
|
||||
CommandLine = Cmd.toString();
|
||||
Printf("CRASH_MIN: executing: %s\n", CommandLine.c_str());
|
||||
ExitCode = ExecuteCommand(Cmd);
|
||||
CopyFileToErr(LogFilePath);
|
||||
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());
|
||||
break;
|
||||
}
|
||||
auto DedupToken2 = GetDedupTokenFromFile(LogFilePath);
|
||||
if (!DedupToken2.empty())
|
||||
Printf("CRASH_MIN: DedupToken2: %s\n", DedupToken2.c_str());
|
||||
|
||||
if (DedupToken1 != DedupToken2) {
|
||||
if (Flags.exact_artifact_path) {
|
||||
CurrentFilePath = Flags.exact_artifact_path;
|
||||
WriteToFile(U, CurrentFilePath);
|
||||
}
|
||||
Printf("CRASH_MIN: mismatch in dedup tokens"
|
||||
" (looks like a different bug). Won't minimize further\n");
|
||||
break;
|
||||
}
|
||||
|
||||
CurrentFilePath = ArtifactPath;
|
||||
Printf("*********************************\n");
|
||||
}
|
||||
RemoveFile(LogFilePath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
|
||||
assert(Inputs->size() == 1);
|
||||
std::string InputFilePath = Inputs->at(0);
|
||||
Unit U = FileToVector(InputFilePath);
|
||||
Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size());
|
||||
if (U.size() < 2) {
|
||||
Printf("INFO: The input is small enough, exiting\n");
|
||||
exit(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 AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict,
|
||||
UnitVector& Corpus) {
|
||||
Printf("Started dictionary minimization (up to %d tests)\n",
|
||||
Dict.size() * Corpus.size() * 2);
|
||||
|
||||
// Scores and usage count for each dictionary unit.
|
||||
Vector<int> Scores(Dict.size());
|
||||
Vector<int> Usages(Dict.size());
|
||||
|
||||
Vector<size_t> InitialFeatures;
|
||||
Vector<size_t> ModifiedFeatures;
|
||||
for (auto &C : Corpus) {
|
||||
// Get coverage for the testcase without modifications.
|
||||
F->ExecuteCallback(C.data(), C.size());
|
||||
InitialFeatures.clear();
|
||||
TPC.CollectFeatures([&](size_t Feature) {
|
||||
InitialFeatures.push_back(Feature);
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < Dict.size(); ++i) {
|
||||
Vector<uint8_t> Data = C;
|
||||
auto StartPos = std::search(Data.begin(), Data.end(),
|
||||
Dict[i].begin(), Dict[i].end());
|
||||
// Skip dictionary unit, if the testcase does not contain it.
|
||||
if (StartPos == Data.end())
|
||||
continue;
|
||||
|
||||
++Usages[i];
|
||||
while (StartPos != Data.end()) {
|
||||
// Replace all occurrences of dictionary unit in the testcase.
|
||||
auto EndPos = StartPos + Dict[i].size();
|
||||
for (auto It = StartPos; It != EndPos; ++It)
|
||||
*It ^= 0xFF;
|
||||
|
||||
StartPos = std::search(EndPos, Data.end(),
|
||||
Dict[i].begin(), Dict[i].end());
|
||||
}
|
||||
|
||||
// Get coverage for testcase with masked occurrences of dictionary unit.
|
||||
F->ExecuteCallback(Data.data(), Data.size());
|
||||
ModifiedFeatures.clear();
|
||||
TPC.CollectFeatures([&](size_t Feature) {
|
||||
ModifiedFeatures.push_back(Feature);
|
||||
});
|
||||
|
||||
if (InitialFeatures == ModifiedFeatures)
|
||||
--Scores[i];
|
||||
else
|
||||
Scores[i] += 2;
|
||||
}
|
||||
}
|
||||
|
||||
Printf("###### Useless dictionary elements. ######\n");
|
||||
for (size_t i = 0; i < Dict.size(); ++i) {
|
||||
// Dictionary units with positive score are treated as useful ones.
|
||||
if (Scores[i] > 0)
|
||||
continue;
|
||||
|
||||
Printf("\"");
|
||||
PrintASCII(Dict[i].data(), Dict[i].size(), "\"");
|
||||
Printf(" # Score: %d, Used: %d\n", Scores[i], Usages[i]);
|
||||
}
|
||||
Printf("###### End of useless dictionary elements. ######\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
|
||||
using namespace fuzzer;
|
||||
assert(argc && argv && "Argument pointers cannot be nullptr");
|
||||
std::string Argv0((*argv)[0]);
|
||||
EF = new ExternalFunctions();
|
||||
if (EF->LLVMFuzzerInitialize)
|
||||
EF->LLVMFuzzerInitialize(argc, argv);
|
||||
if (EF->__msan_scoped_disable_interceptor_checks)
|
||||
EF->__msan_scoped_disable_interceptor_checks();
|
||||
const Vector<std::string> Args(*argv, *argv + *argc);
|
||||
assert(!Args.empty());
|
||||
ProgName = new std::string(Args[0]);
|
||||
if (Argv0 != *ProgName) {
|
||||
Printf("ERROR: argv[0] has been modified in LLVMFuzzerInitialize\n");
|
||||
exit(1);
|
||||
}
|
||||
ParseFlags(Args);
|
||||
if (Flags.help) {
|
||||
PrintHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
FuzzingOptions Options;
|
||||
Options.Verbosity = Flags.verbosity;
|
||||
Options.MaxLen = Flags.max_len;
|
||||
Options.LenControl = Flags.len_control;
|
||||
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.ReduceDepth = Flags.reduce_depth;
|
||||
Options.UseCounters = Flags.use_counters;
|
||||
Options.UseMemmem = Flags.use_memmem;
|
||||
Options.UseCmp = Flags.use_cmp;
|
||||
Options.UseValueProfile = Flags.use_value_profile;
|
||||
Options.Shrink = Flags.shrink;
|
||||
Options.ReduceInputs = Flags.reduce_inputs;
|
||||
Options.ShuffleAtStartUp = Flags.shuffle;
|
||||
Options.PreferSmall = Flags.prefer_small;
|
||||
Options.ReloadIntervalSec = Flags.reload;
|
||||
Options.OnlyASCII = Flags.only_ascii;
|
||||
Options.DetectLeaks = Flags.detect_leaks;
|
||||
Options.PurgeAllocatorIntervalSec = Flags.purge_allocator_interval;
|
||||
Options.TraceMalloc = Flags.trace_malloc;
|
||||
Options.RssLimitMb = Flags.rss_limit_mb;
|
||||
Options.MallocLimitMb = Flags.malloc_limit_mb;
|
||||
if (!Options.MallocLimitMb)
|
||||
Options.MallocLimitMb = Options.RssLimitMb;
|
||||
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;
|
||||
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.PrintNewCovFuncs = Flags.print_funcs;
|
||||
Options.PrintFinalStats = Flags.print_final_stats;
|
||||
Options.PrintMutationStats = Flags.print_mutation_stats;
|
||||
Options.PrintCorpusStats = Flags.print_corpus_stats;
|
||||
Options.PrintCoverage = Flags.print_coverage;
|
||||
Options.PrintUnstableStats = Flags.print_unstable_stats;
|
||||
if (Flags.handle_unstable == TracePC::MinUnstable ||
|
||||
Flags.handle_unstable == TracePC::ZeroUnstable)
|
||||
Options.HandleUnstable = Flags.handle_unstable;
|
||||
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;
|
||||
if (Flags.focus_function)
|
||||
Options.FocusFunction = Flags.focus_function;
|
||||
if (Flags.data_flow_trace)
|
||||
Options.DataFlowTrace = Flags.data_flow_trace;
|
||||
|
||||
unsigned Seed = Flags.seed;
|
||||
// Initialize Seed.
|
||||
if (Seed == 0)
|
||||
Seed =
|
||||
std::chrono::system_clock::now().time_since_epoch().count() + 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;
|
||||
Options.HandleXfsz = Flags.handle_xfsz;
|
||||
Options.HandleUsr1 = Flags.handle_usr1;
|
||||
Options.HandleUsr2 = Flags.handle_usr2;
|
||||
SetSignalHandler(Options);
|
||||
|
||||
std::atexit(Fuzzer::StaticExitCallback);
|
||||
|
||||
if (Flags.minimize_crash)
|
||||
return MinimizeCrashInput(Args, Options);
|
||||
|
||||
if (Flags.minimize_crash_internal_step)
|
||||
return MinimizeCrashInputInternalStep(F, Corpus);
|
||||
|
||||
if (Flags.cleanse_crash)
|
||||
return CleanseCrashInput(Args, Options);
|
||||
|
||||
#if 0 // deprecated, to be removed.
|
||||
if (auto Name = Flags.run_equivalence_server) {
|
||||
SMR.Destroy(Name);
|
||||
if (!SMR.Create(Name)) {
|
||||
Printf("ERROR: can't create shared memory region\n");
|
||||
return 1;
|
||||
}
|
||||
Printf("INFO: EQUIVALENCE SERVER UP\n");
|
||||
while (true) {
|
||||
SMR.WaitClient();
|
||||
size_t Size = SMR.ReadByteArraySize();
|
||||
SMR.WriteByteArray(nullptr, 0);
|
||||
const Unit tmp(SMR.GetByteArray(), SMR.GetByteArray() + Size);
|
||||
F->ExecuteCallback(tmp.data(), tmp.size());
|
||||
SMR.PostServer();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (auto Name = Flags.use_equivalence_server) {
|
||||
if (!SMR.Open(Name)) {
|
||||
Printf("ERROR: can't open shared memory region\n");
|
||||
return 1;
|
||||
}
|
||||
Printf("INFO: EQUIVALENCE CLIENT UP\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
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) {
|
||||
F->CrashResistantMerge(Args, *Inputs,
|
||||
Flags.load_coverage_summary,
|
||||
Flags.save_coverage_summary,
|
||||
Flags.merge_control_file);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (Flags.merge_inner) {
|
||||
const size_t kDefaultMaxMergeLen = 1 << 20;
|
||||
if (Options.MaxLen == 0)
|
||||
F->SetMaxInputLen(kDefaultMaxMergeLen);
|
||||
assert(Flags.merge_control_file);
|
||||
F->CrashResistantMergeInternalStep(Flags.merge_control_file);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (Flags.analyze_dict) {
|
||||
size_t MaxLen = INT_MAX; // Large max length.
|
||||
UnitVector InitialCorpus;
|
||||
for (auto &Inp : *Inputs) {
|
||||
Printf("Loading corpus dir: %s\n", Inp.c_str());
|
||||
ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr,
|
||||
MaxLen, /*ExitOnError=*/false);
|
||||
}
|
||||
|
||||
if (Dictionary.empty() || Inputs->empty()) {
|
||||
Printf("ERROR: can't analyze dict without dict and corpus provided\n");
|
||||
return 1;
|
||||
}
|
||||
if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) {
|
||||
Printf("Dictionary analysis failed\n");
|
||||
exit(1);
|
||||
}
|
||||
Printf("Dictionary analysis succeeded\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
F->Loop(*Inputs);
|
||||
|
||||
if (Flags.verbosity)
|
||||
Printf("Done %zd 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
|
51
contrib/compiler-rt/lib/fuzzer/FuzzerExtFunctions.def
Normal file
51
contrib/compiler-rt/lib/fuzzer/FuzzerExtFunctions.def
Normal file
@ -0,0 +1,51 @@
|
||||
//===- 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_acquire_crash_state, bool, (), true);
|
||||
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_purge_allocator, void, (), false);
|
||||
EXT_FUNC(__sanitizer_print_memory_profile, int, (size_t, 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_set_death_callback, void, (void (*)(void)), true);
|
||||
EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false);
|
||||
EXT_FUNC(__sanitizer_dump_coverage, void, (const uintptr_t *, uintptr_t),
|
||||
false);
|
||||
EXT_FUNC(__msan_scoped_disable_interceptor_checks, void, (), false);
|
||||
EXT_FUNC(__msan_scoped_enable_interceptor_checks, void, (), false);
|
||||
EXT_FUNC(__msan_unpoison, void, (const volatile void *, size_t size), false);
|
35
contrib/compiler-rt/lib/fuzzer/FuzzerExtFunctions.h
Normal file
35
contrib/compiler-rt/lib/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
contrib/compiler-rt/lib/fuzzer/FuzzerExtFunctionsDlsym.cpp
Normal file
52
contrib/compiler-rt/lib/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
|
@ -0,0 +1,62 @@
|
||||
//===- FuzzerExtFunctionsDlsymWin.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 dynamic loading for Windows.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "Windows.h"
|
||||
|
||||
// This must be included after Windows.h.
|
||||
#include "Psapi.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
ExternalFunctions::ExternalFunctions() {
|
||||
HMODULE Modules[1024];
|
||||
DWORD BytesNeeded;
|
||||
HANDLE CurrentProcess = GetCurrentProcess();
|
||||
|
||||
if (!EnumProcessModules(CurrentProcess, Modules, sizeof(Modules),
|
||||
&BytesNeeded)) {
|
||||
Printf("EnumProcessModules failed (error: %d).\n", GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (sizeof(Modules) < BytesNeeded) {
|
||||
Printf("Error: the array is not big enough to hold all loaded modules.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < (BytesNeeded / sizeof(HMODULE)); i++)
|
||||
{
|
||||
FARPROC Fn;
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
if (this->NAME == nullptr) { \
|
||||
Fn = GetProcAddress(Modules[i], #NAME); \
|
||||
if (Fn == nullptr) \
|
||||
Fn = GetProcAddress(Modules[i], #NAME "__dll"); \
|
||||
this->NAME = (decltype(ExternalFunctions::NAME)) Fn; \
|
||||
}
|
||||
#include "FuzzerExtFunctions.def"
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
if (this->NAME == nullptr && WARN) \
|
||||
Printf("WARNING: Failed to find function \"%s\".\n", #NAME);
|
||||
#include "FuzzerExtFunctions.def"
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
55
contrib/compiler-rt/lib/fuzzer/FuzzerExtFunctionsWeak.cpp
Normal file
55
contrib/compiler-rt/lib/fuzzer/FuzzerExtFunctionsWeak.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
//===- 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 || LIBFUZZER_NETBSD || LIBFUZZER_FUCHSIA || \
|
||||
LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD
|
||||
|
||||
#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(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(::NAME)), \
|
||||
#NAME, WARN);
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif
|
@ -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
|
42
contrib/compiler-rt/lib/fuzzer/FuzzerExtraCounters.cpp
Normal file
42
contrib/compiler-rt/lib/fuzzer/FuzzerExtraCounters.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
//===- FuzzerExtraCounters.cpp - Extra coverage counters ------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Extra coverage counters defined by user code.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \
|
||||
LIBFUZZER_OPENBSD
|
||||
__attribute__((weak)) extern uint8_t __start___libfuzzer_extra_counters;
|
||||
__attribute__((weak)) extern uint8_t __stop___libfuzzer_extra_counters;
|
||||
|
||||
namespace fuzzer {
|
||||
uint8_t *ExtraCountersBegin() { return &__start___libfuzzer_extra_counters; }
|
||||
uint8_t *ExtraCountersEnd() { return &__stop___libfuzzer_extra_counters; }
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void ClearExtraCounters() { // hand-written memset, don't asan-ify.
|
||||
uintptr_t *Beg = reinterpret_cast<uintptr_t*>(ExtraCountersBegin());
|
||||
uintptr_t *End = reinterpret_cast<uintptr_t*>(ExtraCountersEnd());
|
||||
for (; Beg < End; Beg++) {
|
||||
*Beg = 0;
|
||||
__asm__ __volatile__("" : : : "memory");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#else
|
||||
// TODO: implement for other platforms.
|
||||
namespace fuzzer {
|
||||
uint8_t *ExtraCountersBegin() { return nullptr; }
|
||||
uint8_t *ExtraCountersEnd() { return nullptr; }
|
||||
void ClearExtraCounters() {}
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif
|
165
contrib/compiler-rt/lib/fuzzer/FuzzerFlags.def
Normal file
165
contrib/compiler-rt/lib/fuzzer/FuzzerFlags.def
Normal file
@ -0,0 +1,165 @@
|
||||
//===- 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(len_control, 1000, "Try generating small inputs first, "
|
||||
"then try larger inputs over time. Specifies the rate at which the length "
|
||||
"limit is increased (smaller == faster). If 0, immediately try inputs with "
|
||||
"size up to max_len.")
|
||||
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(reduce_depth, 0, "Experimental/internal. "
|
||||
"Reduce depth if mutations lose unique features")
|
||||
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_inner, "internal flag")
|
||||
FUZZER_FLAG_STRING(merge_control_file,
|
||||
"Specify a control file used for the merge process. "
|
||||
"If a merge process gets killed it tries to leave this file "
|
||||
"in a state suitable for resuming the merge. "
|
||||
"By default a temporary file will be used.")
|
||||
FUZZER_FLAG_STRING(save_coverage_summary, "Experimental:"
|
||||
" save coverage summary to a given file."
|
||||
" Used with -merge=1")
|
||||
FUZZER_FLAG_STRING(load_coverage_summary, "Experimental:"
|
||||
" load coverage summary from a given file."
|
||||
" Treat this coverage as belonging to the first corpus. "
|
||||
" Used with -merge=1")
|
||||
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."
|
||||
" Use with -exact_artifact_path to specify the output."
|
||||
" Combine with ASAN_OPTIONS=dedup_token_length=3 (or similar) to ensure that"
|
||||
" the minimized input triggers the same crash."
|
||||
)
|
||||
FUZZER_FLAG_INT(cleanse_crash, 0, "If 1, tries to cleanse the provided"
|
||||
" crash input to make it contain fewer original bytes."
|
||||
" Use with -exact_artifact_path to specify the output."
|
||||
)
|
||||
FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag")
|
||||
FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters")
|
||||
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 inputs.")
|
||||
FUZZER_FLAG_INT(reduce_inputs, 1,
|
||||
"Try to reduce the size of inputs while preserving their full feature sets")
|
||||
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(print_pcs, 0, "If 1, print out newly covered PCs.")
|
||||
FUZZER_FLAG_INT(print_funcs, 2, "If >=1, print out at most this number of "
|
||||
"newly covered functions.")
|
||||
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 as text"
|
||||
" at exit.")
|
||||
FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated."
|
||||
" If 1, dump coverage information as a"
|
||||
" .sancov file at exit.")
|
||||
FUZZER_FLAG_INT(handle_unstable, 0, "Experimental."
|
||||
" Executes every input 3 times in total if a unique feature"
|
||||
" is found during the first execution."
|
||||
" If 1, we only use the minimum hit count from the 3 runs"
|
||||
" to determine whether an input is interesting."
|
||||
" If 2, we disregard edges that are found unstable for"
|
||||
" feature collection.")
|
||||
FUZZER_FLAG_INT(print_unstable_stats, 0, "Experimental."
|
||||
" If 1, print unstable statistics at exit.")
|
||||
FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.")
|
||||
FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.")
|
||||
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(handle_xfsz, 1, "If 1, try to intercept SIGXFSZ.")
|
||||
FUZZER_FLAG_INT(handle_usr1, 1, "If 1, try to intercept SIGUSR1.")
|
||||
FUZZER_FLAG_INT(handle_usr2, 1, "If 1, try to intercept SIGUSR2.")
|
||||
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. stderr of asan.")
|
||||
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(purge_allocator_interval, 1, "Purge allocator caches and "
|
||||
"quarantines every <N> seconds. When rss_limit_mb is specified (>0), "
|
||||
"purging starts when RSS exceeds 50% of rss_limit_mb. Pass "
|
||||
"purge_allocator_interval=-1 to disable this functionality.")
|
||||
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_INT(malloc_limit_mb, 0, "If non-zero, the fuzzer will exit "
|
||||
"if the target tries to allocate this number of Mb with one malloc call. "
|
||||
"If zero (default) same limit as rss_limit_mb is applied.")
|
||||
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_FLAG_INT(ignore_remaining_args, 0, "If 1, ignore all arguments passed "
|
||||
"after this one. Useful for fuzzers that need to do their own "
|
||||
"argument parsing.")
|
||||
FUZZER_FLAG_STRING(focus_function, "Experimental. "
|
||||
"Fuzzing will focus on inputs that trigger calls to this function")
|
||||
|
||||
FUZZER_DEPRECATED_FLAG(run_equivalence_server)
|
||||
FUZZER_DEPRECATED_FLAG(use_equivalence_server)
|
||||
FUZZER_FLAG_INT(analyze_dict, 0, "Experimental")
|
||||
FUZZER_DEPRECATED_FLAG(use_clang_coverage)
|
||||
FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace")
|
||||
FUZZER_FLAG_INT(print_mutation_stats, 0, "Experimental")
|
137
contrib/compiler-rt/lib/fuzzer/FuzzerIO.cpp
Normal file
137
contrib/compiler-rt/lib/fuzzer/FuzzerIO.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
//===- 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);
|
||||
auto EndPos = T.tellg();
|
||||
if (EndPos < 0) return {};
|
||||
size_t FileLen = EndPos;
|
||||
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, Vector<Unit> *V,
|
||||
long *Epoch, size_t MaxSize, bool ExitOnError) {
|
||||
long E = Epoch ? *Epoch : 0;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V) {
|
||||
Vector<std::string> Files;
|
||||
ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/true);
|
||||
for (auto &File : Files)
|
||||
if (size_t Size = FileSize(File))
|
||||
V->push_back({File, Size});
|
||||
}
|
||||
|
||||
std::string DirPlusFile(const std::string &DirPath,
|
||||
const std::string &FileName) {
|
||||
return DirPath + GetSeparator() + FileName;
|
||||
}
|
||||
|
||||
std::string Basename(const std::string &Path, char Separator) {
|
||||
size_t Pos = Path.rfind(Separator);
|
||||
if (Pos == std::string::npos)
|
||||
return Path;
|
||||
assert(Pos < Path.size());
|
||||
return Path.substr(Pos + 1);
|
||||
}
|
||||
|
||||
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 *>(GetHandleFromFd(OutputFd)));
|
||||
DiscardOutput(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CloseStdout() {
|
||||
DiscardOutput(1);
|
||||
}
|
||||
|
||||
void Printf(const char *Fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, Fmt);
|
||||
vfprintf(OutputFile, Fmt, ap);
|
||||
va_end(ap);
|
||||
fflush(OutputFile);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
87
contrib/compiler-rt/lib/fuzzer/FuzzerIO.h
Normal file
87
contrib/compiler-rt/lib/fuzzer/FuzzerIO.h
Normal file
@ -0,0 +1,87 @@
|
||||
//===- 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, 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);
|
||||
|
||||
// Returns path to a TmpDir.
|
||||
std::string TmpDir();
|
||||
|
||||
bool IsInterestingCoverageFile(const std::string &FileName);
|
||||
|
||||
void DupAndCloseStderr();
|
||||
|
||||
void CloseStdout();
|
||||
|
||||
void Printf(const char *Fmt, ...);
|
||||
|
||||
// Print using raw syscalls, useful when printing at early init stages.
|
||||
void RawPrint(const char *Str);
|
||||
|
||||
// Platform specific functions:
|
||||
bool IsFile(const std::string &Path);
|
||||
size_t FileSize(const std::string &Path);
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
Vector<std::string> *V, bool TopDir);
|
||||
|
||||
struct SizedFile {
|
||||
std::string File;
|
||||
size_t Size;
|
||||
bool operator<(const SizedFile &B) const { return Size < B.Size; }
|
||||
};
|
||||
|
||||
void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V);
|
||||
|
||||
char GetSeparator();
|
||||
// Similar to the basename utility: returns the file name w/o the dir prefix.
|
||||
std::string Basename(const std::string &Path, char Separator = GetSeparator());
|
||||
|
||||
FILE* OpenFile(int Fd, const char *Mode);
|
||||
|
||||
int CloseFile(int Fd);
|
||||
|
||||
int DuplicateFile(int Fd);
|
||||
|
||||
void RemoveFile(const std::string &Path);
|
||||
|
||||
void DiscardOutput(int Fd);
|
||||
|
||||
intptr_t GetHandleFromFd(int fd);
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_IO_H
|
140
contrib/compiler-rt/lib/fuzzer/FuzzerIOPosix.cpp
Normal file
140
contrib/compiler-rt/lib/fuzzer/FuzzerIOPosix.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
//===- 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 || LIBFUZZER_FUCHSIA
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
static bool IsDirectory(const std::string &Path) {
|
||||
struct stat St;
|
||||
if (stat(Path.c_str(), &St))
|
||||
return false;
|
||||
return S_ISDIR(St.st_mode);
|
||||
}
|
||||
|
||||
size_t FileSize(const std::string &Path) {
|
||||
struct stat St;
|
||||
if (stat(Path.c_str(), &St))
|
||||
return 0;
|
||||
return St.st_size;
|
||||
}
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
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("%s: %s; exiting\n", strerror(errno), 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 ||
|
||||
(E->d_type == DT_UNKNOWN && IsFile(Path)))
|
||||
V->push_back(Path);
|
||||
else if ((E->d_type == DT_DIR ||
|
||||
(E->d_type == DT_UNKNOWN && IsDirectory(Path))) &&
|
||||
*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());
|
||||
}
|
||||
|
||||
void DiscardOutput(int Fd) {
|
||||
FILE* Temp = fopen("/dev/null", "w");
|
||||
if (!Temp)
|
||||
return;
|
||||
dup2(fileno(Temp), Fd);
|
||||
fclose(Temp);
|
||||
}
|
||||
|
||||
intptr_t GetHandleFromFd(int fd) {
|
||||
return static_cast<intptr_t>(fd);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::string TmpDir() {
|
||||
if (auto Env = getenv("TMPDIR"))
|
||||
return Env;
|
||||
return "/tmp";
|
||||
}
|
||||
|
||||
bool IsInterestingCoverageFile(const std::string &FileName) {
|
||||
if (FileName.find("compiler-rt/lib/") != std::string::npos)
|
||||
return false; // sanitizer internal.
|
||||
if (FileName.find("/usr/lib/") != std::string::npos)
|
||||
return false;
|
||||
if (FileName.find("/usr/include/") != std::string::npos)
|
||||
return false;
|
||||
if (FileName == "<null>")
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void RawPrint(const char *Str) {
|
||||
write(2, Str, strlen(Str));
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_POSIX
|
323
contrib/compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp
Normal file
323
contrib/compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp
Normal file
@ -0,0 +1,323 @@
|
||||
//===- 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,
|
||||
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)
|
||||
{
|
||||
if (GetLastError() == ERROR_FILE_NOT_FOUND)
|
||||
return;
|
||||
Printf("No such directory: %s; exiting\n", Dir.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
void DiscardOutput(int Fd) {
|
||||
FILE* Temp = fopen("nul", "w");
|
||||
if (!Temp)
|
||||
return;
|
||||
_dup2(_fileno(Temp), Fd);
|
||||
fclose(Temp);
|
||||
}
|
||||
|
||||
intptr_t GetHandleFromFd(int fd) {
|
||||
return _get_osfhandle(fd);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string TmpDir() {
|
||||
std::string Tmp;
|
||||
Tmp.resize(MAX_PATH + 1);
|
||||
DWORD Size = GetTempPathA(Tmp.size(), &Tmp[0]);
|
||||
if (Size == 0) {
|
||||
Printf("Couldn't get Tmp path.\n");
|
||||
exit(1);
|
||||
}
|
||||
Tmp.resize(Size);
|
||||
return Tmp;
|
||||
}
|
||||
|
||||
bool IsInterestingCoverageFile(const std::string &FileName) {
|
||||
if (FileName.find("Program Files") != std::string::npos)
|
||||
return false;
|
||||
if (FileName.find("compiler-rt\\lib\\") != std::string::npos)
|
||||
return false; // sanitizer internal.
|
||||
if (FileName == "<null>")
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RawPrint(const char *Str) {
|
||||
// Not tested, may or may not work. Fix if needed.
|
||||
Printf("%s", Str);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
71
contrib/compiler-rt/lib/fuzzer/FuzzerInterface.h
Normal file
71
contrib/compiler-rt/lib/fuzzer/FuzzerInterface.h
Normal file
@ -0,0 +1,71 @@
|
||||
//===- 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.
|
||||
__attribute__((visibility("default"))) 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.
|
||||
__attribute__((visibility("default"))) 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.
|
||||
__attribute__((visibility("default"))) 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.
|
||||
__attribute__((visibility("default"))) 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 LLVMFuzzerCustomMutator.
|
||||
// Mutates raw data in [Data, Data+Size) inplace.
|
||||
// Returns the new size, which is not greater than MaxSize.
|
||||
__attribute__((visibility("default"))) size_t
|
||||
LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // LLVM_FUZZER_INTERFACE_H
|
179
contrib/compiler-rt/lib/fuzzer/FuzzerInternal.h
Normal file
179
contrib/compiler-rt/lib/fuzzer/FuzzerInternal.h
Normal file
@ -0,0 +1,179 @@
|
||||
//===- 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 "FuzzerDataFlowTrace.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:
|
||||
|
||||
Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
|
||||
FuzzingOptions Options);
|
||||
~Fuzzer();
|
||||
void Loop(const Vector<std::string> &CorpusDirs);
|
||||
void ReadAndExecuteSeedCorpora(const Vector<std::string> &CorpusDirs);
|
||||
void MinimizeCrashLoop(const Unit &U);
|
||||
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 StaticExitCallback();
|
||||
static void StaticInterruptCallback();
|
||||
static void StaticFileSizeExceedCallback();
|
||||
static void StaticGracefulExitCallback();
|
||||
|
||||
void ExecuteCallback(const uint8_t *Data, size_t Size);
|
||||
void CheckForUnstableCounters(const uint8_t *Data, size_t Size);
|
||||
bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false,
|
||||
InputInfo *II = nullptr, bool *FoundUniqFeatures = nullptr);
|
||||
|
||||
// Merge Corpora[1:] into Corpora[0].
|
||||
void Merge(const Vector<std::string> &Corpora);
|
||||
void CrashResistantMerge(const Vector<std::string> &Args,
|
||||
const Vector<std::string> &Corpora,
|
||||
const char *CoverageSummaryInputPathOrNull,
|
||||
const char *CoverageSummaryOutputPathOrNull,
|
||||
const char *MergeControlFilePathOrNull);
|
||||
void CrashResistantMergeInternalStep(const std::string &ControlFilePath);
|
||||
MutationDispatcher &GetMD() { return MD; }
|
||||
void PrintFinalStats();
|
||||
void SetMaxInputLen(size_t MaxInputLen);
|
||||
void SetMaxMutationLen(size_t MaxMutationLen);
|
||||
void RssLimitCallback();
|
||||
|
||||
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);
|
||||
void AnnounceOutput(const uint8_t *Data, size_t Size);
|
||||
|
||||
private:
|
||||
void AlarmCallback();
|
||||
void CrashCallback();
|
||||
void ExitCallback();
|
||||
void MaybeExitGracefully();
|
||||
void CrashOnOverwrittenData();
|
||||
void InterruptCallback();
|
||||
void MutateAndTestOne();
|
||||
void PurgeAllocator();
|
||||
void ReportNewCoverage(InputInfo *II, const Unit &U);
|
||||
void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t 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, const char *Text);
|
||||
void CheckExitOnSrcPosOrItem();
|
||||
|
||||
static void StaticDeathCallback();
|
||||
void DumpCurrentUnit(const char *Prefix);
|
||||
void DeathCallback();
|
||||
|
||||
void AllocateCurrentUnitData();
|
||||
uint8_t *CurrentUnitData = nullptr;
|
||||
std::atomic<size_t> CurrentUnitSize;
|
||||
uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit.
|
||||
|
||||
bool GracefulExitRequested = false;
|
||||
|
||||
size_t TotalNumberOfRuns = 0;
|
||||
size_t NumberOfNewUnitsAdded = 0;
|
||||
|
||||
size_t LastCorpusUpdateRun = 0;
|
||||
|
||||
bool HasMoreMallocsThanFrees = false;
|
||||
size_t NumberOfLeakDetectionAttempts = 0;
|
||||
|
||||
system_clock::time_point LastAllocatorPurgeAttemptTime = system_clock::now();
|
||||
|
||||
UserCallback CB;
|
||||
InputCorpus &Corpus;
|
||||
MutationDispatcher &MD;
|
||||
FuzzingOptions Options;
|
||||
DataFlowTrace DFT;
|
||||
|
||||
system_clock::time_point ProcessStartTime = system_clock::now();
|
||||
system_clock::time_point UnitStartTime, UnitStopTime;
|
||||
long TimeOfLongestUnitInSeconds = 0;
|
||||
long EpochOfLastReadOfOutputCorpus = 0;
|
||||
|
||||
size_t MaxInputLen = 0;
|
||||
size_t MaxMutationLen = 0;
|
||||
size_t TmpMaxMutationLen = 0;
|
||||
|
||||
Vector<uint32_t> UniqFeatureSetTmp;
|
||||
|
||||
// Need to know our own thread.
|
||||
static thread_local bool IsMyThread;
|
||||
};
|
||||
|
||||
struct ScopedEnableMsanInterceptorChecks {
|
||||
ScopedEnableMsanInterceptorChecks() {
|
||||
if (EF->__msan_scoped_enable_interceptor_checks)
|
||||
EF->__msan_scoped_enable_interceptor_checks();
|
||||
}
|
||||
~ScopedEnableMsanInterceptorChecks() {
|
||||
if (EF->__msan_scoped_disable_interceptor_checks)
|
||||
EF->__msan_scoped_disable_interceptor_checks();
|
||||
}
|
||||
};
|
||||
|
||||
struct ScopedDisableMsanInterceptorChecks {
|
||||
ScopedDisableMsanInterceptorChecks() {
|
||||
if (EF->__msan_scoped_disable_interceptor_checks)
|
||||
EF->__msan_scoped_disable_interceptor_checks();
|
||||
}
|
||||
~ScopedDisableMsanInterceptorChecks() {
|
||||
if (EF->__msan_scoped_enable_interceptor_checks)
|
||||
EF->__msan_scoped_enable_interceptor_checks();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_INTERNAL_H
|
910
contrib/compiler-rt/lib/fuzzer/FuzzerLoop.cpp
Normal file
910
contrib/compiler-rt/lib/fuzzer/FuzzerLoop.cpp
Normal file
@ -0,0 +1,910 @@
|
||||
//===- 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 "FuzzerIO.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "FuzzerShmem.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#if defined(__has_include)
|
||||
#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;
|
||||
|
||||
SharedMemoryRegion SMR;
|
||||
|
||||
bool RunningUserCallback = false;
|
||||
|
||||
// Only one Fuzzer per process.
|
||||
static Fuzzer *F;
|
||||
|
||||
// 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;
|
||||
|
||||
std::recursive_mutex TraceMutex;
|
||||
bool TraceDisabled = false;
|
||||
};
|
||||
|
||||
static MallocFreeTracer AllocTracer;
|
||||
|
||||
// Locks printing and avoids nested hooks triggered from mallocs/frees in
|
||||
// sanitizer.
|
||||
class TraceLock {
|
||||
public:
|
||||
TraceLock() : Lock(AllocTracer.TraceMutex) {
|
||||
AllocTracer.TraceDisabled = !AllocTracer.TraceDisabled;
|
||||
}
|
||||
~TraceLock() { AllocTracer.TraceDisabled = !AllocTracer.TraceDisabled; }
|
||||
|
||||
bool IsDisabled() const {
|
||||
// This is already inverted value.
|
||||
return !AllocTracer.TraceDisabled;
|
||||
}
|
||||
|
||||
private:
|
||||
std::lock_guard<std::recursive_mutex> Lock;
|
||||
};
|
||||
|
||||
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) {
|
||||
TraceLock Lock;
|
||||
if (Lock.IsDisabled())
|
||||
return;
|
||||
Printf("MALLOC[%zd] %p %zd\n", N, ptr, size);
|
||||
if (TraceLevel >= 2 && EF)
|
||||
PrintStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void FreeHook(const volatile void *ptr) {
|
||||
size_t N = AllocTracer.Frees++;
|
||||
if (int TraceLevel = AllocTracer.TraceLevel) {
|
||||
TraceLock Lock;
|
||||
if (Lock.IsDisabled())
|
||||
return;
|
||||
Printf("FREE[%zd] %p\n", N, ptr);
|
||||
if (TraceLevel >= 2 && EF)
|
||||
PrintStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Crash on a single malloc that exceeds the rss limit.
|
||||
void Fuzzer::HandleMalloc(size_t Size) {
|
||||
if (!Options.MallocLimitMb || (Size >> 20) < (size_t)Options.MallocLimitMb)
|
||||
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");
|
||||
PrintStackTrace();
|
||||
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) {
|
||||
if (EF->__sanitizer_set_death_callback)
|
||||
EF->__sanitizer_set_death_callback(StaticDeathCallback);
|
||||
assert(!F);
|
||||
F = this;
|
||||
TPC.ResetMaps();
|
||||
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.SetUseValueProfileMask(Options.UseValueProfile);
|
||||
|
||||
if (Options.Verbosity)
|
||||
TPC.PrintModuleInfo();
|
||||
if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec)
|
||||
EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus);
|
||||
MaxInputLen = MaxMutationLen = Options.MaxLen;
|
||||
TmpMaxMutationLen = Max(size_t(4), Corpus.MaxInputSize());
|
||||
AllocateCurrentUnitData();
|
||||
CurrentUnitSize = 0;
|
||||
memset(BaseSha1, 0, sizeof(BaseSha1));
|
||||
TPC.SetFocusFunction(Options.FocusFunction);
|
||||
DFT.Init(Options.DataFlowTrace, Options.FocusFunction);
|
||||
}
|
||||
|
||||
Fuzzer::~Fuzzer() {}
|
||||
|
||||
void Fuzzer::AllocateCurrentUnitData() {
|
||||
if (CurrentUnitData || MaxInputLen == 0)
|
||||
return;
|
||||
CurrentUnitData = new uint8_t[MaxInputLen];
|
||||
}
|
||||
|
||||
void Fuzzer::StaticDeathCallback() {
|
||||
assert(F);
|
||||
F->DeathCallback();
|
||||
}
|
||||
|
||||
void Fuzzer::DumpCurrentUnit(const char *Prefix) {
|
||||
if (!CurrentUnitData)
|
||||
return; // Happens when running individual inputs.
|
||||
ScopedDisableMsanInterceptorChecks S;
|
||||
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::StaticExitCallback() {
|
||||
assert(F);
|
||||
F->ExitCallback();
|
||||
}
|
||||
|
||||
void Fuzzer::StaticInterruptCallback() {
|
||||
assert(F);
|
||||
F->InterruptCallback();
|
||||
}
|
||||
|
||||
void Fuzzer::StaticGracefulExitCallback() {
|
||||
assert(F);
|
||||
F->GracefulExitRequested = true;
|
||||
Printf("INFO: signal received, trying to exit gracefully\n");
|
||||
}
|
||||
|
||||
void Fuzzer::StaticFileSizeExceedCallback() {
|
||||
Printf("==%lu== ERROR: libFuzzer: file size exceeded\n", GetPid());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void Fuzzer::CrashCallback() {
|
||||
if (EF->__sanitizer_acquire_crash_state)
|
||||
EF->__sanitizer_acquire_crash_state();
|
||||
Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid());
|
||||
PrintStackTrace();
|
||||
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); // Stop right now.
|
||||
}
|
||||
|
||||
void Fuzzer::ExitCallback() {
|
||||
if (!RunningUserCallback)
|
||||
return; // This exit did not come from the user callback
|
||||
if (EF->__sanitizer_acquire_crash_state &&
|
||||
!EF->__sanitizer_acquire_crash_state())
|
||||
return;
|
||||
Printf("==%lu== ERROR: libFuzzer: fuzz target exited\n", GetPid());
|
||||
PrintStackTrace();
|
||||
Printf("SUMMARY: libFuzzer: fuzz target exited\n");
|
||||
DumpCurrentUnit("crash-");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.ErrorExitCode);
|
||||
}
|
||||
|
||||
void Fuzzer::MaybeExitGracefully() {
|
||||
if (!GracefulExitRequested) return;
|
||||
Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid());
|
||||
PrintFinalStats();
|
||||
_Exit(0);
|
||||
}
|
||||
|
||||
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);
|
||||
// In Windows Alarm callback is executed by a different thread.
|
||||
#if !LIBFUZZER_WINDOWS
|
||||
if (!InFuzzingThread())
|
||||
return;
|
||||
#endif
|
||||
if (!RunningUserCallback)
|
||||
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) {
|
||||
if (EF->__sanitizer_acquire_crash_state &&
|
||||
!EF->__sanitizer_acquire_crash_state())
|
||||
return;
|
||||
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);
|
||||
PrintStackTrace();
|
||||
Printf("SUMMARY: libFuzzer: timeout\n");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.TimeoutExitCode); // Stop right now.
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::RssLimitCallback() {
|
||||
if (EF->__sanitizer_acquire_crash_state &&
|
||||
!EF->__sanitizer_acquire_crash_state())
|
||||
return;
|
||||
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");
|
||||
PrintMemoryProfile();
|
||||
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.Verbosity)
|
||||
return;
|
||||
Printf("#%zd\t%s", TotalNumberOfRuns, Where);
|
||||
if (size_t N = TPC.GetTotalPCCoverage())
|
||||
Printf(" cov: %zd", N);
|
||||
if (size_t N = Corpus.NumFeatures())
|
||||
Printf(" ft: %zd", N);
|
||||
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 (size_t FF = Corpus.NumInputsThatTouchFocusFunction())
|
||||
Printf(" focus: %zd", FF);
|
||||
}
|
||||
if (TmpMaxMutationLen)
|
||||
Printf(" lim: %zd", TmpMaxMutationLen);
|
||||
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.PrintUnstableStats)
|
||||
TPC.PrintUnstableStats();
|
||||
if (Options.DumpCoverage)
|
||||
TPC.DumpCoverage();
|
||||
if (Options.PrintCorpusStats)
|
||||
Corpus.PrintStats();
|
||||
if (Options.PrintMutationStats) MD.PrintMutationStats();
|
||||
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; "
|
||||
"libFuzzer will not generate inputs larger than %zd bytes\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 Set<uintptr_t>;
|
||||
auto HandlePC = [&](uintptr_t PC) {
|
||||
if (!PCsSet->insert(PC).second)
|
||||
return;
|
||||
std::string Descr = DescribePC("%F %L", PC + 1);
|
||||
if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) {
|
||||
Printf("INFO: found line matching '%s', exiting.\n",
|
||||
Options.ExitOnSrcPos.c_str());
|
||||
_Exit(0);
|
||||
}
|
||||
};
|
||||
TPC.ForEachObservedPC(HandlePC);
|
||||
}
|
||||
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;
|
||||
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 (RunOne(U.data(), U.size())) {
|
||||
CheckExitOnSrcPosOrItem();
|
||||
Reloaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Reloaded)
|
||||
PrintStats("RELOAD");
|
||||
}
|
||||
|
||||
void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) {
|
||||
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-");
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::CheckForUnstableCounters(const uint8_t *Data, size_t Size) {
|
||||
auto CBSetupAndRun = [&]() {
|
||||
ScopedEnableMsanInterceptorChecks S;
|
||||
UnitStartTime = system_clock::now();
|
||||
TPC.ResetMaps();
|
||||
RunningUserCallback = true;
|
||||
CB(Data, Size);
|
||||
RunningUserCallback = false;
|
||||
UnitStopTime = system_clock::now();
|
||||
};
|
||||
|
||||
// Copy original run counters into our unstable counters
|
||||
TPC.InitializeUnstableCounters();
|
||||
|
||||
// First Rerun
|
||||
CBSetupAndRun();
|
||||
TPC.UpdateUnstableCounters(Options.HandleUnstable);
|
||||
|
||||
// Second Rerun
|
||||
CBSetupAndRun();
|
||||
TPC.UpdateUnstableCounters(Options.HandleUnstable);
|
||||
|
||||
// Move minimum hit counts back to ModuleInline8bitCounters
|
||||
if (Options.HandleUnstable == TracePC::MinUnstable ||
|
||||
Options.HandleUnstable == TracePC::ZeroUnstable)
|
||||
TPC.ApplyUnstableCounters();
|
||||
}
|
||||
|
||||
bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
|
||||
InputInfo *II, bool *FoundUniqFeatures) {
|
||||
if (!Size)
|
||||
return false;
|
||||
|
||||
ExecuteCallback(Data, Size);
|
||||
|
||||
UniqFeatureSetTmp.clear();
|
||||
size_t FoundUniqFeaturesOfII = 0;
|
||||
size_t NumUpdatesBefore = Corpus.NumFeatureUpdates();
|
||||
bool NewFeaturesUnstable = false;
|
||||
|
||||
if (Options.HandleUnstable || Options.PrintUnstableStats) {
|
||||
TPC.CollectFeatures([&](size_t Feature) {
|
||||
if (Corpus.IsFeatureNew(Feature, Size, Options.Shrink))
|
||||
NewFeaturesUnstable = true;
|
||||
});
|
||||
if (NewFeaturesUnstable)
|
||||
CheckForUnstableCounters(Data, Size);
|
||||
}
|
||||
|
||||
TPC.CollectFeatures([&](size_t Feature) {
|
||||
if (Corpus.AddFeature(Feature, Size, Options.Shrink))
|
||||
UniqFeatureSetTmp.push_back(Feature);
|
||||
if (Options.ReduceInputs && II)
|
||||
if (std::binary_search(II->UniqFeatureSet.begin(),
|
||||
II->UniqFeatureSet.end(), Feature))
|
||||
FoundUniqFeaturesOfII++;
|
||||
});
|
||||
|
||||
if (FoundUniqFeatures)
|
||||
*FoundUniqFeatures = FoundUniqFeaturesOfII;
|
||||
PrintPulseAndReportSlowInput(Data, Size);
|
||||
size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore;
|
||||
|
||||
if (NumNewFeatures) {
|
||||
TPC.UpdateObservedPCs();
|
||||
Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile,
|
||||
TPC.ObservedFocusFunction(), UniqFeatureSetTmp, DFT, II);
|
||||
return true;
|
||||
}
|
||||
if (II && FoundUniqFeaturesOfII &&
|
||||
II->DataFlowTraceForFocusFunction.empty() &&
|
||||
FoundUniqFeaturesOfII == II->UniqFeatureSet.size() &&
|
||||
II->U.size() > Size) {
|
||||
Corpus.Replace(II, {Data, Data + Size});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const {
|
||||
assert(InFuzzingThread());
|
||||
*Data = CurrentUnitData;
|
||||
return CurrentUnitSize;
|
||||
}
|
||||
|
||||
void Fuzzer::CrashOnOverwrittenData() {
|
||||
Printf("==%d== ERROR: libFuzzer: fuzz target overwrites it's const input\n",
|
||||
GetPid());
|
||||
DumpCurrentUnit("crash-");
|
||||
Printf("SUMMARY: libFuzzer: out-of-memory\n");
|
||||
_Exit(Options.ErrorExitCode); // Stop right now.
|
||||
}
|
||||
|
||||
// Compare two arrays, but not all bytes if the arrays are large.
|
||||
static bool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) {
|
||||
const size_t Limit = 64;
|
||||
if (Size <= 64)
|
||||
return !memcmp(A, B, Size);
|
||||
// Compare first and last Limit/2 bytes.
|
||||
return !memcmp(A, B, Limit / 2) &&
|
||||
!memcmp(A + Size - Limit / 2, B + Size - Limit / 2, Limit / 2);
|
||||
}
|
||||
|
||||
void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
|
||||
TPC.RecordInitialStack();
|
||||
TotalNumberOfRuns++;
|
||||
assert(InFuzzingThread());
|
||||
if (SMR.IsClient())
|
||||
SMR.WriteByteArray(Data, Size);
|
||||
// We copy the contents of Unit into a separate heap buffer
|
||||
// so that we reliably find buffer overflows in it.
|
||||
uint8_t *DataCopy = new uint8_t[Size];
|
||||
memcpy(DataCopy, Data, Size);
|
||||
if (EF->__msan_unpoison)
|
||||
EF->__msan_unpoison(DataCopy, Size);
|
||||
if (CurrentUnitData && CurrentUnitData != Data)
|
||||
memcpy(CurrentUnitData, Data, Size);
|
||||
CurrentUnitSize = Size;
|
||||
{
|
||||
ScopedEnableMsanInterceptorChecks S;
|
||||
AllocTracer.Start(Options.TraceMalloc);
|
||||
UnitStartTime = system_clock::now();
|
||||
TPC.ResetMaps();
|
||||
RunningUserCallback = true;
|
||||
int Res = CB(DataCopy, Size);
|
||||
RunningUserCallback = false;
|
||||
UnitStopTime = system_clock::now();
|
||||
(void)Res;
|
||||
assert(Res == 0);
|
||||
HasMoreMallocsThanFrees = AllocTracer.Stop();
|
||||
}
|
||||
if (!LooseMemeq(DataCopy, Data, Size))
|
||||
CrashOnOverwrittenData();
|
||||
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 %zd bytes to %s\n", U.size(), 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, const char *Text) {
|
||||
if (!Options.PrintNEW)
|
||||
return;
|
||||
PrintStats(Text, "");
|
||||
if (Options.Verbosity) {
|
||||
Printf(" L: %zd/%zd ", U.size(), Corpus.MaxInputSize());
|
||||
MD.PrintMutationSequence();
|
||||
Printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) {
|
||||
II->NumSuccessfullMutations++;
|
||||
MD.RecordSuccessfulMutationSequence();
|
||||
PrintStatusForNewUnit(U, II->Reduced ? "REDUCE" : "NEW ");
|
||||
WriteToOutputCorpus(U);
|
||||
NumberOfNewUnitsAdded++;
|
||||
CheckExitOnSrcPosOrItem(); // Check only after the unit is saved to corpus.
|
||||
LastCorpusUpdateRun = TotalNumberOfRuns;
|
||||
}
|
||||
|
||||
// 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 (!DuringInitialCorpusExecution &&
|
||||
TotalNumberOfRuns >= Options.MaxNumberOfRuns)
|
||||
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);
|
||||
|
||||
size_t CurrentMaxMutationLen =
|
||||
Min(MaxMutationLen, Max(U.size(), TmpMaxMutationLen));
|
||||
assert(CurrentMaxMutationLen > 0);
|
||||
|
||||
for (int i = 0; i < Options.MutateDepth; i++) {
|
||||
if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
|
||||
break;
|
||||
MaybeExitGracefully();
|
||||
size_t NewSize = 0;
|
||||
if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() &&
|
||||
Size <= CurrentMaxMutationLen)
|
||||
NewSize = MD.MutateWithMask(CurrentUnitData, Size, Size,
|
||||
II.DataFlowTraceForFocusFunction);
|
||||
else
|
||||
NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen);
|
||||
assert(NewSize > 0 && "Mutator returned empty unit");
|
||||
assert(NewSize <= CurrentMaxMutationLen && "Mutator return oversized unit");
|
||||
Size = NewSize;
|
||||
II.NumExecutedMutations++;
|
||||
|
||||
bool FoundUniqFeatures = false;
|
||||
bool NewCov = RunOne(CurrentUnitData, Size, /*MayDeleteFile=*/true, &II,
|
||||
&FoundUniqFeatures);
|
||||
TryDetectingAMemoryLeak(CurrentUnitData, Size,
|
||||
/*DuringInitialCorpusExecution*/ false);
|
||||
if (NewCov) {
|
||||
ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size});
|
||||
break; // We will mutate this input more in the next rounds.
|
||||
}
|
||||
if (Options.ReduceDepth && !FoundUniqFeatures)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::PurgeAllocator() {
|
||||
if (Options.PurgeAllocatorIntervalSec < 0 || !EF->__sanitizer_purge_allocator)
|
||||
return;
|
||||
if (duration_cast<seconds>(system_clock::now() -
|
||||
LastAllocatorPurgeAttemptTime)
|
||||
.count() < Options.PurgeAllocatorIntervalSec)
|
||||
return;
|
||||
|
||||
if (Options.RssLimitMb <= 0 ||
|
||||
GetPeakRSSMb() > static_cast<size_t>(Options.RssLimitMb) / 2)
|
||||
EF->__sanitizer_purge_allocator();
|
||||
|
||||
LastAllocatorPurgeAttemptTime = system_clock::now();
|
||||
}
|
||||
|
||||
void Fuzzer::ReadAndExecuteSeedCorpora(const Vector<std::string> &CorpusDirs) {
|
||||
const size_t kMaxSaneLen = 1 << 20;
|
||||
const size_t kMinDefaultLen = 4096;
|
||||
Vector<SizedFile> SizedFiles;
|
||||
size_t MaxSize = 0;
|
||||
size_t MinSize = -1;
|
||||
size_t TotalSize = 0;
|
||||
size_t LastNumFiles = 0;
|
||||
for (auto &Dir : CorpusDirs) {
|
||||
GetSizedFilesFromDir(Dir, &SizedFiles);
|
||||
Printf("INFO: % 8zd files found in %s\n", SizedFiles.size() - LastNumFiles,
|
||||
Dir.c_str());
|
||||
LastNumFiles = SizedFiles.size();
|
||||
}
|
||||
for (auto &File : SizedFiles) {
|
||||
MaxSize = Max(File.Size, MaxSize);
|
||||
MinSize = Min(File.Size, MinSize);
|
||||
TotalSize += File.Size;
|
||||
}
|
||||
if (Options.MaxLen == 0)
|
||||
SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxSize), kMaxSaneLen));
|
||||
assert(MaxInputLen > 0);
|
||||
|
||||
// Test the callback with empty input and never try it again.
|
||||
uint8_t dummy = 0;
|
||||
ExecuteCallback(&dummy, 0);
|
||||
|
||||
if (SizedFiles.empty()) {
|
||||
Printf("INFO: A corpus is not provided, starting from an empty corpus\n");
|
||||
Unit U({'\n'}); // Valid ASCII input.
|
||||
RunOne(U.data(), U.size());
|
||||
} else {
|
||||
Printf("INFO: seed corpus: files: %zd min: %zdb max: %zdb total: %zdb"
|
||||
" rss: %zdMb\n",
|
||||
SizedFiles.size(), MinSize, MaxSize, TotalSize, GetPeakRSSMb());
|
||||
if (Options.ShuffleAtStartUp)
|
||||
std::shuffle(SizedFiles.begin(), SizedFiles.end(), MD.GetRand());
|
||||
|
||||
if (Options.PreferSmall) {
|
||||
std::stable_sort(SizedFiles.begin(), SizedFiles.end());
|
||||
assert(SizedFiles.front().Size <= SizedFiles.back().Size);
|
||||
}
|
||||
|
||||
// Load and execute inputs one by one.
|
||||
for (auto &SF : SizedFiles) {
|
||||
auto U = FileToVector(SF.File, MaxInputLen, /*ExitOnError=*/false);
|
||||
assert(U.size() <= MaxInputLen);
|
||||
RunOne(U.data(), U.size());
|
||||
CheckExitOnSrcPosOrItem();
|
||||
TryDetectingAMemoryLeak(U.data(), U.size(),
|
||||
/*DuringInitialCorpusExecution*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
PrintStats("INITED");
|
||||
if (!Options.FocusFunction.empty())
|
||||
Printf("INFO: %zd/%zd inputs touch the focus function\n",
|
||||
Corpus.NumInputsThatTouchFocusFunction(), Corpus.size());
|
||||
if (!Options.DataFlowTrace.empty())
|
||||
Printf("INFO: %zd/%zd inputs have the Data Flow Trace\n",
|
||||
Corpus.NumInputsWithDataFlowTrace(), Corpus.size());
|
||||
|
||||
if (Corpus.empty() && Options.MaxNumberOfRuns) {
|
||||
Printf("ERROR: no interesting inputs were found. "
|
||||
"Is the code instrumented for coverage? Exiting.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::Loop(const Vector<std::string> &CorpusDirs) {
|
||||
ReadAndExecuteSeedCorpora(CorpusDirs);
|
||||
DFT.Clear(); // No need for DFT any more.
|
||||
TPC.SetPrintNewPCs(Options.PrintNewCovPcs);
|
||||
TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs);
|
||||
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;
|
||||
|
||||
// Update TmpMaxMutationLen
|
||||
if (Options.LenControl) {
|
||||
if (TmpMaxMutationLen < MaxMutationLen &&
|
||||
TotalNumberOfRuns - LastCorpusUpdateRun >
|
||||
Options.LenControl * Log(TmpMaxMutationLen)) {
|
||||
TmpMaxMutationLen =
|
||||
Min(MaxMutationLen, TmpMaxMutationLen + Log(TmpMaxMutationLen));
|
||||
LastCorpusUpdateRun = TotalNumberOfRuns;
|
||||
}
|
||||
} else {
|
||||
TmpMaxMutationLen = MaxMutationLen;
|
||||
}
|
||||
|
||||
// Perform several mutations and runs.
|
||||
MutateAndTestOne();
|
||||
|
||||
PurgeAllocator();
|
||||
}
|
||||
|
||||
PrintStats("DONE ", "\n");
|
||||
MD.PrintRecommendedDictionary();
|
||||
}
|
||||
|
||||
void Fuzzer::MinimizeCrashLoop(const Unit &U) {
|
||||
if (U.size() <= 1)
|
||||
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);
|
||||
ExecuteCallback(CurrentUnitData, NewSize);
|
||||
PrintPulseAndReportSlowInput(CurrentUnitData, NewSize);
|
||||
TryDetectingAMemoryLeak(CurrentUnitData, NewSize,
|
||||
/*DuringInitialCorpusExecution*/ false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::AnnounceOutput(const uint8_t *Data, size_t Size) {
|
||||
if (SMR.IsServer()) {
|
||||
SMR.WriteByteArray(Data, Size);
|
||||
} else if (SMR.IsClient()) {
|
||||
SMR.PostClient();
|
||||
SMR.WaitServer();
|
||||
size_t OtherSize = SMR.ReadByteArraySize();
|
||||
uint8_t *OtherData = SMR.GetByteArray();
|
||||
if (Size != OtherSize || memcmp(Data, OtherData, Size) != 0) {
|
||||
size_t i = 0;
|
||||
for (i = 0; i < Min(Size, OtherSize); i++)
|
||||
if (Data[i] != OtherData[i])
|
||||
break;
|
||||
Printf("==%lu== ERROR: libFuzzer: equivalence-mismatch. Sizes: %zd %zd; "
|
||||
"offset %zd\n",
|
||||
GetPid(), Size, OtherSize, i);
|
||||
DumpCurrentUnit("mismatch-");
|
||||
Printf("SUMMARY: libFuzzer: equivalence-mismatch\n");
|
||||
PrintFinalStats();
|
||||
_Exit(Options.ErrorExitCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((visibility("default"))) size_t
|
||||
LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
assert(fuzzer::F);
|
||||
return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
// Experimental
|
||||
__attribute__((visibility("default"))) void
|
||||
LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size) {
|
||||
assert(fuzzer::F);
|
||||
fuzzer::F->AnnounceOutput(Data, Size);
|
||||
}
|
||||
} // extern "C"
|
21
contrib/compiler-rt/lib/fuzzer/FuzzerMain.cpp
Normal file
21
contrib/compiler-rt/lib/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"
|
||||
|
||||
__attribute__((visibility("default"))) int main(int argc, char **argv) {
|
||||
return fuzzer::FuzzerDriver(&argc, &argv, LLVMFuzzerTestOneInput);
|
||||
}
|
390
contrib/compiler-rt/lib/fuzzer/FuzzerMerge.cpp
Normal file
390
contrib/compiler-rt/lib/fuzzer/FuzzerMerge.cpp
Normal file
@ -0,0 +1,390 @@
|
||||
//===- 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 "FuzzerCommand.h"
|
||||
#include "FuzzerMerge.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include "FuzzerUtil.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#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;
|
||||
Vector<uint32_t> TmpFeatures;
|
||||
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_ID COV1 COV2 COV3 ...
|
||||
size_t CurrentFileIdx = N;
|
||||
if (CurrentFileIdx != LastSeenStartMarker)
|
||||
return false;
|
||||
LastSeenStartMarker = kInvalidStartMarker;
|
||||
if (ParseCoverage) {
|
||||
TmpFeatures.clear(); // use a vector from outer scope to avoid resizes.
|
||||
while (ISS1 >> std::hex >> N)
|
||||
TmpFeatures.push_back(N);
|
||||
std::sort(TmpFeatures.begin(), TmpFeatures.end());
|
||||
Files[CurrentFileIdx].Features = TmpFeatures;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (LastSeenStartMarker != kInvalidStartMarker)
|
||||
LastFailure = Files[LastSeenStartMarker].Name;
|
||||
|
||||
FirstNotProcessedFile = ExpectedStartMarker;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Merger::ApproximateMemoryConsumption() const {
|
||||
size_t Res = 0;
|
||||
for (const auto &F: Files)
|
||||
Res += sizeof(F) + F.Features.size() * sizeof(F.Features[0]);
|
||||
return Res;
|
||||
}
|
||||
|
||||
// Decides which files need to be merged (add thost to NewFiles).
|
||||
// Returns the number of new features added.
|
||||
size_t Merger::Merge(const Set<uint32_t> &InitialFeatures,
|
||||
Vector<std::string> *NewFiles) {
|
||||
NewFiles->clear();
|
||||
assert(NumFilesInFirstCorpus <= Files.size());
|
||||
Set<uint32_t> AllFeatures(InitialFeatures);
|
||||
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
|
||||
void Merger::PrintSummary(std::ostream &OS) {
|
||||
for (auto &File : Files) {
|
||||
OS << std::hex;
|
||||
OS << File.Name << " size: " << File.Size << " features: ";
|
||||
for (auto Feature : File.Features)
|
||||
OS << " " << Feature;
|
||||
OS << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
Set<uint32_t> Merger::AllFeatures() const {
|
||||
Set<uint32_t> S;
|
||||
for (auto &File : Files)
|
||||
S.insert(File.Features.begin(), File.Features.end());
|
||||
return S;
|
||||
}
|
||||
|
||||
Set<uint32_t> Merger::ParseSummary(std::istream &IS) {
|
||||
std::string Line, Tmp;
|
||||
Set<uint32_t> Res;
|
||||
while (std::getline(IS, Line, '\n')) {
|
||||
size_t N;
|
||||
std::istringstream ISS1(Line);
|
||||
ISS1 >> Tmp; // Name
|
||||
ISS1 >> Tmp; // size:
|
||||
assert(Tmp == "size:" && "Corrupt summary file");
|
||||
ISS1 >> std::hex;
|
||||
ISS1 >> N; // File Size
|
||||
ISS1 >> Tmp; // features:
|
||||
assert(Tmp == "features:" && "Corrupt summary file");
|
||||
while (ISS1 >> std::hex >> N)
|
||||
Res.insert(N);
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
// 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);
|
||||
Set<size_t> AllFeatures;
|
||||
for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) {
|
||||
MaybeExitGracefully();
|
||||
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 Command::Execute may crash.
|
||||
// Run.
|
||||
TPC.ResetMaps();
|
||||
ExecuteCallback(U.data(), U.size());
|
||||
// Collect coverage. We are iterating over the files in this order:
|
||||
// * First, files in the initial corpus ordered by size, smallest first.
|
||||
// * Then, all other files, smallest first.
|
||||
// So it makes no sense to record all features for all files, instead we
|
||||
// only record features that were not seen before.
|
||||
Set<size_t> UniqFeatures;
|
||||
TPC.CollectFeatures([&](size_t Feature) {
|
||||
if (AllFeatures.insert(Feature).second)
|
||||
UniqFeatures.insert(Feature);
|
||||
});
|
||||
// Show stats.
|
||||
if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)))
|
||||
PrintStats("pulse ");
|
||||
// Write the post-run marker and the coverage.
|
||||
OF << "DONE " << i;
|
||||
for (size_t F : UniqFeatures)
|
||||
OF << " " << std::hex << F;
|
||||
OF << "\n";
|
||||
OF.flush();
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteNewControlFile(const std::string &CFPath,
|
||||
const Vector<SizedFile> &AllFiles,
|
||||
size_t NumFilesInFirstCorpus) {
|
||||
RemoveFile(CFPath);
|
||||
std::ofstream ControlFile(CFPath);
|
||||
ControlFile << AllFiles.size() << "\n";
|
||||
ControlFile << NumFilesInFirstCorpus << "\n";
|
||||
for (auto &SF: AllFiles)
|
||||
ControlFile << SF.File << "\n";
|
||||
if (!ControlFile) {
|
||||
Printf("MERGE-OUTER: failed to write to the control file: %s\n",
|
||||
CFPath.c_str());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Outer process. Does not call the target code and thus sohuld not fail.
|
||||
void Fuzzer::CrashResistantMerge(const Vector<std::string> &Args,
|
||||
const Vector<std::string> &Corpora,
|
||||
const char *CoverageSummaryInputPathOrNull,
|
||||
const char *CoverageSummaryOutputPathOrNull,
|
||||
const char *MergeControlFilePathOrNull) {
|
||||
if (Corpora.size() <= 1) {
|
||||
Printf("Merge requires two or more corpus dirs\n");
|
||||
return;
|
||||
}
|
||||
auto CFPath =
|
||||
MergeControlFilePathOrNull
|
||||
? MergeControlFilePathOrNull
|
||||
: DirPlusFile(TmpDir(),
|
||||
"libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
|
||||
|
||||
size_t NumAttempts = 0;
|
||||
if (MergeControlFilePathOrNull && FileSize(MergeControlFilePathOrNull)) {
|
||||
Printf("MERGE-OUTER: non-empty control file provided: '%s'\n",
|
||||
MergeControlFilePathOrNull);
|
||||
Merger M;
|
||||
std::ifstream IF(MergeControlFilePathOrNull);
|
||||
if (M.Parse(IF, /*ParseCoverage=*/false)) {
|
||||
Printf("MERGE-OUTER: control file ok, %zd files total,"
|
||||
" first not processed file %zd\n",
|
||||
M.Files.size(), M.FirstNotProcessedFile);
|
||||
if (!M.LastFailure.empty())
|
||||
Printf("MERGE-OUTER: '%s' will be skipped as unlucky "
|
||||
"(merge has stumbled on it the last time)\n",
|
||||
M.LastFailure.c_str());
|
||||
if (M.FirstNotProcessedFile >= M.Files.size()) {
|
||||
Printf("MERGE-OUTER: nothing to do, merge has been completed before\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
NumAttempts = M.Files.size() - M.FirstNotProcessedFile;
|
||||
} else {
|
||||
Printf("MERGE-OUTER: bad control file, will overwrite it\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!NumAttempts) {
|
||||
// The supplied control file is empty or bad, create a fresh one.
|
||||
Vector<SizedFile> AllFiles;
|
||||
GetSizedFilesFromDir(Corpora[0], &AllFiles);
|
||||
size_t NumFilesInFirstCorpus = AllFiles.size();
|
||||
std::sort(AllFiles.begin(), AllFiles.end());
|
||||
for (size_t i = 1; i < Corpora.size(); i++)
|
||||
GetSizedFilesFromDir(Corpora[i], &AllFiles);
|
||||
std::sort(AllFiles.begin() + NumFilesInFirstCorpus, AllFiles.end());
|
||||
Printf("MERGE-OUTER: %zd files, %zd in the initial corpus\n",
|
||||
AllFiles.size(), NumFilesInFirstCorpus);
|
||||
WriteNewControlFile(CFPath, AllFiles, NumFilesInFirstCorpus);
|
||||
NumAttempts = AllFiles.size();
|
||||
}
|
||||
|
||||
// Execute the inner process until it passes.
|
||||
// Every inner process should execute at least one input.
|
||||
Command BaseCmd(Args);
|
||||
BaseCmd.removeFlag("merge");
|
||||
bool Success = false;
|
||||
for (size_t Attempt = 1; Attempt <= NumAttempts; Attempt++) {
|
||||
MaybeExitGracefully();
|
||||
Printf("MERGE-OUTER: attempt %zd\n", Attempt);
|
||||
Command Cmd(BaseCmd);
|
||||
Cmd.addFlag("merge_control_file", CFPath);
|
||||
Cmd.addFlag("merge_inner", "1");
|
||||
auto ExitCode = ExecuteCommand(Cmd);
|
||||
if (!ExitCode) {
|
||||
Printf("MERGE-OUTER: succesfull in %zd attempt(s)\n", Attempt);
|
||||
Success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!Success) {
|
||||
Printf("MERGE-OUTER: zero succesfull attempts, exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
// Read the control file and do the merge.
|
||||
Merger M;
|
||||
std::ifstream IF(CFPath);
|
||||
IF.seekg(0, IF.end);
|
||||
Printf("MERGE-OUTER: the control file has %zd bytes\n", (size_t)IF.tellg());
|
||||
IF.seekg(0, IF.beg);
|
||||
M.ParseOrExit(IF, true);
|
||||
IF.close();
|
||||
Printf("MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n",
|
||||
M.ApproximateMemoryConsumption() >> 20, GetPeakRSSMb());
|
||||
if (CoverageSummaryOutputPathOrNull) {
|
||||
Printf("MERGE-OUTER: writing coverage summary for %zd files to %s\n",
|
||||
M.Files.size(), CoverageSummaryOutputPathOrNull);
|
||||
std::ofstream SummaryOut(CoverageSummaryOutputPathOrNull);
|
||||
M.PrintSummary(SummaryOut);
|
||||
}
|
||||
Vector<std::string> NewFiles;
|
||||
Set<uint32_t> InitialFeatures;
|
||||
if (CoverageSummaryInputPathOrNull) {
|
||||
std::ifstream SummaryIn(CoverageSummaryInputPathOrNull);
|
||||
InitialFeatures = M.ParseSummary(SummaryIn);
|
||||
Printf("MERGE-OUTER: coverage summary loaded from %s, %zd features found\n",
|
||||
CoverageSummaryInputPathOrNull, InitialFeatures.size());
|
||||
}
|
||||
size_t NumNewFeatures = M.Merge(InitialFeatures, &NewFiles);
|
||||
Printf("MERGE-OUTER: %zd new files with %zd new features added\n",
|
||||
NewFiles.size(), NumNewFeatures);
|
||||
for (auto &F: NewFiles)
|
||||
WriteToOutputCorpus(FileToVector(F, MaxInputLen));
|
||||
// We are done, delete the control file if it was a temporary one.
|
||||
if (!MergeControlFilePathOrNull)
|
||||
RemoveFile(CFPath);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
80
contrib/compiler-rt/lib/fuzzer/FuzzerMerge.h
Normal file
80
contrib/compiler-rt/lib/fuzzer/FuzzerMerge.h
Normal file
@ -0,0 +1,80 @@
|
||||
//===- 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 <ostream>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
struct MergeFileInfo {
|
||||
std::string Name;
|
||||
size_t Size = 0;
|
||||
Vector<uint32_t> Features;
|
||||
};
|
||||
|
||||
struct Merger {
|
||||
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);
|
||||
void PrintSummary(std::ostream &OS);
|
||||
Set<uint32_t> ParseSummary(std::istream &IS);
|
||||
size_t Merge(const Set<uint32_t> &InitialFeatures,
|
||||
Vector<std::string> *NewFiles);
|
||||
size_t Merge(Vector<std::string> *NewFiles) {
|
||||
return Merge(Set<uint32_t>{}, NewFiles);
|
||||
}
|
||||
size_t ApproximateMemoryConsumption() const;
|
||||
Set<uint32_t> AllFeatures() const;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_MERGE_H
|
582
contrib/compiler-rt/lib/fuzzer/FuzzerMutate.cpp
Normal file
582
contrib/compiler-rt/lib/fuzzer/FuzzerMutate.cpp
Normal file
@ -0,0 +1,582 @@
|
||||
//===- 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 "FuzzerMutate.h"
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.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", 0, 0},
|
||||
{&MutationDispatcher::Mutate_InsertByte, "InsertByte", 0, 0},
|
||||
{&MutationDispatcher::Mutate_InsertRepeatedBytes,
|
||||
"InsertRepeatedBytes", 0, 0},
|
||||
{&MutationDispatcher::Mutate_ChangeByte, "ChangeByte", 0, 0},
|
||||
{&MutationDispatcher::Mutate_ChangeBit, "ChangeBit", 0, 0},
|
||||
{&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes", 0, 0},
|
||||
{&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt", 0,
|
||||
0},
|
||||
{&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt", 0,
|
||||
0},
|
||||
{&MutationDispatcher::Mutate_CopyPart, "CopyPart", 0, 0},
|
||||
{&MutationDispatcher::Mutate_CrossOver, "CrossOver", 0, 0},
|
||||
{&MutationDispatcher::Mutate_AddWordFromManualDictionary,
|
||||
"ManualDict", 0, 0},
|
||||
{&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
|
||||
"PersAutoDict", 0, 0},
|
||||
});
|
||||
if(Options.UseCmp)
|
||||
DefaultMutators.push_back(
|
||||
{&MutationDispatcher::Mutate_AddWordFromTORC, "CMP", 0, 0});
|
||||
|
||||
if (EF->LLVMFuzzerCustomMutator)
|
||||
Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom", 0, 0});
|
||||
else
|
||||
Mutators = DefaultMutators;
|
||||
|
||||
if (EF->LLVMFuzzerCustomCrossOver)
|
||||
Mutators.push_back(
|
||||
{&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver", 0, 0});
|
||||
}
|
||||
|
||||
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;
|
||||
CustomCrossOverInPlaceHere.resize(MaxSize);
|
||||
auto &U = CustomCrossOverInPlaceHere;
|
||||
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 || Size == 0) return 0;
|
||||
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::shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, Rand);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
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::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.
|
||||
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||
const void *Arg1, const void *Arg2,
|
||||
const void *Arg1Mutation, const void *Arg2Mutation,
|
||||
size_t ArgSize, const uint8_t *Data,
|
||||
size_t Size) {
|
||||
bool HandleFirst = Rand.RandBool();
|
||||
const void *ExistingBytes, *DesiredBytes;
|
||||
Word W;
|
||||
const uint8_t *End = Data + Size;
|
||||
for (int Arg = 0; Arg < 2; Arg++) {
|
||||
ExistingBytes = HandleFirst ? Arg1 : Arg2;
|
||||
DesiredBytes = HandleFirst ? Arg2Mutation : Arg1Mutation;
|
||||
HandleFirst = !HandleFirst;
|
||||
W.Set(reinterpret_cast<const uint8_t*>(DesiredBytes), ArgSize);
|
||||
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 =
|
||||
(const uint8_t *)SearchMemory(Cur, End - Cur, ExistingBytes, ArgSize);
|
||||
if (!Cur) break;
|
||||
Positions[NumPositions++] = Cur - Data;
|
||||
}
|
||||
if (!NumPositions) continue;
|
||||
return DictionaryEntry(W, Positions[Rand(NumPositions)]);
|
||||
}
|
||||
DictionaryEntry DE(W);
|
||||
return DE;
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||
T Arg1, T Arg2, const uint8_t *Data, size_t Size) {
|
||||
if (Rand.RandBool()) Arg1 = Bswap(Arg1);
|
||||
if (Rand.RandBool()) Arg2 = Bswap(Arg2);
|
||||
T Arg1Mutation = Arg1 + Rand(-1, 1);
|
||||
T Arg2Mutation = Arg2 + Rand(-1, 1);
|
||||
return MakeDictionaryEntryFromCMP(&Arg1, &Arg2, &Arg1Mutation, &Arg2Mutation,
|
||||
sizeof(Arg1), Data, Size);
|
||||
}
|
||||
|
||||
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||
const Word &Arg1, const Word &Arg2, const uint8_t *Data, size_t Size) {
|
||||
return MakeDictionaryEntryFromCMP(Arg1.data(), Arg2.data(), Arg1.data(),
|
||||
Arg2.data(), Arg1.size(), Data, Size);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromTORC(
|
||||
uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
Word W;
|
||||
DictionaryEntry DE;
|
||||
switch (Rand(4)) {
|
||||
case 0: {
|
||||
auto X = TPC.TORC8.Get(Rand.Rand());
|
||||
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||
} break;
|
||||
case 1: {
|
||||
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);
|
||||
} break;
|
||||
case 2: {
|
||||
auto X = TPC.TORCW.Get(Rand.Rand());
|
||||
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||
} break;
|
||||
case 3: if (Options.UseMemmem) {
|
||||
auto X = TPC.MMT.Get(Rand.Rand());
|
||||
DE = DictionaryEntry(X);
|
||||
} break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
if (!DE.GetW().size()) return 0;
|
||||
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 || Size == 0) return 0;
|
||||
// If Size == MaxSize, `InsertPartOf(...)` will
|
||||
// fail so there's no point using it in this case.
|
||||
if (Size == MaxSize || 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)
|
||||
NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size());
|
||||
break;
|
||||
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();
|
||||
assert(DE->GetW().size());
|
||||
// Linear search is fine here as this happens seldom.
|
||||
if (!PersistentAutoDictionary.ContainsWord(DE->GetW()))
|
||||
PersistentAutoDictionary.push_back({DE->GetW(), 1});
|
||||
}
|
||||
RecordUsefulMutations();
|
||||
}
|
||||
|
||||
void MutationDispatcher::PrintRecommendedDictionary() {
|
||||
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) {
|
||||
assert(DE.GetW().size());
|
||||
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,
|
||||
Vector<Mutator> &Mutators) {
|
||||
assert(MaxSize > 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);
|
||||
M->TotalCount++;
|
||||
return NewSize;
|
||||
}
|
||||
}
|
||||
*Data = ' ';
|
||||
return 1; // Fallback, should not happen frequently.
|
||||
}
|
||||
|
||||
// Mask represents the set of Data bytes that are worth mutating.
|
||||
size_t MutationDispatcher::MutateWithMask(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize,
|
||||
const Vector<uint8_t> &Mask) {
|
||||
assert(Size <= Mask.size());
|
||||
// * Copy the worthy bytes into a temporary array T
|
||||
// * Mutate T
|
||||
// * Copy T back.
|
||||
// This is totally unoptimized.
|
||||
auto &T = MutateWithMaskTemp;
|
||||
if (T.size() < Size)
|
||||
T.resize(Size);
|
||||
size_t OneBits = 0;
|
||||
for (size_t I = 0; I < Size; I++)
|
||||
if (Mask[I])
|
||||
T[OneBits++] = Data[I];
|
||||
|
||||
assert(!T.empty());
|
||||
size_t NewSize = Mutate(T.data(), OneBits, OneBits);
|
||||
assert(NewSize <= OneBits);
|
||||
(void)NewSize;
|
||||
// Even if NewSize < OneBits we still use all OneBits bytes.
|
||||
for (size_t I = 0, J = 0; I < Size; I++)
|
||||
if (Mask[I])
|
||||
Data[I] = T[J++];
|
||||
return Size;
|
||||
}
|
||||
|
||||
void MutationDispatcher::AddWordToManualDictionary(const Word &W) {
|
||||
ManualDictionary.push_back(
|
||||
{W, std::numeric_limits<size_t>::max()});
|
||||
}
|
||||
|
||||
void MutationDispatcher::RecordUsefulMutations() {
|
||||
for (auto M : CurrentMutatorSequence) M->UsefulCount++;
|
||||
}
|
||||
|
||||
void MutationDispatcher::PrintMutationStats() {
|
||||
Printf("\nstat::mutation_usefulness: ");
|
||||
for (size_t i = 0; i < Mutators.size(); i++) {
|
||||
double UsefulPercentage =
|
||||
Mutators[i].TotalCount
|
||||
? (100.0 * Mutators[i].UsefulCount) / Mutators[i].TotalCount
|
||||
: 0;
|
||||
Printf("%.3f", UsefulPercentage);
|
||||
if (i < Mutators.size() - 1) Printf(",");
|
||||
}
|
||||
Printf("\n");
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
163
contrib/compiler-rt/lib/fuzzer/FuzzerMutate.h
Normal file
163
contrib/compiler-rt/lib/fuzzer/FuzzerMutate.h
Normal file
@ -0,0 +1,163 @@
|
||||
//===- 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 "FuzzerOptions.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 successful.
|
||||
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 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 configured mutations to the bytes of Data
|
||||
/// that have '1' in Mask.
|
||||
/// Mask.size() should be >= Size.
|
||||
size_t MutateWithMask(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
const Vector<uint8_t> &Mask);
|
||||
|
||||
/// 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 PrintRecommendedDictionary();
|
||||
|
||||
void SetCorpus(const InputCorpus *Corpus) { this->Corpus = Corpus; }
|
||||
|
||||
Random &GetRand() { return Rand; }
|
||||
|
||||
void PrintMutationStats();
|
||||
|
||||
void RecordUsefulMutations();
|
||||
|
||||
private:
|
||||
struct Mutator {
|
||||
size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max);
|
||||
const char *Name;
|
||||
uint64_t UsefulCount;
|
||||
uint64_t TotalCount;
|
||||
};
|
||||
|
||||
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,
|
||||
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);
|
||||
DictionaryEntry MakeDictionaryEntryFromCMP(const Word &Arg1, const Word &Arg2,
|
||||
const uint8_t *Data, size_t Size);
|
||||
DictionaryEntry MakeDictionaryEntryFromCMP(const void *Arg1, const void *Arg2,
|
||||
const void *Arg1Mutation,
|
||||
const void *Arg2Mutation,
|
||||
size_t ArgSize,
|
||||
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 successful discoveries in the past mutations.
|
||||
Dictionary PersistentAutoDictionary;
|
||||
|
||||
Vector<DictionaryEntry *> CurrentDictionaryEntrySequence;
|
||||
Vector<Mutator *> CurrentMutatorSequence;
|
||||
|
||||
static const size_t kCmpDictionaryEntriesDequeSize = 16;
|
||||
DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
|
||||
size_t CmpDictionaryEntriesDequeIdx = 0;
|
||||
|
||||
const InputCorpus *Corpus = nullptr;
|
||||
Vector<uint8_t> MutateInPlaceHere;
|
||||
Vector<uint8_t> MutateWithMaskTemp;
|
||||
// CustomCrossOver needs its own buffer as a custom implementation may call
|
||||
// LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere.
|
||||
Vector<uint8_t> CustomCrossOverInPlaceHere;
|
||||
|
||||
Vector<Mutator> Mutators;
|
||||
Vector<Mutator> DefaultMutators;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_MUTATE_H
|
78
contrib/compiler-rt/lib/fuzzer/FuzzerOptions.h
Normal file
78
contrib/compiler-rt/lib/fuzzer/FuzzerOptions.h
Normal file
@ -0,0 +1,78 @@
|
||||
//
|
||||
// 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;
|
||||
size_t LenControl = 1000;
|
||||
int UnitTimeoutSec = 300;
|
||||
int TimeoutExitCode = 77;
|
||||
int ErrorExitCode = 77;
|
||||
int MaxTotalTimeSec = 0;
|
||||
int RssLimitMb = 0;
|
||||
int MallocLimitMb = 0;
|
||||
bool DoCrossOver = true;
|
||||
int MutateDepth = 5;
|
||||
bool ReduceDepth = false;
|
||||
bool UseCounters = false;
|
||||
bool UseMemmem = true;
|
||||
bool UseCmp = false;
|
||||
int UseValueProfile = false;
|
||||
bool Shrink = false;
|
||||
bool ReduceInputs = 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;
|
||||
std::string FocusFunction;
|
||||
std::string DataFlowTrace;
|
||||
bool SaveArtifacts = true;
|
||||
bool PrintNEW = true; // Print a status line when new units are found;
|
||||
bool PrintNewCovPcs = false;
|
||||
int PrintNewCovFuncs = 0;
|
||||
bool PrintFinalStats = false;
|
||||
bool PrintMutationStats = false;
|
||||
bool PrintCorpusStats = false;
|
||||
bool PrintCoverage = false;
|
||||
bool PrintUnstableStats = false;
|
||||
int HandleUnstable = 0;
|
||||
bool DumpCoverage = false;
|
||||
bool DetectLeaks = true;
|
||||
int PurgeAllocatorIntervalSec = 1;
|
||||
int TraceMalloc = 0;
|
||||
bool HandleAbrt = false;
|
||||
bool HandleBus = false;
|
||||
bool HandleFpe = false;
|
||||
bool HandleIll = false;
|
||||
bool HandleInt = false;
|
||||
bool HandleSegv = false;
|
||||
bool HandleTerm = false;
|
||||
bool HandleXfsz = false;
|
||||
bool HandleUsr1 = false;
|
||||
bool HandleUsr2 = false;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_OPTIONS_H
|
34
contrib/compiler-rt/lib/fuzzer/FuzzerRandom.h
Normal file
34
contrib/compiler-rt/lib/fuzzer/FuzzerRandom.h
Normal file
@ -0,0 +1,34 @@
|
||||
//===- 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 std::mt19937 {
|
||||
public:
|
||||
Random(unsigned int seed) : std::mt19937(seed) {}
|
||||
result_type operator()() { return this->std::mt19937::operator()(); }
|
||||
size_t Rand() { return this->operator()(); }
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_RANDOM_H
|
222
contrib/compiler-rt/lib/fuzzer/FuzzerSHA1.cpp
Normal file
222
contrib/compiler-rt/lib/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
contrib/compiler-rt/lib/fuzzer/FuzzerSHA1.h
Normal file
33
contrib/compiler-rt/lib/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
|
69
contrib/compiler-rt/lib/fuzzer/FuzzerShmem.h
Normal file
69
contrib/compiler-rt/lib/fuzzer/FuzzerShmem.h
Normal file
@ -0,0 +1,69 @@
|
||||
//===- FuzzerShmem.h - shared memory interface ------------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SharedMemoryRegion
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_SHMEM_H
|
||||
#define LLVM_FUZZER_SHMEM_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
class SharedMemoryRegion {
|
||||
public:
|
||||
bool Create(const char *Name);
|
||||
bool Open(const char *Name);
|
||||
bool Destroy(const char *Name);
|
||||
uint8_t *GetData() { return Data; }
|
||||
void PostServer() {Post(0);}
|
||||
void WaitServer() {Wait(0);}
|
||||
void PostClient() {Post(1);}
|
||||
void WaitClient() {Wait(1);}
|
||||
|
||||
size_t WriteByteArray(const uint8_t *Bytes, size_t N) {
|
||||
assert(N <= kShmemSize - sizeof(N));
|
||||
memcpy(GetData(), &N, sizeof(N));
|
||||
memcpy(GetData() + sizeof(N), Bytes, N);
|
||||
assert(N == ReadByteArraySize());
|
||||
return N;
|
||||
}
|
||||
size_t ReadByteArraySize() {
|
||||
size_t Res;
|
||||
memcpy(&Res, GetData(), sizeof(Res));
|
||||
return Res;
|
||||
}
|
||||
uint8_t *GetByteArray() { return GetData() + sizeof(size_t); }
|
||||
|
||||
bool IsServer() const { return Data && IAmServer; }
|
||||
bool IsClient() const { return Data && !IAmServer; }
|
||||
|
||||
private:
|
||||
|
||||
static const size_t kShmemSize = 1 << 22;
|
||||
bool IAmServer;
|
||||
std::string Path(const char *Name);
|
||||
std::string SemName(const char *Name, int Idx);
|
||||
void Post(int Idx);
|
||||
void Wait(int Idx);
|
||||
|
||||
bool Map(int fd);
|
||||
uint8_t *Data = nullptr;
|
||||
void *Semaphore[2];
|
||||
};
|
||||
|
||||
extern SharedMemoryRegion SMR;
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_SHMEM_H
|
38
contrib/compiler-rt/lib/fuzzer/FuzzerShmemFuchsia.cpp
Normal file
38
contrib/compiler-rt/lib/fuzzer/FuzzerShmemFuchsia.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
//===- FuzzerShmemPosix.cpp - Posix shared memory ---------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SharedMemoryRegion. For Fuchsia, this is just stubs as equivalence servers
|
||||
// are not currently supported.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
#if LIBFUZZER_FUCHSIA
|
||||
|
||||
#include "FuzzerShmem.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
bool SharedMemoryRegion::Create(const char *Name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Open(const char *Name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Destroy(const char *Name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void SharedMemoryRegion::Post(int Idx) {}
|
||||
|
||||
void SharedMemoryRegion::Wait(int Idx) {}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_FUCHSIA
|
108
contrib/compiler-rt/lib/fuzzer/FuzzerShmemPosix.cpp
Normal file
108
contrib/compiler-rt/lib/fuzzer/FuzzerShmemPosix.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
//===- FuzzerShmemPosix.cpp - Posix shared memory ---------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SharedMemoryRegion
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_POSIX
|
||||
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerShmem.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <semaphore.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
std::string SharedMemoryRegion::Path(const char *Name) {
|
||||
return DirPlusFile(TmpDir(), Name);
|
||||
}
|
||||
|
||||
std::string SharedMemoryRegion::SemName(const char *Name, int Idx) {
|
||||
std::string Res(Name);
|
||||
// When passing a name without a leading <slash> character to
|
||||
// sem_open, the behaviour is unspecified in POSIX. Add a leading
|
||||
// <slash> character for the name if there is no such one.
|
||||
if (!Res.empty() && Res[0] != '/')
|
||||
Res.insert(Res.begin(), '/');
|
||||
return Res + (char)('0' + Idx);
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Map(int fd) {
|
||||
Data =
|
||||
(uint8_t *)mmap(0, kShmemSize, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (Data == (uint8_t*)-1)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Create(const char *Name) {
|
||||
int fd = open(Path(Name).c_str(), O_CREAT | O_RDWR, 0777);
|
||||
if (fd < 0) return false;
|
||||
if (ftruncate(fd, kShmemSize) < 0) return false;
|
||||
if (!Map(fd))
|
||||
return false;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
sem_unlink(SemName(Name, i).c_str());
|
||||
Semaphore[i] = sem_open(SemName(Name, i).c_str(), O_CREAT, 0644, 0);
|
||||
if (Semaphore[i] == SEM_FAILED)
|
||||
return false;
|
||||
}
|
||||
IAmServer = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Open(const char *Name) {
|
||||
int fd = open(Path(Name).c_str(), O_RDWR);
|
||||
if (fd < 0) return false;
|
||||
struct stat stat_res;
|
||||
if (0 != fstat(fd, &stat_res))
|
||||
return false;
|
||||
assert(stat_res.st_size == kShmemSize);
|
||||
if (!Map(fd))
|
||||
return false;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Semaphore[i] = sem_open(SemName(Name, i).c_str(), 0);
|
||||
if (Semaphore[i] == SEM_FAILED)
|
||||
return false;
|
||||
}
|
||||
IAmServer = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Destroy(const char *Name) {
|
||||
return 0 == unlink(Path(Name).c_str());
|
||||
}
|
||||
|
||||
void SharedMemoryRegion::Post(int Idx) {
|
||||
assert(Idx == 0 || Idx == 1);
|
||||
sem_post((sem_t*)Semaphore[Idx]);
|
||||
}
|
||||
|
||||
void SharedMemoryRegion::Wait(int Idx) {
|
||||
assert(Idx == 0 || Idx == 1);
|
||||
for (int i = 0; i < 10 && sem_wait((sem_t*)Semaphore[Idx]); i++) {
|
||||
// sem_wait may fail if interrupted by a signal.
|
||||
sleep(i);
|
||||
if (i)
|
||||
Printf("%s: sem_wait[%d] failed %s\n", i < 9 ? "WARNING" : "ERROR", i,
|
||||
strerror(errno));
|
||||
if (i == 9) abort();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_POSIX
|
64
contrib/compiler-rt/lib/fuzzer/FuzzerShmemWindows.cpp
Normal file
64
contrib/compiler-rt/lib/fuzzer/FuzzerShmemWindows.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
//===- FuzzerShmemWindows.cpp - Posix shared memory -------------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SharedMemoryRegion
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerShmem.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
std::string SharedMemoryRegion::Path(const char *Name) {
|
||||
return DirPlusFile(TmpDir(), Name);
|
||||
}
|
||||
|
||||
std::string SharedMemoryRegion::SemName(const char *Name, int Idx) {
|
||||
std::string Res(Name);
|
||||
return Res + (char)('0' + Idx);
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Map(int fd) {
|
||||
assert(0 && "UNIMPLEMENTED");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Create(const char *Name) {
|
||||
assert(0 && "UNIMPLEMENTED");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Open(const char *Name) {
|
||||
assert(0 && "UNIMPLEMENTED");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SharedMemoryRegion::Destroy(const char *Name) {
|
||||
assert(0 && "UNIMPLEMENTED");
|
||||
return false;
|
||||
}
|
||||
|
||||
void SharedMemoryRegion::Post(int Idx) {
|
||||
assert(0 && "UNIMPLEMENTED");
|
||||
}
|
||||
|
||||
void SharedMemoryRegion::Wait(int Idx) {
|
||||
Semaphore[1] = nullptr;
|
||||
assert(0 && "UNIMPLEMENTED");
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
682
contrib/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp
Normal file
682
contrib/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp
Normal file
@ -0,0 +1,682 @@
|
||||
//===- 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 "FuzzerTracePC.h"
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerDictionary.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerUtil.h"
|
||||
#include "FuzzerValueBitMap.h"
|
||||
#include <set>
|
||||
|
||||
// The coverage counters and PCs.
|
||||
// These are declared as global variables named "__sancov_*" to simplify
|
||||
// experiments with inlined instrumentation.
|
||||
alignas(64) ATTRIBUTE_INTERFACE
|
||||
uint8_t __sancov_trace_pc_guard_8bit_counters[fuzzer::TracePC::kNumPCs];
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs];
|
||||
|
||||
// Used by -fsanitize-coverage=stack-depth to track stack depth
|
||||
ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec")))
|
||||
thread_local uintptr_t __sancov_lowest_stack;
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
TracePC TPC;
|
||||
|
||||
uint8_t *TracePC::Counters() const {
|
||||
return __sancov_trace_pc_guard_8bit_counters;
|
||||
}
|
||||
|
||||
uintptr_t *TracePC::PCs() const {
|
||||
return __sancov_trace_pc_pcs;
|
||||
}
|
||||
|
||||
size_t TracePC::GetTotalPCCoverage() {
|
||||
if (ObservedPCs.size())
|
||||
return ObservedPCs.size();
|
||||
size_t Res = 0;
|
||||
for (size_t i = 1, N = GetNumPCs(); i < N; i++)
|
||||
if (PCs()[i])
|
||||
Res++;
|
||||
return Res;
|
||||
}
|
||||
|
||||
template<class CallBack>
|
||||
void TracePC::IterateInline8bitCounters(CallBack CB) const {
|
||||
if (NumInline8bitCounters && NumInline8bitCounters == NumPCsInPCTables) {
|
||||
size_t CounterIdx = 0;
|
||||
for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
|
||||
uint8_t *Beg = ModuleCounters[i].Start;
|
||||
size_t Size = ModuleCounters[i].Stop - Beg;
|
||||
assert(Size == (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
|
||||
for (size_t j = 0; j < Size; j++, CounterIdx++)
|
||||
CB(i, j, CounterIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes unstable counters by copying Inline8bitCounters to unstable
|
||||
// counters.
|
||||
void TracePC::InitializeUnstableCounters() {
|
||||
IterateInline8bitCounters([&](int i, int j, int UnstableIdx) {
|
||||
UnstableCounters[UnstableIdx].Counter = ModuleCounters[i].Start[j];
|
||||
});
|
||||
}
|
||||
|
||||
// Compares the current counters with counters from previous runs
|
||||
// and records differences as unstable edges.
|
||||
void TracePC::UpdateUnstableCounters(int UnstableMode) {
|
||||
IterateInline8bitCounters([&](int i, int j, int UnstableIdx) {
|
||||
if (ModuleCounters[i].Start[j] != UnstableCounters[UnstableIdx].Counter) {
|
||||
UnstableCounters[UnstableIdx].IsUnstable = true;
|
||||
if (UnstableMode == ZeroUnstable)
|
||||
UnstableCounters[UnstableIdx].Counter = 0;
|
||||
else if (UnstableMode == MinUnstable)
|
||||
UnstableCounters[UnstableIdx].Counter = std::min(
|
||||
ModuleCounters[i].Start[j], UnstableCounters[UnstableIdx].Counter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Moves the minimum hit counts to ModuleCounters.
|
||||
void TracePC::ApplyUnstableCounters() {
|
||||
IterateInline8bitCounters([&](int i, int j, int UnstableIdx) {
|
||||
ModuleCounters[i].Start[j] = UnstableCounters[UnstableIdx].Counter;
|
||||
});
|
||||
}
|
||||
|
||||
void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) {
|
||||
if (Start == Stop) return;
|
||||
if (NumModulesWithInline8bitCounters &&
|
||||
ModuleCounters[NumModulesWithInline8bitCounters-1].Start == Start) return;
|
||||
assert(NumModulesWithInline8bitCounters <
|
||||
sizeof(ModuleCounters) / sizeof(ModuleCounters[0]));
|
||||
ModuleCounters[NumModulesWithInline8bitCounters++] = {Start, Stop};
|
||||
NumInline8bitCounters += Stop - Start;
|
||||
}
|
||||
|
||||
void TracePC::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop) {
|
||||
const PCTableEntry *B = reinterpret_cast<const PCTableEntry *>(Start);
|
||||
const PCTableEntry *E = reinterpret_cast<const PCTableEntry *>(Stop);
|
||||
if (NumPCTables && ModulePCTable[NumPCTables - 1].Start == B) return;
|
||||
assert(NumPCTables < sizeof(ModulePCTable) / sizeof(ModulePCTable[0]));
|
||||
ModulePCTable[NumPCTables++] = {B, E};
|
||||
NumPCsInPCTables += E - B;
|
||||
}
|
||||
|
||||
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++) {
|
||||
NumGuards++;
|
||||
if (NumGuards == kNumPCs) {
|
||||
RawPrint(
|
||||
"WARNING: The binary has too many instrumented PCs.\n"
|
||||
" You may want to reduce the size of the binary\n"
|
||||
" for more efficient fuzzing and precise coverage data\n");
|
||||
}
|
||||
*P = NumGuards % kNumPCs;
|
||||
}
|
||||
Modules[NumModules].Start = Start;
|
||||
Modules[NumModules].Stop = Stop;
|
||||
NumModules++;
|
||||
}
|
||||
|
||||
void TracePC::PrintModuleInfo() {
|
||||
if (NumGuards) {
|
||||
Printf("INFO: Loaded %zd modules (%zd guards): ", NumModules, NumGuards);
|
||||
for (size_t i = 0; i < NumModules; i++)
|
||||
Printf("%zd [%p, %p), ", Modules[i].Stop - Modules[i].Start,
|
||||
Modules[i].Start, Modules[i].Stop);
|
||||
Printf("\n");
|
||||
}
|
||||
if (NumModulesWithInline8bitCounters) {
|
||||
Printf("INFO: Loaded %zd modules (%zd inline 8-bit counters): ",
|
||||
NumModulesWithInline8bitCounters, NumInline8bitCounters);
|
||||
for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++)
|
||||
Printf("%zd [%p, %p), ", ModuleCounters[i].Stop - ModuleCounters[i].Start,
|
||||
ModuleCounters[i].Start, ModuleCounters[i].Stop);
|
||||
Printf("\n");
|
||||
}
|
||||
if (NumPCTables) {
|
||||
Printf("INFO: Loaded %zd PC tables (%zd PCs): ", NumPCTables,
|
||||
NumPCsInPCTables);
|
||||
for (size_t i = 0; i < NumPCTables; i++) {
|
||||
Printf("%zd [%p,%p), ", ModulePCTable[i].Stop - ModulePCTable[i].Start,
|
||||
ModulePCTable[i].Start, ModulePCTable[i].Stop);
|
||||
}
|
||||
Printf("\n");
|
||||
|
||||
if ((NumGuards && NumGuards != NumPCsInPCTables) ||
|
||||
(NumInline8bitCounters && NumInline8bitCounters != NumPCsInPCTables)) {
|
||||
Printf("ERROR: The size of coverage PC tables does not match the\n"
|
||||
"number of instrumented PCs. This might be a compiler bug,\n"
|
||||
"please contact the libFuzzer developers.\n"
|
||||
"Also check https://bugs.llvm.org/show_bug.cgi?id=34636\n"
|
||||
"for possible workarounds (tl;dr: don't use the old GNU ld)\n");
|
||||
_Exit(1);
|
||||
}
|
||||
}
|
||||
if (size_t NumExtraCounters = ExtraCountersEnd() - ExtraCountersBegin())
|
||||
Printf("INFO: %zd Extra Counters\n", NumExtraCounters);
|
||||
}
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
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);
|
||||
ValueProfileMap.AddValueModPrime(Idx);
|
||||
}
|
||||
|
||||
void TracePC::UpdateObservedPCs() {
|
||||
Vector<uintptr_t> CoveredFuncs;
|
||||
auto ObservePC = [&](uintptr_t PC) {
|
||||
if (ObservedPCs.insert(PC).second && DoPrintNewPCs) {
|
||||
PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p", PC + 1);
|
||||
Printf("\n");
|
||||
}
|
||||
};
|
||||
|
||||
auto Observe = [&](const PCTableEntry &TE) {
|
||||
if (TE.PCFlags & 1)
|
||||
if (++ObservedFuncs[TE.PC] == 1 && NumPrintNewFuncs)
|
||||
CoveredFuncs.push_back(TE.PC);
|
||||
ObservePC(TE.PC);
|
||||
};
|
||||
|
||||
if (NumPCsInPCTables) {
|
||||
if (NumInline8bitCounters == NumPCsInPCTables) {
|
||||
IterateInline8bitCounters([&](int i, int j, int CounterIdx) {
|
||||
if (ModuleCounters[i].Start[j])
|
||||
Observe(ModulePCTable[i].Start[j]);
|
||||
});
|
||||
} else if (NumGuards == NumPCsInPCTables) {
|
||||
size_t GuardIdx = 1;
|
||||
for (size_t i = 0; i < NumModules; i++) {
|
||||
uint32_t *Beg = Modules[i].Start;
|
||||
size_t Size = Modules[i].Stop - Beg;
|
||||
assert(Size ==
|
||||
(size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
|
||||
for (size_t j = 0; j < Size; j++, GuardIdx++)
|
||||
if (Counters()[GuardIdx])
|
||||
Observe(ModulePCTable[i].Start[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0, N = Min(CoveredFuncs.size(), NumPrintNewFuncs); i < N;
|
||||
i++) {
|
||||
Printf("\tNEW_FUNC[%zd/%zd]: ", i + 1, CoveredFuncs.size());
|
||||
PrintPC("%p %F %L", "%p", CoveredFuncs[i] + 1);
|
||||
Printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) {
|
||||
// TODO: this implementation is x86 only.
|
||||
// see sanitizer_common GetPreviousInstructionPc for full implementation.
|
||||
return PC - 1;
|
||||
}
|
||||
|
||||
inline ALWAYS_INLINE uintptr_t GetNextInstructionPc(uintptr_t PC) {
|
||||
// TODO: this implementation is x86 only.
|
||||
// see sanitizer_common GetPreviousInstructionPc for full implementation.
|
||||
return PC + 1;
|
||||
}
|
||||
|
||||
static std::string GetModuleName(uintptr_t PC) {
|
||||
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 *>(PC), ModulePathRaw,
|
||||
sizeof(ModulePathRaw), &OffsetRaw))
|
||||
return "";
|
||||
return ModulePathRaw;
|
||||
}
|
||||
|
||||
template<class CallBack>
|
||||
void TracePC::IterateCoveredFunctions(CallBack CB) {
|
||||
for (size_t i = 0; i < NumPCTables; i++) {
|
||||
auto &M = ModulePCTable[i];
|
||||
assert(M.Start < M.Stop);
|
||||
auto ModuleName = GetModuleName(M.Start->PC);
|
||||
for (auto NextFE = M.Start; NextFE < M.Stop; ) {
|
||||
auto FE = NextFE;
|
||||
assert((FE->PCFlags & 1) && "Not a function entry point");
|
||||
do {
|
||||
NextFE++;
|
||||
} while (NextFE < M.Stop && !(NextFE->PCFlags & 1));
|
||||
if (ObservedFuncs.count(FE->PC))
|
||||
CB(FE, NextFE, ObservedFuncs[FE->PC]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TracePC::SetFocusFunction(const std::string &FuncName) {
|
||||
// This function should be called once.
|
||||
assert(FocusFunction.first > NumModulesWithInline8bitCounters);
|
||||
if (FuncName.empty())
|
||||
return;
|
||||
for (size_t M = 0; M < NumModulesWithInline8bitCounters; M++) {
|
||||
auto &PCTE = ModulePCTable[M];
|
||||
size_t N = PCTE.Stop - PCTE.Start;
|
||||
for (size_t I = 0; I < N; I++) {
|
||||
if (!(PCTE.Start[I].PCFlags & 1)) continue; // not a function entry.
|
||||
auto Name = DescribePC("%F", GetNextInstructionPc(PCTE.Start[I].PC));
|
||||
if (Name[0] == 'i' && Name[1] == 'n' && Name[2] == ' ')
|
||||
Name = Name.substr(3, std::string::npos);
|
||||
if (FuncName != Name) continue;
|
||||
Printf("INFO: Focus function is set to '%s'\n", Name.c_str());
|
||||
FocusFunction = {M, I};
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TracePC::ObservedFocusFunction() {
|
||||
size_t I = FocusFunction.first;
|
||||
size_t J = FocusFunction.second;
|
||||
if (I >= NumModulesWithInline8bitCounters)
|
||||
return false;
|
||||
auto &MC = ModuleCounters[I];
|
||||
size_t Size = MC.Stop - MC.Start;
|
||||
if (J >= Size)
|
||||
return false;
|
||||
return MC.Start[J] != 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
Printf("COVERAGE:\n");
|
||||
auto CoveredFunctionCallback = [&](const PCTableEntry *First,
|
||||
const PCTableEntry *Last,
|
||||
uintptr_t Counter) {
|
||||
assert(First < Last);
|
||||
auto VisualizePC = GetNextInstructionPc(First->PC);
|
||||
std::string FileStr = DescribePC("%s", VisualizePC);
|
||||
if (!IsInterestingCoverageFile(FileStr))
|
||||
return;
|
||||
std::string FunctionStr = DescribePC("%F", VisualizePC);
|
||||
if (FunctionStr.find("in ") == 0)
|
||||
FunctionStr = FunctionStr.substr(3);
|
||||
std::string LineStr = DescribePC("%l", VisualizePC);
|
||||
size_t Line = std::stoul(LineStr);
|
||||
size_t NumEdges = Last - First;
|
||||
Vector<uintptr_t> UncoveredPCs;
|
||||
for (auto TE = First; TE < Last; TE++)
|
||||
if (!ObservedPCs.count(TE->PC))
|
||||
UncoveredPCs.push_back(TE->PC);
|
||||
Printf("COVERED_FUNC: hits: %zd", Counter);
|
||||
Printf(" edges: %zd/%zd", NumEdges - UncoveredPCs.size(), NumEdges);
|
||||
Printf(" %s %s:%zd\n", FunctionStr.c_str(), FileStr.c_str(), Line);
|
||||
for (auto PC: UncoveredPCs)
|
||||
Printf(" UNCOVERED_PC: %s\n",
|
||||
DescribePC("%s:%l", GetNextInstructionPc(PC)).c_str());
|
||||
};
|
||||
|
||||
IterateCoveredFunctions(CoveredFunctionCallback);
|
||||
}
|
||||
|
||||
void TracePC::DumpCoverage() {
|
||||
if (EF->__sanitizer_dump_coverage) {
|
||||
Vector<uintptr_t> PCsCopy(GetNumPCs());
|
||||
for (size_t i = 0; i < GetNumPCs(); i++)
|
||||
PCsCopy[i] = PCs()[i] ? GetPreviousInstructionPc(PCs()[i]) : 0;
|
||||
EF->__sanitizer_dump_coverage(PCsCopy.data(), PCsCopy.size());
|
||||
}
|
||||
}
|
||||
|
||||
void TracePC::PrintUnstableStats() {
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < NumInline8bitCounters; i++)
|
||||
if (UnstableCounters[i].IsUnstable)
|
||||
count++;
|
||||
Printf("stat::stability_rate: %.2f\n",
|
||||
100 - static_cast<float>(count * 100) / NumInline8bitCounters);
|
||||
}
|
||||
|
||||
// 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_ALL
|
||||
void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
|
||||
size_t n, bool StopAtZero) {
|
||||
if (!n) return;
|
||||
size_t Len = std::min(n, Word::GetMaxSize());
|
||||
const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1);
|
||||
const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2);
|
||||
uint8_t B1[Word::kMaxSize];
|
||||
uint8_t B2[Word::kMaxSize];
|
||||
// Copy the data into locals in this non-msan-instrumented function
|
||||
// to avoid msan complaining further.
|
||||
size_t Hash = 0; // Compute some simple hash of both strings.
|
||||
for (size_t i = 0; i < Len; i++) {
|
||||
B1[i] = A1[i];
|
||||
B2[i] = A2[i];
|
||||
size_t T = B1[i];
|
||||
Hash ^= (T << 8) | B2[i];
|
||||
}
|
||||
size_t I = 0;
|
||||
for (; I < Len; I++)
|
||||
if (B1[I] != B2[I] || (StopAtZero && B1[I] == 0))
|
||||
break;
|
||||
size_t PC = reinterpret_cast<size_t>(caller_pc);
|
||||
size_t Idx = (PC & 4095) | (I << 12);
|
||||
ValueProfileMap.AddValue(Idx);
|
||||
TORCW.Insert(Idx ^ Hash, Word(B1, Len), Word(B2, Len));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
ATTRIBUTE_TARGET_POPCNT ALWAYS_INLINE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void TracePC::HandleCmp(uintptr_t PC, T Arg1, T Arg2) {
|
||||
uint64_t ArgXor = Arg1 ^ Arg2;
|
||||
uint64_t ArgDistance = __builtin_popcountll(ArgXor) + 1; // [1,65]
|
||||
uintptr_t Idx = ((PC & 4095) + 1) * ArgDistance;
|
||||
if (sizeof(T) == 4)
|
||||
TORC4.Insert(ArgXor, Arg1, Arg2);
|
||||
else if (sizeof(T) == 8)
|
||||
TORC8.Insert(ArgXor, Arg1, Arg2);
|
||||
// TODO: remove these flags and instead use all metrics at once.
|
||||
if (UseValueProfileMask & 1)
|
||||
ValueProfileMap.AddValue(Idx);
|
||||
if (UseValueProfileMask & 2)
|
||||
ValueProfileMap.AddValue(
|
||||
PC * 64 + (Arg1 == Arg2 ? 0 : __builtin_clzll(Arg1 - Arg2) + 1));
|
||||
if (UseValueProfileMask & 4) // alternative way to use the hamming distance
|
||||
ValueProfileMap.AddValue(PC * 64 + ArgDistance);
|
||||
}
|
||||
|
||||
static size_t InternalStrnlen(const char *S, size_t MaxLen) {
|
||||
size_t Len = 0;
|
||||
for (; Len < MaxLen && S[Len]; Len++) {}
|
||||
return Len;
|
||||
}
|
||||
|
||||
// Finds min of (strlen(S1), strlen(S2)).
|
||||
// Needed bacause one of these strings may actually be non-zero terminated.
|
||||
static size_t InternalStrnlen2(const char *S1, const char *S2) {
|
||||
size_t Len = 0;
|
||||
for (; S1[Len] && S2[Len]; Len++) {}
|
||||
return Len;
|
||||
}
|
||||
|
||||
void TracePC::ClearInlineCounters() {
|
||||
for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
|
||||
uint8_t *Beg = ModuleCounters[i].Start;
|
||||
size_t Size = ModuleCounters[i].Stop - Beg;
|
||||
memset(Beg, 0, Size);
|
||||
}
|
||||
}
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void TracePC::RecordInitialStack() {
|
||||
int stack;
|
||||
__sancov_lowest_stack = InitialStack = reinterpret_cast<uintptr_t>(&stack);
|
||||
}
|
||||
|
||||
uintptr_t TracePC::GetMaxStackOffset() const {
|
||||
return InitialStack - __sancov_lowest_stack; // Stack grows down
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
extern "C" {
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uint32_t Idx = *Guard;
|
||||
__sancov_trace_pc_pcs[Idx] = PC;
|
||||
__sancov_trace_pc_guard_8bit_counters[Idx]++;
|
||||
}
|
||||
|
||||
// Best-effort support for -fsanitize-coverage=trace-pc, which is available
|
||||
// in both Clang and GCC.
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void __sanitizer_cov_trace_pc() {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t Idx = PC & (((uintptr_t)1 << fuzzer::TracePC::kTracePcBits) - 1);
|
||||
__sancov_trace_pc_pcs[Idx] = PC;
|
||||
__sancov_trace_pc_guard_8bit_counters[Idx]++;
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) {
|
||||
fuzzer::TPC.HandleInit(Start, Stop);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
void __sanitizer_cov_8bit_counters_init(uint8_t *Start, uint8_t *Stop) {
|
||||
fuzzer::TPC.HandleInline8bitCountersInit(Start, Stop);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg,
|
||||
const uintptr_t *pcs_end) {
|
||||
fuzzer::TPC.HandlePCsInit(pcs_beg, pcs_end);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCallerCallee(PC, Callee);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
// Now the __sanitizer_cov_trace_const_cmp[1248] callbacks just mimic
|
||||
// the behaviour of __sanitizer_cov_trace_cmp[1248] ones. This, however,
|
||||
// should be changed later to make full use of instrumentation.
|
||||
void __sanitizer_cov_trace_const_cmp8(uint64_t Arg1, uint64_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_const_cmp4(uint32_t Arg1, uint32_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_const_cmp2(uint16_t Arg1, uint16_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_const_cmp1(uint8_t Arg1, uint8_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
|
||||
uint64_t N = Cases[0];
|
||||
uint64_t ValSizeInBits = Cases[1];
|
||||
uint64_t *Vals = Cases + 2;
|
||||
// Skip the most common and the most boring case.
|
||||
if (Vals[N - 1] < 256 && Val < 256)
|
||||
return;
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
size_t i;
|
||||
uint64_t Token = 0;
|
||||
for (i = 0; i < N; i++) {
|
||||
Token = Val ^ Vals[i];
|
||||
if (Val < Vals[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if (ValSizeInBits == 16)
|
||||
fuzzer::TPC.HandleCmp(PC + i, static_cast<uint16_t>(Token), (uint16_t)(0));
|
||||
else if (ValSizeInBits == 32)
|
||||
fuzzer::TPC.HandleCmp(PC + i, static_cast<uint32_t>(Token), (uint32_t)(0));
|
||||
else
|
||||
fuzzer::TPC.HandleCmp(PC + i, Token, (uint64_t)(0));
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_div4(uint32_t Val) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Val, (uint32_t)0);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_div8(uint64_t Val) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Val, (uint64_t)0);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_gep(uintptr_t Idx) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
fuzzer::TPC.HandleCmp(PC, Idx, (uintptr_t)0);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
|
||||
const void *s2, size_t n, int result) {
|
||||
if (!fuzzer::RunningUserCallback) return;
|
||||
if (result == 0) return; // No reason to mutate.
|
||||
if (n <= 1) return; // Not interesting.
|
||||
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/false);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1,
|
||||
const char *s2, size_t n, int result) {
|
||||
if (!fuzzer::RunningUserCallback) 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.
|
||||
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/true);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
|
||||
const char *s2, int result) {
|
||||
if (!fuzzer::RunningUserCallback) return;
|
||||
if (result == 0) return; // No reason to mutate.
|
||||
size_t N = fuzzer::InternalStrnlen2(s1, s2);
|
||||
if (N <= 1) return; // Not interesting.
|
||||
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, N, /*StopAtZero*/true);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1,
|
||||
const char *s2, size_t n, int result) {
|
||||
if (!fuzzer::RunningUserCallback) return;
|
||||
return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1,
|
||||
const char *s2, int result) {
|
||||
if (!fuzzer::RunningUserCallback) return;
|
||||
return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1,
|
||||
const char *s2, char *result) {
|
||||
if (!fuzzer::RunningUserCallback) return;
|
||||
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1,
|
||||
const char *s2, char *result) {
|
||||
if (!fuzzer::RunningUserCallback) return;
|
||||
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1,
|
||||
const void *s2, size_t len2, void *result) {
|
||||
if (!fuzzer::RunningUserCallback) return;
|
||||
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), len2);
|
||||
}
|
||||
} // extern "C"
|
310
contrib/compiler-rt/lib/fuzzer/FuzzerTracePC.h
Normal file
310
contrib/compiler-rt/lib/fuzzer/FuzzerTracePC.h
Normal file
@ -0,0 +1,310 @@
|
||||
//===- 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 "FuzzerDictionary.h"
|
||||
#include "FuzzerValueBitMap.h"
|
||||
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
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;
|
||||
};
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void Insert(size_t Idx, const T &Arg1, const 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];
|
||||
};
|
||||
|
||||
template <size_t kSizeT>
|
||||
struct MemMemTable {
|
||||
static const size_t kSize = kSizeT;
|
||||
Word MemMemWords[kSize];
|
||||
Word EmptyWord;
|
||||
|
||||
void Add(const uint8_t *Data, size_t Size) {
|
||||
if (Size <= 2) return;
|
||||
Size = std::min(Size, Word::GetMaxSize());
|
||||
size_t Idx = SimpleFastHash(Data, Size) % kSize;
|
||||
MemMemWords[Idx].Set(Data, Size);
|
||||
}
|
||||
const Word &Get(size_t Idx) {
|
||||
for (size_t i = 0; i < kSize; i++) {
|
||||
const Word &W = MemMemWords[(Idx + i) % kSize];
|
||||
if (W.size()) return W;
|
||||
}
|
||||
EmptyWord.Set(nullptr, 0);
|
||||
return EmptyWord;
|
||||
}
|
||||
};
|
||||
|
||||
class TracePC {
|
||||
public:
|
||||
static const size_t kNumPCs = 1 << 21;
|
||||
// How many bits of PC are used from __sanitizer_cov_trace_pc.
|
||||
static const size_t kTracePcBits = 18;
|
||||
|
||||
enum HandleUnstableOptions {
|
||||
MinUnstable = 1,
|
||||
ZeroUnstable = 2,
|
||||
};
|
||||
|
||||
void HandleInit(uint32_t *Start, uint32_t *Stop);
|
||||
void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop);
|
||||
void HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop);
|
||||
void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee);
|
||||
template <class T> void HandleCmp(uintptr_t PC, T Arg1, T Arg2);
|
||||
size_t GetTotalPCCoverage();
|
||||
void SetUseCounters(bool UC) { UseCounters = UC; }
|
||||
void SetUseValueProfileMask(uint32_t VPMask) { UseValueProfileMask = VPMask; }
|
||||
void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; }
|
||||
void SetPrintNewFuncs(size_t P) { NumPrintNewFuncs = P; }
|
||||
void UpdateObservedPCs();
|
||||
template <class Callback> void CollectFeatures(Callback CB) const;
|
||||
|
||||
void ResetMaps() {
|
||||
ValueProfileMap.Reset();
|
||||
if (NumModules)
|
||||
memset(Counters(), 0, GetNumPCs());
|
||||
ClearExtraCounters();
|
||||
ClearInlineCounters();
|
||||
}
|
||||
|
||||
void ClearInlineCounters();
|
||||
|
||||
void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize);
|
||||
void PrintFeatureSet();
|
||||
|
||||
void PrintModuleInfo();
|
||||
|
||||
void PrintCoverage();
|
||||
void DumpCoverage();
|
||||
void PrintUnstableStats();
|
||||
|
||||
template<class CallBack>
|
||||
void IterateCoveredFunctions(CallBack CB);
|
||||
|
||||
void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
|
||||
size_t n, bool StopAtZero);
|
||||
|
||||
TableOfRecentCompares<uint32_t, 32> TORC4;
|
||||
TableOfRecentCompares<uint64_t, 32> TORC8;
|
||||
TableOfRecentCompares<Word, 32> TORCW;
|
||||
MemMemTable<1024> MMT;
|
||||
|
||||
size_t GetNumPCs() const {
|
||||
return NumGuards == 0 ? (1 << kTracePcBits) : Min(kNumPCs, NumGuards + 1);
|
||||
}
|
||||
uintptr_t GetPC(size_t Idx) {
|
||||
assert(Idx < GetNumPCs());
|
||||
return PCs()[Idx];
|
||||
}
|
||||
|
||||
void RecordInitialStack();
|
||||
uintptr_t GetMaxStackOffset() const;
|
||||
|
||||
template<class CallBack>
|
||||
void ForEachObservedPC(CallBack CB) {
|
||||
for (auto PC : ObservedPCs)
|
||||
CB(PC);
|
||||
}
|
||||
|
||||
void SetFocusFunction(const std::string &FuncName);
|
||||
bool ObservedFocusFunction();
|
||||
|
||||
void InitializeUnstableCounters();
|
||||
void UpdateUnstableCounters(int UnstableMode);
|
||||
void ApplyUnstableCounters();
|
||||
|
||||
private:
|
||||
struct UnstableEdge {
|
||||
uint8_t Counter;
|
||||
bool IsUnstable;
|
||||
};
|
||||
|
||||
UnstableEdge UnstableCounters[kNumPCs];
|
||||
|
||||
bool UseCounters = false;
|
||||
uint32_t UseValueProfileMask = false;
|
||||
bool DoPrintNewPCs = false;
|
||||
size_t NumPrintNewFuncs = 0;
|
||||
|
||||
struct Module {
|
||||
uint32_t *Start, *Stop;
|
||||
};
|
||||
|
||||
Module Modules[4096];
|
||||
size_t NumModules; // linker-initialized.
|
||||
size_t NumGuards; // linker-initialized.
|
||||
|
||||
struct { uint8_t *Start, *Stop; } ModuleCounters[4096];
|
||||
size_t NumModulesWithInline8bitCounters; // linker-initialized.
|
||||
size_t NumInline8bitCounters;
|
||||
|
||||
struct PCTableEntry {
|
||||
uintptr_t PC, PCFlags;
|
||||
};
|
||||
|
||||
struct { const PCTableEntry *Start, *Stop; } ModulePCTable[4096];
|
||||
size_t NumPCTables;
|
||||
size_t NumPCsInPCTables;
|
||||
|
||||
uint8_t *Counters() const;
|
||||
uintptr_t *PCs() const;
|
||||
|
||||
Set<uintptr_t> ObservedPCs;
|
||||
std::unordered_map<uintptr_t, uintptr_t> ObservedFuncs; // PC => Counter.
|
||||
|
||||
template <class Callback>
|
||||
void IterateInline8bitCounters(Callback CB) const;
|
||||
|
||||
std::pair<size_t, size_t> FocusFunction = {-1, -1}; // Module and PC IDs.
|
||||
|
||||
ValueBitMap ValueProfileMap;
|
||||
uintptr_t InitialStack;
|
||||
};
|
||||
|
||||
template <class Callback>
|
||||
// void Callback(size_t FirstFeature, size_t Idx, uint8_t Value);
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End,
|
||||
size_t FirstFeature, Callback Handle8bitCounter) {
|
||||
typedef uintptr_t LargeType;
|
||||
const size_t Step = sizeof(LargeType) / sizeof(uint8_t);
|
||||
const size_t StepMask = Step - 1;
|
||||
auto P = Begin;
|
||||
// Iterate by 1 byte until either the alignment boundary or the end.
|
||||
for (; reinterpret_cast<uintptr_t>(P) & StepMask && P < End; P++)
|
||||
if (uint8_t V = *P)
|
||||
Handle8bitCounter(FirstFeature, P - Begin, V);
|
||||
|
||||
// Iterate by Step bytes at a time.
|
||||
for (; P < End; P += Step)
|
||||
if (LargeType Bundle = *reinterpret_cast<const LargeType *>(P))
|
||||
for (size_t I = 0; I < Step; I++, Bundle >>= 8)
|
||||
if (uint8_t V = Bundle & 0xff)
|
||||
Handle8bitCounter(FirstFeature, P - Begin + I, V);
|
||||
|
||||
// Iterate by 1 byte until the end.
|
||||
for (; P < End; P++)
|
||||
if (uint8_t V = *P)
|
||||
Handle8bitCounter(FirstFeature, P - Begin, V);
|
||||
}
|
||||
|
||||
// Given a non-zero Counter returns a number in the range [0,7].
|
||||
template<class T>
|
||||
unsigned CounterToFeature(T Counter) {
|
||||
// Returns a feature number by placing Counters into buckets as illustrated
|
||||
// below.
|
||||
//
|
||||
// Counter bucket: [1] [2] [3] [4-7] [8-15] [16-31] [32-127] [128+]
|
||||
// Feature number: 0 1 2 3 4 5 6 7
|
||||
//
|
||||
// This is a heuristic taken from AFL (see
|
||||
// http://lcamtuf.coredump.cx/afl/technical_details.txt).
|
||||
//
|
||||
// This implementation may change in the future so clients should
|
||||
// not rely on it.
|
||||
assert(Counter);
|
||||
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;
|
||||
return Bit;
|
||||
}
|
||||
|
||||
template <class Callback> // void Callback(size_t Feature)
|
||||
ATTRIBUTE_NO_SANITIZE_ADDRESS
|
||||
__attribute__((noinline))
|
||||
void TracePC::CollectFeatures(Callback HandleFeature) const {
|
||||
uint8_t *Counters = this->Counters();
|
||||
size_t N = GetNumPCs();
|
||||
auto Handle8bitCounter = [&](size_t FirstFeature,
|
||||
size_t Idx, uint8_t Counter) {
|
||||
if (UseCounters)
|
||||
HandleFeature(FirstFeature + Idx * 8 + CounterToFeature(Counter));
|
||||
else
|
||||
HandleFeature(FirstFeature + Idx);
|
||||
};
|
||||
|
||||
size_t FirstFeature = 0;
|
||||
|
||||
if (!NumInline8bitCounters) {
|
||||
ForEachNonZeroByte(Counters, Counters + N, FirstFeature, Handle8bitCounter);
|
||||
FirstFeature += N * 8;
|
||||
}
|
||||
|
||||
if (NumInline8bitCounters) {
|
||||
for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
|
||||
ForEachNonZeroByte(ModuleCounters[i].Start, ModuleCounters[i].Stop,
|
||||
FirstFeature, Handle8bitCounter);
|
||||
FirstFeature += 8 * (ModuleCounters[i].Stop - ModuleCounters[i].Start);
|
||||
}
|
||||
}
|
||||
|
||||
ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(), FirstFeature,
|
||||
Handle8bitCounter);
|
||||
FirstFeature += (ExtraCountersEnd() - ExtraCountersBegin()) * 8;
|
||||
|
||||
if (UseValueProfileMask) {
|
||||
ValueProfileMap.ForEach([&](size_t Idx) {
|
||||
HandleFeature(FirstFeature + Idx);
|
||||
});
|
||||
FirstFeature += ValueProfileMap.SizeInBits();
|
||||
}
|
||||
|
||||
// Step function, grows similar to 8 * Log_2(A).
|
||||
auto StackDepthStepFunction = [](uint32_t A) -> uint32_t {
|
||||
if (!A) return A;
|
||||
uint32_t Log2 = Log(A);
|
||||
if (Log2 < 3) return A;
|
||||
Log2 -= 3;
|
||||
return (Log2 + 1) * 8 + ((A >> Log2) & 7);
|
||||
};
|
||||
assert(StackDepthStepFunction(1024) == 64);
|
||||
assert(StackDepthStepFunction(1024 * 4) == 80);
|
||||
assert(StackDepthStepFunction(1024 * 1024) == 144);
|
||||
|
||||
if (auto MaxStackOffset = GetMaxStackOffset())
|
||||
HandleFeature(FirstFeature + StackDepthStepFunction(MaxStackOffset / 8));
|
||||
}
|
||||
|
||||
extern TracePC TPC;
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_TRACE_PC
|
232
contrib/compiler-rt/lib/fuzzer/FuzzerUtil.cpp
Normal file
232
contrib/compiler-rt/lib/fuzzer/FuzzerUtil.cpp
Normal file
@ -0,0 +1,232 @@
|
||||
//===- 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 <mutex>
|
||||
#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, 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;
|
||||
}
|
||||
|
||||
static std::mutex SymbolizeMutex;
|
||||
|
||||
std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) {
|
||||
std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock);
|
||||
if (!EF->__sanitizer_symbolize_pc || !l.owns_lock())
|
||||
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);
|
||||
}
|
||||
|
||||
void PrintStackTrace() {
|
||||
std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock);
|
||||
if (EF->__sanitizer_print_stack_trace && l.owns_lock())
|
||||
EF->__sanitizer_print_stack_trace();
|
||||
}
|
||||
|
||||
void PrintMemoryProfile() {
|
||||
std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock);
|
||||
if (EF->__sanitizer_print_memory_profile && l.owns_lock())
|
||||
EF->__sanitizer_print_memory_profile(95, 8);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
size_t SimpleFastHash(const uint8_t *Data, size_t Size) {
|
||||
size_t Res = 0;
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
Res = Res * 11 + Data[i];
|
||||
return Res;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
91
contrib/compiler-rt/lib/fuzzer/FuzzerUtil.h
Normal file
91
contrib/compiler-rt/lib/fuzzer/FuzzerUtil.h
Normal file
@ -0,0 +1,91 @@
|
||||
//===- 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"
|
||||
#include "FuzzerCommand.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);
|
||||
|
||||
void PrintStackTrace();
|
||||
|
||||
void PrintMemoryProfile();
|
||||
|
||||
unsigned NumberOfCpuCores();
|
||||
|
||||
// Platform specific functions.
|
||||
void SetSignalHandler(const FuzzingOptions& Options);
|
||||
|
||||
void SleepSeconds(int Seconds);
|
||||
|
||||
unsigned long GetPid();
|
||||
|
||||
size_t GetPeakRSSMb();
|
||||
|
||||
int ExecuteCommand(const Command &Cmd);
|
||||
|
||||
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 Vector<std::string> &Args,
|
||||
const char *X1, const char *X2);
|
||||
|
||||
inline std::string CloneArgsWithoutX(const Vector<std::string> &Args,
|
||||
const char *X) {
|
||||
return CloneArgsWithoutX(Args, X, X);
|
||||
}
|
||||
|
||||
inline std::pair<std::string, std::string> SplitBefore(std::string X,
|
||||
std::string S) {
|
||||
auto Pos = S.find(X);
|
||||
if (Pos == std::string::npos)
|
||||
return std::make_pair(S, "");
|
||||
return std::make_pair(S.substr(0, Pos), S.substr(Pos));
|
||||
}
|
||||
|
||||
std::string DisassembleCmd(const std::string &FileName);
|
||||
|
||||
std::string SearchRegexCmd(const std::string &Regex);
|
||||
|
||||
size_t SimpleFastHash(const uint8_t *Data, size_t Size);
|
||||
|
||||
inline uint32_t Log(uint32_t X) { return 32 - __builtin_clz(X) - 1; }
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_UTIL_H
|
162
contrib/compiler-rt/lib/fuzzer/FuzzerUtilDarwin.cpp
Normal file
162
contrib/compiler-rt/lib/fuzzer/FuzzerUtilDarwin.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
//===- 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 "FuzzerCommand.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include <mutex>
|
||||
#include <signal.h>
|
||||
#include <spawn.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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 Command &Cmd) {
|
||||
std::string CmdLine = Cmd.toString();
|
||||
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 = CmdLine.c_str();
|
||||
char *const Argv[] = {
|
||||
strdup("sh"),
|
||||
strdup("-c"),
|
||||
strdup(CommandCStr),
|
||||
NULL
|
||||
};
|
||||
int ErrorCode = 0, ProcessStatus = 0;
|
||||
// FIXME: We probably shouldn't hardcode the shell path.
|
||||
ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes,
|
||||
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);
|
||||
}
|
||||
for (unsigned i = 0, n = sizeof(Argv) / sizeof(Argv[0]); i < n; ++i)
|
||||
free(Argv[i]);
|
||||
|
||||
// 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
|
467
contrib/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp
Normal file
467
contrib/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp
Normal file
@ -0,0 +1,467 @@
|
||||
//===- FuzzerUtilFuchsia.cpp - Misc utils for Fuchsia. --------------------===//
|
||||
//
|
||||
// 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 Fuchsia/Zircon APIs.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
#if LIBFUZZER_FUCHSIA
|
||||
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerUtil.h"
|
||||
#include <cerrno>
|
||||
#include <cinttypes>
|
||||
#include <cstdint>
|
||||
#include <fcntl.h>
|
||||
#include <lib/fdio/spawn.h>
|
||||
#include <string>
|
||||
#include <sys/select.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
#include <zircon/errors.h>
|
||||
#include <zircon/process.h>
|
||||
#include <zircon/sanitizer.h>
|
||||
#include <zircon/status.h>
|
||||
#include <zircon/syscalls.h>
|
||||
#include <zircon/syscalls/debug.h>
|
||||
#include <zircon/syscalls/exception.h>
|
||||
#include <zircon/syscalls/port.h>
|
||||
#include <zircon/types.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
// Given that Fuchsia doesn't have the POSIX signals that libFuzzer was written
|
||||
// around, the general approach is to spin up dedicated threads to watch for
|
||||
// each requested condition (alarm, interrupt, crash). Of these, the crash
|
||||
// handler is the most involved, as it requires resuming the crashed thread in
|
||||
// order to invoke the sanitizers to get the needed state.
|
||||
|
||||
// Forward declaration of assembly trampoline needed to resume crashed threads.
|
||||
// This appears to have external linkage to C++, which is why it's not in the
|
||||
// anonymous namespace. The assembly definition inside MakeTrampoline()
|
||||
// actually defines the symbol with internal linkage only.
|
||||
void CrashTrampolineAsm() __asm__("CrashTrampolineAsm");
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO(phosek): remove this and replace it with ZX_TIME_INFINITE
|
||||
#define ZX_TIME_INFINITE_OLD INT64_MAX
|
||||
|
||||
// A magic value for the Zircon exception port, chosen to spell 'FUZZING'
|
||||
// when interpreted as a byte sequence on little-endian platforms.
|
||||
const uint64_t kFuzzingCrash = 0x474e495a5a5546;
|
||||
|
||||
// Helper function to handle Zircon syscall failures.
|
||||
void ExitOnErr(zx_status_t Status, const char *Syscall) {
|
||||
if (Status != ZX_OK) {
|
||||
Printf("libFuzzer: %s failed: %s\n", Syscall,
|
||||
_zx_status_get_string(Status));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void AlarmHandler(int Seconds) {
|
||||
while (true) {
|
||||
SleepSeconds(Seconds);
|
||||
Fuzzer::StaticAlarmCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void InterruptHandler() {
|
||||
fd_set readfds;
|
||||
// Ctrl-C sends ETX in Zircon.
|
||||
do {
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(STDIN_FILENO, &readfds);
|
||||
select(STDIN_FILENO + 1, &readfds, nullptr, nullptr, nullptr);
|
||||
} while(!FD_ISSET(STDIN_FILENO, &readfds) || getchar() != 0x03);
|
||||
Fuzzer::StaticInterruptCallback();
|
||||
}
|
||||
|
||||
// For the crash handler, we need to call Fuzzer::StaticCrashSignalCallback
|
||||
// without POSIX signal handlers. To achieve this, we use an assembly function
|
||||
// to add the necessary CFI unwinding information and a C function to bridge
|
||||
// from that back into C++.
|
||||
|
||||
// FIXME: This works as a short-term solution, but this code really shouldn't be
|
||||
// architecture dependent. A better long term solution is to implement remote
|
||||
// unwinding and expose the necessary APIs through sanitizer_common and/or ASAN
|
||||
// to allow the exception handling thread to gather the crash state directly.
|
||||
//
|
||||
// Alternatively, Fuchsia may in future actually implement basic signal
|
||||
// handling for the machine trap signals.
|
||||
#if defined(__x86_64__)
|
||||
#define FOREACH_REGISTER(OP_REG, OP_NUM) \
|
||||
OP_REG(rax) \
|
||||
OP_REG(rbx) \
|
||||
OP_REG(rcx) \
|
||||
OP_REG(rdx) \
|
||||
OP_REG(rsi) \
|
||||
OP_REG(rdi) \
|
||||
OP_REG(rbp) \
|
||||
OP_REG(rsp) \
|
||||
OP_REG(r8) \
|
||||
OP_REG(r9) \
|
||||
OP_REG(r10) \
|
||||
OP_REG(r11) \
|
||||
OP_REG(r12) \
|
||||
OP_REG(r13) \
|
||||
OP_REG(r14) \
|
||||
OP_REG(r15) \
|
||||
OP_REG(rip)
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
#define FOREACH_REGISTER(OP_REG, OP_NUM) \
|
||||
OP_NUM(0) \
|
||||
OP_NUM(1) \
|
||||
OP_NUM(2) \
|
||||
OP_NUM(3) \
|
||||
OP_NUM(4) \
|
||||
OP_NUM(5) \
|
||||
OP_NUM(6) \
|
||||
OP_NUM(7) \
|
||||
OP_NUM(8) \
|
||||
OP_NUM(9) \
|
||||
OP_NUM(10) \
|
||||
OP_NUM(11) \
|
||||
OP_NUM(12) \
|
||||
OP_NUM(13) \
|
||||
OP_NUM(14) \
|
||||
OP_NUM(15) \
|
||||
OP_NUM(16) \
|
||||
OP_NUM(17) \
|
||||
OP_NUM(18) \
|
||||
OP_NUM(19) \
|
||||
OP_NUM(20) \
|
||||
OP_NUM(21) \
|
||||
OP_NUM(22) \
|
||||
OP_NUM(23) \
|
||||
OP_NUM(24) \
|
||||
OP_NUM(25) \
|
||||
OP_NUM(26) \
|
||||
OP_NUM(27) \
|
||||
OP_NUM(28) \
|
||||
OP_NUM(29) \
|
||||
OP_NUM(30) \
|
||||
OP_REG(sp)
|
||||
|
||||
#else
|
||||
#error "Unsupported architecture for fuzzing on Fuchsia"
|
||||
#endif
|
||||
|
||||
// Produces a CFI directive for the named or numbered register.
|
||||
#define CFI_OFFSET_REG(reg) ".cfi_offset " #reg ", %c[" #reg "]\n"
|
||||
#define CFI_OFFSET_NUM(num) CFI_OFFSET_REG(r##num)
|
||||
|
||||
// Produces an assembler input operand for the named or numbered register.
|
||||
#define ASM_OPERAND_REG(reg) \
|
||||
[reg] "i"(offsetof(zx_thread_state_general_regs_t, reg)),
|
||||
#define ASM_OPERAND_NUM(num) \
|
||||
[r##num] "i"(offsetof(zx_thread_state_general_regs_t, r[num])),
|
||||
|
||||
// Trampoline to bridge from the assembly below to the static C++ crash
|
||||
// callback.
|
||||
__attribute__((noreturn))
|
||||
static void StaticCrashHandler() {
|
||||
Fuzzer::StaticCrashSignalCallback();
|
||||
for (;;) {
|
||||
_Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Creates the trampoline with the necessary CFI information to unwind through
|
||||
// to the crashing call stack. The attribute is necessary because the function
|
||||
// is never called; it's just a container around the assembly to allow it to
|
||||
// use operands for compile-time computed constants.
|
||||
__attribute__((used))
|
||||
void MakeTrampoline() {
|
||||
__asm__(".cfi_endproc\n"
|
||||
".pushsection .text.CrashTrampolineAsm\n"
|
||||
".type CrashTrampolineAsm,STT_FUNC\n"
|
||||
"CrashTrampolineAsm:\n"
|
||||
".cfi_startproc simple\n"
|
||||
".cfi_signal_frame\n"
|
||||
#if defined(__x86_64__)
|
||||
".cfi_return_column rip\n"
|
||||
".cfi_def_cfa rsp, 0\n"
|
||||
FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)
|
||||
"call %c[StaticCrashHandler]\n"
|
||||
"ud2\n"
|
||||
#elif defined(__aarch64__)
|
||||
".cfi_return_column 33\n"
|
||||
".cfi_def_cfa sp, 0\n"
|
||||
".cfi_offset 33, %c[pc]\n"
|
||||
FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)
|
||||
"bl %[StaticCrashHandler]\n"
|
||||
#else
|
||||
#error "Unsupported architecture for fuzzing on Fuchsia"
|
||||
#endif
|
||||
".cfi_endproc\n"
|
||||
".size CrashTrampolineAsm, . - CrashTrampolineAsm\n"
|
||||
".popsection\n"
|
||||
".cfi_startproc\n"
|
||||
: // No outputs
|
||||
: FOREACH_REGISTER(ASM_OPERAND_REG, ASM_OPERAND_NUM)
|
||||
#if defined(__aarch64__)
|
||||
ASM_OPERAND_REG(pc)
|
||||
#endif
|
||||
[StaticCrashHandler] "i" (StaticCrashHandler));
|
||||
}
|
||||
|
||||
void CrashHandler(zx_handle_t *Event) {
|
||||
// This structure is used to ensure we close handles to objects we create in
|
||||
// this handler.
|
||||
struct ScopedHandle {
|
||||
~ScopedHandle() { _zx_handle_close(Handle); }
|
||||
zx_handle_t Handle = ZX_HANDLE_INVALID;
|
||||
};
|
||||
|
||||
// Create and bind the exception port. We need to claim to be a "debugger" so
|
||||
// the kernel will allow us to modify and resume dying threads (see below).
|
||||
// Once the port is set, we can signal the main thread to continue and wait
|
||||
// for the exception to arrive.
|
||||
ScopedHandle Port;
|
||||
ExitOnErr(_zx_port_create(0, &Port.Handle), "_zx_port_create");
|
||||
zx_handle_t Self = _zx_process_self();
|
||||
|
||||
ExitOnErr(_zx_task_bind_exception_port(Self, Port.Handle, kFuzzingCrash,
|
||||
ZX_EXCEPTION_PORT_DEBUGGER),
|
||||
"_zx_task_bind_exception_port");
|
||||
|
||||
ExitOnErr(_zx_object_signal(*Event, 0, ZX_USER_SIGNAL_0),
|
||||
"_zx_object_signal");
|
||||
|
||||
zx_port_packet_t Packet;
|
||||
ExitOnErr(_zx_port_wait(Port.Handle, ZX_TIME_INFINITE_OLD, &Packet),
|
||||
"_zx_port_wait");
|
||||
|
||||
// At this point, we want to get the state of the crashing thread, but
|
||||
// libFuzzer and the sanitizers assume this will happen from that same thread
|
||||
// via a POSIX signal handler. "Resurrecting" the thread in the middle of the
|
||||
// appropriate callback is as simple as forcibly setting the instruction
|
||||
// pointer/program counter, provided we NEVER EVER return from that function
|
||||
// (since otherwise our stack will not be valid).
|
||||
ScopedHandle Thread;
|
||||
ExitOnErr(_zx_object_get_child(Self, Packet.exception.tid,
|
||||
ZX_RIGHT_SAME_RIGHTS, &Thread.Handle),
|
||||
"_zx_object_get_child");
|
||||
|
||||
zx_thread_state_general_regs_t GeneralRegisters;
|
||||
ExitOnErr(_zx_thread_read_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS,
|
||||
&GeneralRegisters, sizeof(GeneralRegisters)),
|
||||
"_zx_thread_read_state");
|
||||
|
||||
// To unwind properly, we need to push the crashing thread's register state
|
||||
// onto the stack and jump into a trampoline with CFI instructions on how
|
||||
// to restore it.
|
||||
#if defined(__x86_64__)
|
||||
uintptr_t StackPtr =
|
||||
(GeneralRegisters.rsp - (128 + sizeof(GeneralRegisters))) &
|
||||
-(uintptr_t)16;
|
||||
__unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters,
|
||||
sizeof(GeneralRegisters));
|
||||
GeneralRegisters.rsp = StackPtr;
|
||||
GeneralRegisters.rip = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm);
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
uintptr_t StackPtr =
|
||||
(GeneralRegisters.sp - sizeof(GeneralRegisters)) & -(uintptr_t)16;
|
||||
__unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters,
|
||||
sizeof(GeneralRegisters));
|
||||
GeneralRegisters.sp = StackPtr;
|
||||
GeneralRegisters.pc = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm);
|
||||
|
||||
#else
|
||||
#error "Unsupported architecture for fuzzing on Fuchsia"
|
||||
#endif
|
||||
|
||||
// Now force the crashing thread's state.
|
||||
ExitOnErr(_zx_thread_write_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS,
|
||||
&GeneralRegisters, sizeof(GeneralRegisters)),
|
||||
"_zx_thread_write_state");
|
||||
|
||||
ExitOnErr(_zx_task_resume_from_exception(Thread.Handle, Port.Handle, 0),
|
||||
"_zx_task_resume_from_exception");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Platform specific functions.
|
||||
void SetSignalHandler(const FuzzingOptions &Options) {
|
||||
// Set up alarm handler if needed.
|
||||
if (Options.UnitTimeoutSec > 0) {
|
||||
std::thread T(AlarmHandler, Options.UnitTimeoutSec / 2 + 1);
|
||||
T.detach();
|
||||
}
|
||||
|
||||
// Set up interrupt handler if needed.
|
||||
if (Options.HandleInt || Options.HandleTerm) {
|
||||
std::thread T(InterruptHandler);
|
||||
T.detach();
|
||||
}
|
||||
|
||||
// Early exit if no crash handler needed.
|
||||
if (!Options.HandleSegv && !Options.HandleBus && !Options.HandleIll &&
|
||||
!Options.HandleFpe && !Options.HandleAbrt)
|
||||
return;
|
||||
|
||||
// Set up the crash handler and wait until it is ready before proceeding.
|
||||
zx_handle_t Event;
|
||||
ExitOnErr(_zx_event_create(0, &Event), "_zx_event_create");
|
||||
|
||||
std::thread T(CrashHandler, &Event);
|
||||
zx_status_t Status = _zx_object_wait_one(Event, ZX_USER_SIGNAL_0,
|
||||
ZX_TIME_INFINITE_OLD, nullptr);
|
||||
_zx_handle_close(Event);
|
||||
ExitOnErr(Status, "_zx_object_wait_one");
|
||||
|
||||
T.detach();
|
||||
}
|
||||
|
||||
void SleepSeconds(int Seconds) {
|
||||
_zx_nanosleep(_zx_deadline_after(ZX_SEC(Seconds)));
|
||||
}
|
||||
|
||||
unsigned long GetPid() {
|
||||
zx_status_t rc;
|
||||
zx_info_handle_basic_t Info;
|
||||
if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info,
|
||||
sizeof(Info), NULL, NULL)) != ZX_OK) {
|
||||
Printf("libFuzzer: unable to get info about self: %s\n",
|
||||
_zx_status_get_string(rc));
|
||||
exit(1);
|
||||
}
|
||||
return Info.koid;
|
||||
}
|
||||
|
||||
size_t GetPeakRSSMb() {
|
||||
zx_status_t rc;
|
||||
zx_info_task_stats_t Info;
|
||||
if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_TASK_STATS, &Info,
|
||||
sizeof(Info), NULL, NULL)) != ZX_OK) {
|
||||
Printf("libFuzzer: unable to get info about self: %s\n",
|
||||
_zx_status_get_string(rc));
|
||||
exit(1);
|
||||
}
|
||||
return (Info.mem_private_bytes + Info.mem_shared_bytes) >> 20;
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
class RunOnDestruction {
|
||||
public:
|
||||
explicit RunOnDestruction(Fn fn) : fn_(fn) {}
|
||||
~RunOnDestruction() { fn_(); }
|
||||
|
||||
private:
|
||||
Fn fn_;
|
||||
};
|
||||
|
||||
template <typename Fn>
|
||||
RunOnDestruction<Fn> at_scope_exit(Fn fn) {
|
||||
return RunOnDestruction<Fn>(fn);
|
||||
}
|
||||
|
||||
int ExecuteCommand(const Command &Cmd) {
|
||||
zx_status_t rc;
|
||||
|
||||
// Convert arguments to C array
|
||||
auto Args = Cmd.getArguments();
|
||||
size_t Argc = Args.size();
|
||||
assert(Argc != 0);
|
||||
std::unique_ptr<const char *[]> Argv(new const char *[Argc + 1]);
|
||||
for (size_t i = 0; i < Argc; ++i)
|
||||
Argv[i] = Args[i].c_str();
|
||||
Argv[Argc] = nullptr;
|
||||
|
||||
// Determine stdout
|
||||
int FdOut = STDOUT_FILENO;
|
||||
|
||||
if (Cmd.hasOutputFile()) {
|
||||
auto Filename = Cmd.getOutputFile();
|
||||
FdOut = open(Filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0);
|
||||
if (FdOut == -1) {
|
||||
Printf("libFuzzer: failed to open %s: %s\n", Filename.c_str(),
|
||||
strerror(errno));
|
||||
return ZX_ERR_IO;
|
||||
}
|
||||
}
|
||||
auto CloseFdOut = at_scope_exit([&]() { close(FdOut); } );
|
||||
|
||||
// Determine stderr
|
||||
int FdErr = STDERR_FILENO;
|
||||
if (Cmd.isOutAndErrCombined())
|
||||
FdErr = FdOut;
|
||||
|
||||
// Clone the file descriptors into the new process
|
||||
fdio_spawn_action_t SpawnAction[] = {
|
||||
{
|
||||
.action = FDIO_SPAWN_ACTION_CLONE_FD,
|
||||
.fd =
|
||||
{
|
||||
.local_fd = STDIN_FILENO,
|
||||
.target_fd = STDIN_FILENO,
|
||||
},
|
||||
},
|
||||
{
|
||||
.action = FDIO_SPAWN_ACTION_CLONE_FD,
|
||||
.fd =
|
||||
{
|
||||
.local_fd = FdOut,
|
||||
.target_fd = STDOUT_FILENO,
|
||||
},
|
||||
},
|
||||
{
|
||||
.action = FDIO_SPAWN_ACTION_CLONE_FD,
|
||||
.fd =
|
||||
{
|
||||
.local_fd = FdErr,
|
||||
.target_fd = STDERR_FILENO,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Start the process.
|
||||
char ErrorMsg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
|
||||
zx_handle_t ProcessHandle = ZX_HANDLE_INVALID;
|
||||
rc = fdio_spawn_etc(
|
||||
ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL & (~FDIO_SPAWN_CLONE_STDIO),
|
||||
Argv[0], Argv.get(), nullptr, 3, SpawnAction, &ProcessHandle, ErrorMsg);
|
||||
if (rc != ZX_OK) {
|
||||
Printf("libFuzzer: failed to launch '%s': %s, %s\n", Argv[0], ErrorMsg,
|
||||
_zx_status_get_string(rc));
|
||||
return rc;
|
||||
}
|
||||
auto CloseHandle = at_scope_exit([&]() { _zx_handle_close(ProcessHandle); });
|
||||
|
||||
// Now join the process and return the exit status.
|
||||
if ((rc = _zx_object_wait_one(ProcessHandle, ZX_PROCESS_TERMINATED,
|
||||
ZX_TIME_INFINITE_OLD, nullptr)) != ZX_OK) {
|
||||
Printf("libFuzzer: failed to join '%s': %s\n", Argv[0],
|
||||
_zx_status_get_string(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
zx_info_process_t Info;
|
||||
if ((rc = _zx_object_get_info(ProcessHandle, ZX_INFO_PROCESS, &Info,
|
||||
sizeof(Info), nullptr, nullptr)) != ZX_OK) {
|
||||
Printf("libFuzzer: unable to get return code from '%s': %s\n", Argv[0],
|
||||
_zx_status_get_string(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
return Info.return_code;
|
||||
}
|
||||
|
||||
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_FUCHSIA
|
27
contrib/compiler-rt/lib/fuzzer/FuzzerUtilLinux.cpp
Normal file
27
contrib/compiler-rt/lib/fuzzer/FuzzerUtilLinux.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
//===- 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 || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \
|
||||
LIBFUZZER_OPENBSD
|
||||
#include "FuzzerCommand.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
int ExecuteCommand(const Command &Cmd) {
|
||||
std::string CmdLine = Cmd.toString();
|
||||
return system(CmdLine.c_str());
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif
|
152
contrib/compiler-rt/lib/fuzzer/FuzzerUtilPosix.cpp
Normal file
152
contrib/compiler-rt/lib/fuzzer/FuzzerUtilPosix.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
//===- 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 <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 GracefulExitHandler(int, siginfo_t *, void *) {
|
||||
Fuzzer::StaticGracefulExitCallback();
|
||||
}
|
||||
|
||||
static void FileSizeExceedHandler(int, siginfo_t *, void *) {
|
||||
Fuzzer::StaticFileSizeExceedCallback();
|
||||
}
|
||||
|
||||
static void SetSigaction(int signum,
|
||||
void (*callback)(int, siginfo_t *, void *)) {
|
||||
struct sigaction sigact = {};
|
||||
if (sigaction(signum, nullptr, &sigact)) {
|
||||
Printf("libFuzzer: sigaction failed with %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
if (sigact.sa_flags & SA_SIGINFO) {
|
||||
if (sigact.sa_sigaction)
|
||||
return;
|
||||
} else {
|
||||
if (sigact.sa_handler != SIG_DFL && sigact.sa_handler != SIG_IGN &&
|
||||
sigact.sa_handler != SIG_ERR)
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
if (Options.HandleXfsz)
|
||||
SetSigaction(SIGXFSZ, FileSizeExceedHandler);
|
||||
if (Options.HandleUsr1)
|
||||
SetSigaction(SIGUSR1, GracefulExitHandler);
|
||||
if (Options.HandleUsr2)
|
||||
SetSigaction(SIGUSR2, GracefulExitHandler);
|
||||
}
|
||||
|
||||
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 || LIBFUZZER_FREEBSD || LIBFUZZER_NETBSD ||
|
||||
LIBFUZZER_OPENBSD) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
std::string DisassembleCmd(const std::string &FileName) {
|
||||
return "objdump -d " + FileName;
|
||||
}
|
||||
|
||||
std::string SearchRegexCmd(const std::string &Regex) {
|
||||
return "grep '" + Regex + "'";
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_POSIX
|
194
contrib/compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp
Normal file
194
contrib/compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp
Normal file
@ -0,0 +1,194 @@
|
||||
//===- 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 "FuzzerCommand.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <iomanip>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <windows.h>
|
||||
|
||||
// This must be included after windows.h.
|
||||
#include <Psapi.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
static const FuzzingOptions* HandlerOpt = nullptr;
|
||||
|
||||
static 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;
|
||||
// TODO: handle (Options.HandleXfsz)
|
||||
}
|
||||
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)
|
||||
SetUnhandledExceptionFilter(ExceptionHandler);
|
||||
|
||||
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 Command &Cmd) {
|
||||
std::string CmdLine = Cmd.toString();
|
||||
return system(CmdLine.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;
|
||||
}
|
||||
|
||||
std::string DisassembleCmd(const std::string &FileName) {
|
||||
if (ExecuteCommand("dumpbin /summary > nul") == 0)
|
||||
return "dumpbin /disasm " + FileName;
|
||||
Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::string SearchRegexCmd(const std::string &Regex) {
|
||||
return "findstr /r \"" + Regex + "\"";
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
73
contrib/compiler-rt/lib/fuzzer/FuzzerValueBitMap.h
Normal file
73
contrib/compiler-rt/lib/fuzzer/FuzzerValueBitMap.h
Normal file
@ -0,0 +1,73 @@
|
||||
//===- 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 = 1 << 16;
|
||||
static const size_t kMapPrimeMod = 65371; // Largest Prime < kMapSizeInBits;
|
||||
static const size_t kBitsInWord = (sizeof(uintptr_t) * 8);
|
||||
static const size_t kMapSizeInWords = kMapSizeInBits / kBitsInWord;
|
||||
public:
|
||||
|
||||
// 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.
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
inline bool AddValue(uintptr_t Value) {
|
||||
uintptr_t Idx = 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;
|
||||
}
|
||||
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
inline bool AddValueModPrime(uintptr_t Value) {
|
||||
return AddValue(Value % kMapPrimeMod);
|
||||
}
|
||||
|
||||
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 SizeInBits() const { return kMapSizeInBits; }
|
||||
|
||||
template <class Callback>
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void ForEach(Callback CB) const {
|
||||
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:
|
||||
uintptr_t Map[kMapSizeInWords] __attribute__((aligned(512)));
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_VALUE_BIT_MAP_H
|
1
contrib/compiler-rt/lib/fuzzer/README.txt
Normal file
1
contrib/compiler-rt/lib/fuzzer/README.txt
Normal file
@ -0,0 +1 @@
|
||||
See http://llvm.org/docs/LibFuzzer.html
|
33
lib/libclang_rt/fuzzer/Makefile
Normal file
33
lib/libclang_rt/fuzzer/Makefile
Normal file
@ -0,0 +1,33 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.include <bsd.init.mk>
|
||||
|
||||
LIB= clang_rt.fuzzer-${CRTARCH}
|
||||
|
||||
SRCS+= fuzzer/FuzzerCrossOver.cpp
|
||||
SRCS+= fuzzer/FuzzerDataFlowTrace.cpp
|
||||
SRCS+= fuzzer/FuzzerDriver.cpp
|
||||
SRCS+= fuzzer/FuzzerExtFunctionsDlsym.cpp
|
||||
SRCS+= fuzzer/FuzzerExtFunctionsDlsymWin.cpp
|
||||
SRCS+= fuzzer/FuzzerExtFunctionsWeak.cpp
|
||||
SRCS+= fuzzer/FuzzerExtraCounters.cpp
|
||||
SRCS+= fuzzer/FuzzerIO.cpp
|
||||
SRCS+= fuzzer/FuzzerIOPosix.cpp
|
||||
SRCS+= fuzzer/FuzzerIOWindows.cpp
|
||||
SRCS+= fuzzer/FuzzerLoop.cpp
|
||||
SRCS+= fuzzer/FuzzerMain.cpp
|
||||
SRCS+= fuzzer/FuzzerMerge.cpp
|
||||
SRCS+= fuzzer/FuzzerMutate.cpp
|
||||
SRCS+= fuzzer/FuzzerSHA1.cpp
|
||||
SRCS+= fuzzer/FuzzerShmemFuchsia.cpp
|
||||
SRCS+= fuzzer/FuzzerShmemPosix.cpp
|
||||
SRCS+= fuzzer/FuzzerShmemWindows.cpp
|
||||
SRCS+= fuzzer/FuzzerTracePC.cpp
|
||||
SRCS+= fuzzer/FuzzerUtil.cpp
|
||||
SRCS+= fuzzer/FuzzerUtilDarwin.cpp
|
||||
SRCS+= fuzzer/FuzzerUtilFuchsia.cpp
|
||||
SRCS+= fuzzer/FuzzerUtilLinux.cpp
|
||||
SRCS+= fuzzer/FuzzerUtilPosix.cpp
|
||||
SRCS+= fuzzer/FuzzerUtilWindows.cpp
|
||||
|
||||
.include <bsd.lib.mk>
|
32
lib/libclang_rt/fuzzer_no_main/Makefile
Normal file
32
lib/libclang_rt/fuzzer_no_main/Makefile
Normal file
@ -0,0 +1,32 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.include <bsd.init.mk>
|
||||
|
||||
LIB= clang_rt.fuzzer_no_main-${CRTARCH}
|
||||
|
||||
SRCS+= fuzzer/FuzzerCrossOver.cpp
|
||||
SRCS+= fuzzer/FuzzerDataFlowTrace.cpp
|
||||
SRCS+= fuzzer/FuzzerDriver.cpp
|
||||
SRCS+= fuzzer/FuzzerExtFunctionsDlsym.cpp
|
||||
SRCS+= fuzzer/FuzzerExtFunctionsDlsymWin.cpp
|
||||
SRCS+= fuzzer/FuzzerExtFunctionsWeak.cpp
|
||||
SRCS+= fuzzer/FuzzerExtraCounters.cpp
|
||||
SRCS+= fuzzer/FuzzerIO.cpp
|
||||
SRCS+= fuzzer/FuzzerIOPosix.cpp
|
||||
SRCS+= fuzzer/FuzzerIOWindows.cpp
|
||||
SRCS+= fuzzer/FuzzerLoop.cpp
|
||||
SRCS+= fuzzer/FuzzerMerge.cpp
|
||||
SRCS+= fuzzer/FuzzerMutate.cpp
|
||||
SRCS+= fuzzer/FuzzerSHA1.cpp
|
||||
SRCS+= fuzzer/FuzzerShmemFuchsia.cpp
|
||||
SRCS+= fuzzer/FuzzerShmemPosix.cpp
|
||||
SRCS+= fuzzer/FuzzerShmemWindows.cpp
|
||||
SRCS+= fuzzer/FuzzerTracePC.cpp
|
||||
SRCS+= fuzzer/FuzzerUtil.cpp
|
||||
SRCS+= fuzzer/FuzzerUtilDarwin.cpp
|
||||
SRCS+= fuzzer/FuzzerUtilFuchsia.cpp
|
||||
SRCS+= fuzzer/FuzzerUtilLinux.cpp
|
||||
SRCS+= fuzzer/FuzzerUtilPosix.cpp
|
||||
SRCS+= fuzzer/FuzzerUtilWindows.cpp
|
||||
|
||||
.include <bsd.lib.mk>
|
Loading…
Reference in New Issue
Block a user