diff --git a/lib/Fuzzer/CMakeLists.txt b/lib/Fuzzer/CMakeLists.txt index 49d7f8fa5e8..8b4d61905d0 100644 --- a/lib/Fuzzer/CMakeLists.txt +++ b/lib/Fuzzer/CMakeLists.txt @@ -2,8 +2,9 @@ set(LIBFUZZER_FLAGS_BASE "${CMAKE_CXX_FLAGS_RELEASE}") # Disable the coverage and sanitizer instrumentation for the fuzzer itself. set(CMAKE_CXX_FLAGS_RELEASE "${LIBFUZZER_FLAGS_BASE} -O2 -fno-sanitize=all") if( LLVM_USE_SANITIZE_COVERAGE ) - add_library(LLVMFuzzerNoMain OBJECT + add_library(LLVMFuzzerNoMainObjects OBJECT FuzzerCrossOver.cpp + FuzzerInterface.cpp FuzzerTraceState.cpp FuzzerDriver.cpp FuzzerIO.cpp @@ -13,9 +14,12 @@ if( LLVM_USE_SANITIZE_COVERAGE ) FuzzerSHA1.cpp FuzzerUtil.cpp ) + add_library(LLVMFuzzerNoMain STATIC + $ + ) add_library(LLVMFuzzer STATIC FuzzerMain.cpp - $ + $ ) if( LLVM_INCLUDE_TESTS ) diff --git a/lib/Fuzzer/FuzzerCrossOver.cpp b/lib/Fuzzer/FuzzerCrossOver.cpp index 94af6d547ed..f03a94a54dd 100644 --- a/lib/Fuzzer/FuzzerCrossOver.cpp +++ b/lib/Fuzzer/FuzzerCrossOver.cpp @@ -9,39 +9,42 @@ // Cross over test inputs. //===----------------------------------------------------------------------===// +#include + #include "FuzzerInternal.h" -#include namespace fuzzer { -// Cross A and B, store the result (ap to MaxLen bytes) in U. -void CrossOver(const Unit &A, const Unit &B, Unit *U, size_t MaxLen) { - size_t Size = rand() % MaxLen + 1; - U->clear(); - const Unit *V = &A; - size_t PosA = 0; - size_t PosB = 0; - size_t *Pos = &PosA; - while (U->size() < Size && (PosA < A.size() || PosB < B.size())) { - // Merge a part of V into U. - size_t SizeLeftU = Size - U->size(); - if (*Pos < V->size()) { - size_t SizeLeftV = V->size() - *Pos; - size_t MaxExtraSize = std::min(SizeLeftU, SizeLeftV); +// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out. +size_t CrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize) { + 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; - U->insert(U->end(), V->begin() + *Pos, V->begin() + *Pos + ExtraSize); - (*Pos) += ExtraSize; - } - - // Use the other Unit on the next iteration. - if (Pos == &PosA) { - Pos = &PosB; - V = &B; - } else { - Pos = &PosA; - V = &A; + 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 diff --git a/lib/Fuzzer/FuzzerDriver.cpp b/lib/Fuzzer/FuzzerDriver.cpp index edfe09e8731..780b615d714 100644 --- a/lib/Fuzzer/FuzzerDriver.cpp +++ b/lib/Fuzzer/FuzzerDriver.cpp @@ -204,6 +204,11 @@ int ApplyTokens(const Fuzzer &F, const char *InputFilePath) { } int FuzzerDriver(int argc, char **argv, UserCallback Callback) { + SimpleUserSuppliedFuzzer SUSF(Callback); + return FuzzerDriver(argc, argv, SUSF); +} + +int FuzzerDriver(int argc, char **argv, UserSuppliedFuzzer &USF) { using namespace fuzzer; ProgName = argv[0]; @@ -244,7 +249,7 @@ int FuzzerDriver(int argc, char **argv, UserCallback Callback) { if (Flags.sync_command) Options.SyncCommand = Flags.sync_command; Options.SyncTimeout = Flags.sync_timeout; - Fuzzer F(Callback, Options); + Fuzzer F(USF, Options); unsigned seed = Flags.seed; // Initialize seed. diff --git a/lib/Fuzzer/FuzzerInterface.cpp b/lib/Fuzzer/FuzzerInterface.cpp new file mode 100644 index 00000000000..dcd4e746013 --- /dev/null +++ b/lib/Fuzzer/FuzzerInterface.cpp @@ -0,0 +1,27 @@ +//===- FuzzerInterface.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. +// +//===----------------------------------------------------------------------===// +// Parts of public interface for libFuzzer. +//===----------------------------------------------------------------------===// + + +#include "FuzzerInterface.h" +#include "FuzzerInternal.h" + +namespace fuzzer { +size_t UserSuppliedFuzzer::BasicMutate(uint8_t *Data, size_t Size, + size_t MaxSize) { + return ::fuzzer::Mutate(Data, Size, MaxSize); +} +size_t UserSuppliedFuzzer::BasicCrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize) { + return ::fuzzer::CrossOver(Data1, Size1, Data2, Size2, Out, MaxOutSize); +} + +} // namespace fuzzer. diff --git a/lib/Fuzzer/FuzzerInterface.h b/lib/Fuzzer/FuzzerInterface.h index 49d8c0f6145..d4b0f99c993 100644 --- a/lib/Fuzzer/FuzzerInterface.h +++ b/lib/Fuzzer/FuzzerInterface.h @@ -9,6 +9,10 @@ // Define the interface between the Fuzzer and the library being tested. //===----------------------------------------------------------------------===// +// WARNING: keep the interface free of STL or any other header-based C++ lib, +// to avoid bad interactions between the code used in the fuzzer and +// the code used in the target function. + #ifndef LLVM_FUZZER_INTERFACE_H #define LLVM_FUZZER_INTERFACE_H @@ -17,9 +21,69 @@ namespace fuzzer { -typedef void (*UserCallback)(const uint8_t *data, size_t size); +// Simple C-like interface with a single user-supplied callback. +/* Usage: --------------------------------------------------------------------- +#include "FuzzerInterface.h" + +void LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + DoStuffWithData(Data, Size); +} + +// Implement your own main() or use the one from FuzzerMain.cpp. +int main(int argc, char **argv) { + InitializeMeIfNeeded(); + return fuzzer::FuzzerDriver(argc, argv, LLVMFuzzerTestOneInput); +} +----------------------------------------------------------------------------- */ +typedef void (*UserCallback)(const uint8_t *Data, size_t Size); int FuzzerDriver(int argc, char **argv, UserCallback Callback); +// An abstract class that allows to use user-supplied mutators with libFuzzer. +/* Usage: --------------------------------------------------------------------- +#include "FuzzerInterface.h" +class MyFuzzer : public fuzzer::UserSuppliedFuzzer { + public: + // Must define the target function. + void TargetFunction(...) { ... } + // Optionally define the mutator. + size_t Mutate(...) { ... } + // Optionally define the CrossOver method. + size_t CrossOver(...) { ... } +}; + +int main(int argc, char **argv) { + MyFuzzer F; + fuzzer::FuzzerDriver(argc, argv, F); +} +----------------------------------------------------------------------------- */ +class UserSuppliedFuzzer { + public: + // Executes the target function on 'Size' bytes of 'Data'. + virtual void TargetFunction(const uint8_t *Data, size_t Size) = 0; + // Mutates 'Size' bytes of data in 'Data' inplace into up to 'MaxSize' bytes, + // returns the new size of the data. + virtual size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize) { + return BasicMutate(Data, Size, MaxSize); + } + // Crosses 'Data1' and 'Data2', writes up to 'MaxOutSize' bytes into Out, + // returns the number of bytes written. + virtual size_t CrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize) { + return BasicCrossOver(Data1, Size1, Data2, Size2, Out, MaxOutSize); + } + virtual ~UserSuppliedFuzzer() {} + + protected: + // These can be called internally by Mutate and CrossOver. + size_t BasicMutate(uint8_t *Data, size_t Size, size_t MaxSize); + size_t BasicCrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize); +}; + +int FuzzerDriver(int argc, char **argv, UserSuppliedFuzzer &USF); + } // namespace fuzzer #endif // LLVM_FUZZER_INTERFACE_H diff --git a/lib/Fuzzer/FuzzerInternal.h b/lib/Fuzzer/FuzzerInternal.h index b0c27b0da33..b25449b2c5f 100644 --- a/lib/Fuzzer/FuzzerInternal.h +++ b/lib/Fuzzer/FuzzerInternal.h @@ -33,9 +33,10 @@ void CopyFileToErr(const std::string &Path); std::string DirPlusFile(const std::string &DirPath, const std::string &FileName); -void Mutate(Unit *U, size_t MaxLen); +size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize); -void CrossOver(const Unit &A, const Unit &B, Unit *U, size_t MaxLen); +size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2, + size_t Size2, uint8_t *Out, size_t MaxOutSize); void Print(const Unit &U, const char *PrintAfter = ""); void PrintASCII(const Unit &U, const char *PrintAfter = ""); @@ -72,7 +73,7 @@ class Fuzzer { std::string SyncCommand; std::vector Tokens; }; - Fuzzer(UserCallback Callback, FuzzingOptions Options); + Fuzzer(UserSuppliedFuzzer &USF, FuzzingOptions Options); void AddToCorpus(const Unit &U) { Corpus.push_back(U); } void Loop(size_t NumIterations); void ShuffleAndMinimize(); @@ -144,7 +145,7 @@ class Fuzzer { return Res; } - UserCallback Callback; + UserSuppliedFuzzer &USF; FuzzingOptions Options; system_clock::time_point ProcessStartTime = system_clock::now(); system_clock::time_point LastExternalSync = system_clock::now(); @@ -153,4 +154,15 @@ class Fuzzer { long EpochOfLastReadOfOutputCorpus = 0; }; +class SimpleUserSuppliedFuzzer: public UserSuppliedFuzzer { + public: + SimpleUserSuppliedFuzzer(UserCallback Callback) : Callback(Callback) {} + virtual void TargetFunction(const uint8_t *Data, size_t Size) { + return Callback(Data, Size); + } + + private: + UserCallback Callback; +}; + }; // namespace fuzzer diff --git a/lib/Fuzzer/FuzzerLoop.cpp b/lib/Fuzzer/FuzzerLoop.cpp index 696811b4511..2e4da1da91b 100644 --- a/lib/Fuzzer/FuzzerLoop.cpp +++ b/lib/Fuzzer/FuzzerLoop.cpp @@ -19,8 +19,8 @@ namespace fuzzer { // Only one Fuzzer per process. static Fuzzer *F; -Fuzzer::Fuzzer(UserCallback Callback, FuzzingOptions Options) - : Callback(Callback), Options(Options) { +Fuzzer::Fuzzer(UserSuppliedFuzzer &USF, FuzzingOptions Options) + : USF(USF), Options(Options) { SetDeathCallback(); InitializeTraceState(); assert(!F); @@ -207,10 +207,10 @@ Unit Fuzzer::SubstituteTokens(const Unit &U) const { void Fuzzer::ExecuteCallback(const Unit &U) { if (Options.Tokens.empty()) { - Callback(U.data(), U.size()); + USF.TargetFunction(U.data(), U.size()); } else { auto T = SubstituteTokens(U); - Callback(T.data(), T.size()); + USF.TargetFunction(T.data(), T.size()); } } @@ -321,7 +321,11 @@ void Fuzzer::ReportNewCoverage(size_t NewCoverage, const Unit &U) { void Fuzzer::MutateAndTestOne(Unit *U) { for (int i = 0; i < Options.MutateDepth; i++) { StartTraceRecording(); - Mutate(U, Options.MaxLen); + size_t Size = U->size(); + U->resize(Options.MaxLen); + size_t NewSize = USF.Mutate(U->data(), Size, U->size()); + assert(NewSize > 0 && NewSize <= Options.MaxLen); + U->resize(NewSize); RunOneAndUpdateCorpus(*U); size_t NumTraceBasedMutations = StopTraceRecording(); for (size_t j = 0; j < NumTraceBasedMutations; j++) { @@ -344,8 +348,12 @@ void Fuzzer::Loop(size_t NumIterations) { // Now, cross with others. if (Options.DoCrossOver) { for (size_t J2 = 0; J2 < Corpus.size(); J2++) { - CurrentUnit.clear(); - CrossOver(Corpus[J1], Corpus[J2], &CurrentUnit, Options.MaxLen); + CurrentUnit.resize(Options.MaxLen); + size_t NewSize = USF.CrossOver( + Corpus[J1].data(), Corpus[J1].size(), Corpus[J2].data(), + Corpus[J2].size(), CurrentUnit.data(), CurrentUnit.size()); + assert(NewSize > 0 && NewSize <= Options.MaxLen); + CurrentUnit.resize(NewSize); MutateAndTestOne(&CurrentUnit); } } diff --git a/lib/Fuzzer/FuzzerMutate.cpp b/lib/Fuzzer/FuzzerMutate.cpp index b28264ac8c1..f537fa90fd8 100644 --- a/lib/Fuzzer/FuzzerMutate.cpp +++ b/lib/Fuzzer/FuzzerMutate.cpp @@ -9,6 +9,8 @@ // Mutate a test input. //===----------------------------------------------------------------------===// +#include + #include "FuzzerInternal.h" namespace fuzzer { @@ -31,40 +33,39 @@ static char RandCh() { return Special[rand() % (sizeof(Special) - 1)]; } -// Mutate U in place. -void Mutate(Unit *U, size_t MaxLen) { - assert(MaxLen > 0); - assert(U->size() <= MaxLen); - if (U->empty()) { - for (size_t i = 0; i < MaxLen; i++) - U->push_back(RandCh()); - return; +// Mutates Data in place, returns new size. +size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize) { + assert(MaxSize > 0); + assert(Size <= MaxSize); + if (Size == 0) { + for (size_t i = 0; i < MaxSize; i++) + Data[i] = RandCh(); + return MaxSize; } - assert(!U->empty()); + assert(Size > 0); + size_t Idx = rand() % Size; switch (rand() % 3) { case 0: - if (U->size() > 1) { - U->erase(U->begin() + rand() % U->size()); - break; + if (Size > 1) { + // Erase Data[Idx]. + memmove(Data + Idx, Data + Idx + 1, Size - Idx - 1); + Size = Size - 1; } [[clang::fallthrough]]; case 1: - if (U->size() < MaxLen) { - U->insert(U->begin() + rand() % U->size(), RandCh()); - } else { // At MaxLen. - uint8_t Ch = RandCh(); - size_t Idx = rand() % U->size(); - (*U)[Idx] = Ch; + if (Size < MaxSize) { + // Insert new value at Data[Idx]. + memmove(Data + Idx + 1, Data + Idx, Size - Idx); + Data[Idx] = RandCh(); } + Data[Idx] = RandCh(); break; - default: - { - size_t Idx = rand() % U->size(); - (*U)[Idx] = FlipRandomBit((*U)[Idx]); - } + case 2: + Data[Idx] = FlipRandomBit(Data[Idx]); break; } - assert(!U->empty()); + assert(Size > 0); + return Size; } } // namespace fuzzer diff --git a/lib/Fuzzer/test/CMakeLists.txt b/lib/Fuzzer/test/CMakeLists.txt index fc663883ea1..a9acec15d4d 100644 --- a/lib/Fuzzer/test/CMakeLists.txt +++ b/lib/Fuzzer/test/CMakeLists.txt @@ -21,6 +21,10 @@ set(Tests ${DFSanTests} ) +set(CustomMainTests + UserSuppliedFuzzerTest + ) + set(TestBinaries) @@ -34,6 +38,17 @@ foreach(Test ${Tests}) set(TestBinaries ${TestBinaries} LLVMFuzzer-${Test}) endforeach() +foreach(Test ${CustomMainTests}) + add_executable(LLVMFuzzer-${Test} + ${Test}.cpp + ) + target_link_libraries(LLVMFuzzer-${Test} + LLVMFuzzerNoMain + ) + set(TestBinaries ${TestBinaries} LLVMFuzzer-${Test}) +endforeach() + + configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg @@ -49,7 +64,7 @@ include_directories(${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include) add_executable(LLVMFuzzer-Unittest FuzzerUnittest.cpp - $ + $ ) target_link_libraries(LLVMFuzzer-Unittest diff --git a/lib/Fuzzer/test/FuzzerUnittest.cpp b/lib/Fuzzer/test/FuzzerUnittest.cpp index 7b429b722b6..50f2f99760e 100644 --- a/lib/Fuzzer/test/FuzzerUnittest.cpp +++ b/lib/Fuzzer/test/FuzzerUnittest.cpp @@ -51,7 +51,10 @@ TEST(Fuzzer, CrossOver) { for (size_t Len = 1; Len < 8; Len++) { std::set FoundUnits, ExpectedUnitsWitThisLength; for (int Iter = 0; Iter < 3000; Iter++) { - CrossOver(A, B, &C, Len); + C.resize(Len); + size_t NewSize = CrossOver(A.data(), A.size(), B.data(), B.size(), + C.data(), C.size()); + C.resize(NewSize); FoundUnits.insert(C); } for (const Unit &U : Expected) diff --git a/lib/Fuzzer/test/UserSuppliedFuzzerTest.cpp b/lib/Fuzzer/test/UserSuppliedFuzzerTest.cpp new file mode 100644 index 00000000000..b46313dbafb --- /dev/null +++ b/lib/Fuzzer/test/UserSuppliedFuzzerTest.cpp @@ -0,0 +1,47 @@ +// Simple test for a fuzzer. +// The fuzzer must find the string "Hi!" preceded by a magic value. +// Uses UserSuppliedFuzzer which ensures that the magic is present. +#include +#include +#include +#include +#include +#include + +#include "FuzzerInterface.h" + +static const uint64_t kMagic = 8860221463604ULL; + +class MyFuzzer : public fuzzer::UserSuppliedFuzzer { + public: + void TargetFunction(const uint8_t *Data, size_t Size) { + if (Size <= 10) return; + if (memcmp(Data, &kMagic, sizeof(kMagic))) return; + // It's hard to get here w/o advanced fuzzing techniques (e.g. cmp tracing). + // So, we simply 'fix' the data in the custom mutator. + if (Data[8] == 'H') { + if (Data[9] == 'i') { + if (Data[10] == '!') { + std::cout << "BINGO; Found the target, exiting\n"; + exit(1); + } + } + } + } + // Custom mutator. + virtual size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize) { + assert(MaxSize > sizeof(kMagic)); + if (Size < sizeof(kMagic)) + Size = sizeof(kMagic); + // "Fix" the data, then mutate. + memcpy(Data, &kMagic, std::min(MaxSize, sizeof(kMagic))); + return BasicMutate(Data + sizeof(kMagic), Size - sizeof(kMagic), + MaxSize - sizeof(kMagic)); + } + // No need to redefine CrossOver() here. +}; + +int main(int argc, char **argv) { + MyFuzzer F; + fuzzer::FuzzerDriver(argc, argv, F); +} diff --git a/lib/Fuzzer/test/fuzzer.test b/lib/Fuzzer/test/fuzzer.test index aa2c7943fa7..f27be80a192 100644 --- a/lib/Fuzzer/test/fuzzer.test +++ b/lib/Fuzzer/test/fuzzer.test @@ -26,3 +26,5 @@ RUN: not ./LLVMFuzzer-DFSanMemcmpTest-DFSan -use_traces=1 -seed=1 -runs=100 -tim RUN: not ./LLVMFuzzer-CxxTokensTest -seed=1 -timeout=15 -tokens=%S/../cxx_fuzzer_tokens.txt 2>&1 | FileCheck %s +RUN: not ./LLVMFuzzer-UserSuppliedFuzzerTest -seed=1 -timeout=15 2>&1 | FileCheck %s +