From 7ba1aa81797a5d341d923b84404bd0f8ccdceb8a Mon Sep 17 00:00:00 2001 From: julianvolodia Date: Mon, 4 May 2020 23:09:27 +0000 Subject: [PATCH 01/13] libFuzzer: information about repository --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ca6be65..db43735 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,8 @@ cd libFuzzer Fuzzer/build.sh ``` +libFuzzer repository could be found inside LLVM's compiler-rt project. + ## Links From e29c53a5bbb3e037189fed76160e49712eec4e88 Mon Sep 17 00:00:00 2001 From: julianvolodia Date: Mon, 4 May 2020 23:38:41 +0000 Subject: [PATCH 02/13] libFuzzer: add update commands --- checkout_build_install_llvm.sh | 4 ++++ 1 file changed, 4 insertions(+) mode change 100644 => 100755 checkout_build_install_llvm.sh diff --git a/checkout_build_install_llvm.sh b/checkout_build_install_llvm.sh old mode 100644 new mode 100755 index 189d8b5..19f26b2 --- a/checkout_build_install_llvm.sh +++ b/checkout_build_install_llvm.sh @@ -34,6 +34,10 @@ cd $WORK_DIR/src/llvm/projects && git clone --depth 1 http://llvm.org/git/compil cd $WORK_DIR/src/llvm/projects && git clone --depth 1 http://llvm.org/git/libcxx.git cd $WORK_DIR/src/llvm/projects && git clone --depth 1 http://llvm.org/git/libcxxabi.git +# Uncomment if you want *fresh* libFuzzer from checkouted repository. +#rm -r $WORK_DIR/libFuzzer/Fuzzer +#cp -r $WORK_DIR/src/llvm/projects/compiler-rt/lib/fuzzer/ $WORK_DIR/libFuzzer/Fuzzer + # Build & Install mkdir -p $WORK_DIR/work/llvm cd $WORK_DIR/work/llvm From ff141ddcbda613b2ea29ea0e17a886e9e6eecd3d Mon Sep 17 00:00:00 2001 From: julianvolodia Date: Mon, 4 May 2020 23:42:22 +0000 Subject: [PATCH 03/13] libFuzzer: update from llvm checkout --- libFuzzer/Fuzzer/CMakeLists.txt | 198 ++++-- libFuzzer/Fuzzer/FuzzerBuiltins.h | 35 + libFuzzer/Fuzzer/FuzzerBuiltinsMsvc.h | 72 ++ libFuzzer/Fuzzer/FuzzerCommand.h | 178 +++++ libFuzzer/Fuzzer/FuzzerCorpus.h | 166 +++-- libFuzzer/Fuzzer/FuzzerCrossOver.cpp | 7 +- libFuzzer/Fuzzer/FuzzerDataFlowTrace.cpp | 281 ++++++++ libFuzzer/Fuzzer/FuzzerDataFlowTrace.h | 135 ++++ libFuzzer/Fuzzer/FuzzerDefs.h | 149 +++- libFuzzer/Fuzzer/FuzzerDictionary.h | 19 +- libFuzzer/Fuzzer/FuzzerDriver.cpp | 416 +++++++---- libFuzzer/Fuzzer/FuzzerExtFunctions.def | 26 +- libFuzzer/Fuzzer/FuzzerExtFunctions.h | 7 +- libFuzzer/Fuzzer/FuzzerExtFunctionsDlsym.cpp | 7 +- .../Fuzzer/FuzzerExtFunctionsDlsymWin.cpp | 60 -- libFuzzer/Fuzzer/FuzzerExtFunctionsWeak.cpp | 17 +- .../Fuzzer/FuzzerExtFunctionsWeakAlias.cpp | 56 -- .../Fuzzer/FuzzerExtFunctionsWindows.cpp | 82 +++ libFuzzer/Fuzzer/FuzzerExtraCounters.cpp | 41 ++ libFuzzer/Fuzzer/FuzzerFlags.def | 95 ++- libFuzzer/Fuzzer/FuzzerFnAdapter.h | 187 ----- libFuzzer/Fuzzer/FuzzerFork.cpp | 409 +++++++++++ libFuzzer/Fuzzer/FuzzerFork.h | 24 + libFuzzer/Fuzzer/FuzzerIO.cpp | 65 +- libFuzzer/Fuzzer/FuzzerIO.h | 44 +- libFuzzer/Fuzzer/FuzzerIOPosix.cpp | 81 ++- libFuzzer/Fuzzer/FuzzerIOWindows.cpp | 118 +++- libFuzzer/Fuzzer/FuzzerInterface.h | 38 +- libFuzzer/Fuzzer/FuzzerInternal.h | 82 ++- libFuzzer/Fuzzer/FuzzerLoop.cpp | 592 ++++++++++------ libFuzzer/Fuzzer/FuzzerMain.cpp | 9 +- libFuzzer/Fuzzer/FuzzerMerge.cpp | 282 +++++--- libFuzzer/Fuzzer/FuzzerMerge.h | 30 +- libFuzzer/Fuzzer/FuzzerMutate.cpp | 89 ++- libFuzzer/Fuzzer/FuzzerMutate.h | 48 +- libFuzzer/Fuzzer/FuzzerOptions.h | 32 +- libFuzzer/Fuzzer/FuzzerRandom.h | 18 +- libFuzzer/Fuzzer/FuzzerSHA1.cpp | 10 +- libFuzzer/Fuzzer/FuzzerSHA1.h | 7 +- libFuzzer/Fuzzer/FuzzerShmem.h | 69 -- libFuzzer/Fuzzer/FuzzerShmemPosix.cpp | 103 --- libFuzzer/Fuzzer/FuzzerShmemWindows.cpp | 64 -- libFuzzer/Fuzzer/FuzzerTracePC.cpp | 658 ++++++++++++------ libFuzzer/Fuzzer/FuzzerTracePC.h | 244 +++++-- libFuzzer/Fuzzer/FuzzerTraceState.cpp | 170 ----- libFuzzer/Fuzzer/FuzzerUtil.cpp | 43 +- libFuzzer/Fuzzer/FuzzerUtil.h | 48 +- libFuzzer/Fuzzer/FuzzerUtilDarwin.cpp | 27 +- libFuzzer/Fuzzer/FuzzerUtilFuchsia.cpp | 502 +++++++++++++ libFuzzer/Fuzzer/FuzzerUtilLinux.cpp | 24 +- libFuzzer/Fuzzer/FuzzerUtilPosix.cpp | 53 +- libFuzzer/Fuzzer/FuzzerUtilWindows.cpp | 28 +- libFuzzer/Fuzzer/FuzzerValueBitMap.h | 36 +- libFuzzer/Fuzzer/README.txt | 3 +- libFuzzer/Fuzzer/afl/afl_driver.cpp | 346 +++++---- libFuzzer/Fuzzer/build.sh | 2 +- libFuzzer/Fuzzer/cxx.dict | 122 ---- libFuzzer/Fuzzer/dataflow/DataFlow.cpp | 205 ++++++ libFuzzer/Fuzzer/dataflow/DataFlow.h | 32 + .../Fuzzer/dataflow/DataFlowCallbacks.cpp | 86 +++ libFuzzer/Fuzzer/scripts/unbalanced_allocs.py | 92 +++ .../standalone/StandaloneFuzzTargetMain.c | 8 +- libFuzzer/Fuzzer/test/AFLDriverTest.cpp | 22 - .../Fuzzer/test/AbsNegAndConstant64Test.cpp | 23 - .../Fuzzer/test/AbsNegAndConstantTest.cpp | 23 - .../Fuzzer/test/AccumulateAllocationsTest.cpp | 17 - libFuzzer/Fuzzer/test/BogusInitializeTest.cpp | 15 - .../Fuzzer/test/BufferOverflowOnInput.cpp | 23 - libFuzzer/Fuzzer/test/CMakeLists.txt | 267 ------- libFuzzer/Fuzzer/test/CallerCalleeTest.cpp | 59 -- libFuzzer/Fuzzer/test/CounterTest.cpp | 18 - .../test/CustomCrossOverAndMutateTest.cpp | 34 - libFuzzer/Fuzzer/test/CustomCrossOverTest.cpp | 63 -- libFuzzer/Fuzzer/test/CustomMutatorTest.cpp | 38 - libFuzzer/Fuzzer/test/CxxStringEqTest.cpp | 24 - libFuzzer/Fuzzer/test/DSO1.cpp | 14 - libFuzzer/Fuzzer/test/DSO2.cpp | 14 - libFuzzer/Fuzzer/test/DSOTestExtra.cpp | 11 - libFuzzer/Fuzzer/test/DSOTestMain.cpp | 31 - libFuzzer/Fuzzer/test/DivTest.cpp | 20 - libFuzzer/Fuzzer/test/EmptyTest.cpp | 11 - libFuzzer/Fuzzer/test/EquivalenceATest.cpp | 17 - libFuzzer/Fuzzer/test/EquivalenceBTest.cpp | 27 - .../test/FourIndependentBranchesTest.cpp | 22 - libFuzzer/Fuzzer/test/FullCoverageSetTest.cpp | 24 - .../Fuzzer/test/FuzzerFnAdapterUnittest.cpp | 110 --- libFuzzer/Fuzzer/test/InitializeTest.cpp | 28 - libFuzzer/Fuzzer/test/LargeTest.cpp | 37 - libFuzzer/Fuzzer/test/LeakTest.cpp | 17 - libFuzzer/Fuzzer/test/LeakTimeoutTest.cpp | 17 - libFuzzer/Fuzzer/test/LoadTest.cpp | 22 - libFuzzer/Fuzzer/test/Memcmp64BytesTest.cpp | 20 - libFuzzer/Fuzzer/test/MemcmpTest.cpp | 31 - libFuzzer/Fuzzer/test/NotinstrumentedTest.cpp | 11 - libFuzzer/Fuzzer/test/NthRunCrashTest.cpp | 18 - .../Fuzzer/test/NullDerefOnEmptyTest.cpp | 19 - libFuzzer/Fuzzer/test/NullDerefTest.cpp | 26 - libFuzzer/Fuzzer/test/OneHugeAllocTest.cpp | 28 - .../test/OutOfMemorySingleLargeMallocTest.cpp | 27 - libFuzzer/Fuzzer/test/OutOfMemoryTest.cpp | 31 - libFuzzer/Fuzzer/test/RepeatedBytesTest.cpp | 29 - libFuzzer/Fuzzer/test/RepeatedMemcmp.cpp | 25 - .../Fuzzer/test/ShrinkControlFlowTest.cpp | 28 - .../Fuzzer/test/ShrinkValueProfileTest.cpp | 22 - .../Fuzzer/test/SignedIntOverflowTest.cpp | 28 - libFuzzer/Fuzzer/test/SimpleCmpTest.cpp | 46 -- .../Fuzzer/test/SimpleDictionaryTest.cpp | 29 - libFuzzer/Fuzzer/test/SimpleFnAdapterTest.cpp | 24 - libFuzzer/Fuzzer/test/SimpleHashTest.cpp | 40 -- libFuzzer/Fuzzer/test/SimpleTest.cpp | 27 - libFuzzer/Fuzzer/test/SimpleThreadedTest.cpp | 25 - libFuzzer/Fuzzer/test/SingleByteInputTest.cpp | 17 - libFuzzer/Fuzzer/test/SingleMemcmpTest.cpp | 17 - libFuzzer/Fuzzer/test/SingleStrcmpTest.cpp | 21 - libFuzzer/Fuzzer/test/SingleStrncmpTest.cpp | 17 - libFuzzer/Fuzzer/test/SpamyTest.cpp | 21 - libFuzzer/Fuzzer/test/StrcmpTest.cpp | 32 - libFuzzer/Fuzzer/test/StrncmpOOBTest.cpp | 21 - libFuzzer/Fuzzer/test/StrncmpTest.cpp | 28 - libFuzzer/Fuzzer/test/StrstrTest.cpp | 28 - libFuzzer/Fuzzer/test/SwapCmpTest.cpp | 34 - libFuzzer/Fuzzer/test/Switch2Test.cpp | 35 - libFuzzer/Fuzzer/test/SwitchTest.cpp | 58 -- libFuzzer/Fuzzer/test/ThreadedLeakTest.cpp | 18 - libFuzzer/Fuzzer/test/ThreadedTest.cpp | 26 - libFuzzer/Fuzzer/test/TimeoutEmptyTest.cpp | 14 - libFuzzer/Fuzzer/test/TimeoutTest.cpp | 26 - libFuzzer/Fuzzer/test/TraceMallocTest.cpp | 27 - .../Fuzzer/test/TwoDifferentBugsTest.cpp | 22 - libFuzzer/Fuzzer/test/UninstrumentedTest.cpp | 11 - .../Fuzzer/test/afl-driver-extra-stats.test | 30 - libFuzzer/Fuzzer/test/afl-driver-stderr.test | 12 - libFuzzer/Fuzzer/test/caller-callee.test | 2 - libFuzzer/Fuzzer/test/coverage.test | 21 - libFuzzer/Fuzzer/test/cxxstring.test | 2 - libFuzzer/Fuzzer/test/dict1.txt | 4 - libFuzzer/Fuzzer/test/dump_coverage.test | 14 - libFuzzer/Fuzzer/test/equivalence.test | 18 - .../Fuzzer/test/fuzzer-customcrossover.test | 10 - .../test/fuzzer-customcrossoverandmutate.test | 1 - .../Fuzzer/test/fuzzer-custommutator.test | 4 - libFuzzer/Fuzzer/test/fuzzer-dict.test | 6 - libFuzzer/Fuzzer/test/fuzzer-dirs.test | 15 - libFuzzer/Fuzzer/test/fuzzer-fdmask.test | 30 - libFuzzer/Fuzzer/test/fuzzer-finalstats.test | 11 - libFuzzer/Fuzzer/test/fuzzer-flags.test | 10 - libFuzzer/Fuzzer/test/fuzzer-fn-adapter.test | 3 - libFuzzer/Fuzzer/test/fuzzer-jobs.test | 29 - libFuzzer/Fuzzer/test/fuzzer-leak.test | 35 - .../Fuzzer/test/fuzzer-oom-with-profile.test | 6 - libFuzzer/Fuzzer/test/fuzzer-oom.test | 13 - libFuzzer/Fuzzer/test/fuzzer-printcovpcs.test | 8 - libFuzzer/Fuzzer/test/fuzzer-runs.test | 8 - libFuzzer/Fuzzer/test/fuzzer-seed.test | 3 - libFuzzer/Fuzzer/test/fuzzer-segv.test | 5 - .../Fuzzer/test/fuzzer-singleinputs.test | 16 - libFuzzer/Fuzzer/test/fuzzer-threaded.test | 7 - libFuzzer/Fuzzer/test/fuzzer-timeout.test | 19 - .../Fuzzer/test/fuzzer-traces-hooks.test | 17 - libFuzzer/Fuzzer/test/fuzzer-ubsan.test | 4 - libFuzzer/Fuzzer/test/fuzzer.test | 60 -- libFuzzer/Fuzzer/test/hi.txt | 1 - libFuzzer/Fuzzer/test/lit.cfg | 55 -- libFuzzer/Fuzzer/test/lit.site.cfg.in | 5 - libFuzzer/Fuzzer/test/merge-posix.test | 23 - libFuzzer/Fuzzer/test/merge.test | 53 -- libFuzzer/Fuzzer/test/minimize_crash.test | 12 - .../Fuzzer/test/no-coverage/CMakeLists.txt | 29 - libFuzzer/Fuzzer/test/repeated-bytes.test | 2 - libFuzzer/Fuzzer/test/shrink.test | 7 - libFuzzer/Fuzzer/test/simple-cmp.test | 2 - libFuzzer/Fuzzer/test/standalone.test | 4 - libFuzzer/Fuzzer/test/swap-cmp.test | 2 - libFuzzer/Fuzzer/test/trace-bb/CMakeLists.txt | 15 - libFuzzer/Fuzzer/test/trace-malloc-2.test | 8 - libFuzzer/Fuzzer/test/trace-malloc.test | 5 - libFuzzer/Fuzzer/test/trace-pc/CMakeLists.txt | 45 -- libFuzzer/Fuzzer/test/ubsan/CMakeLists.txt | 12 - libFuzzer/Fuzzer/test/ulimit.test | 4 - .../Fuzzer/test/uninstrumented/CMakeLists.txt | 13 - libFuzzer/Fuzzer/test/unit/lit.cfg | 7 - libFuzzer/Fuzzer/test/unit/lit.site.cfg.in | 2 - libFuzzer/Fuzzer/test/value-profile-cmp.test | 2 - libFuzzer/Fuzzer/test/value-profile-cmp2.test | 2 - libFuzzer/Fuzzer/test/value-profile-cmp3.test | 2 - libFuzzer/Fuzzer/test/value-profile-cmp4.test | 2 - libFuzzer/Fuzzer/test/value-profile-div.test | 3 - libFuzzer/Fuzzer/test/value-profile-load.test | 3 - libFuzzer/Fuzzer/test/value-profile-mem.test | 2 - libFuzzer/Fuzzer/test/value-profile-set.test | 3 - .../Fuzzer/test/value-profile-strcmp.test | 2 - .../Fuzzer/test/value-profile-strncmp.test | 2 - .../Fuzzer/test/value-profile-switch.test | 3 - libFuzzer/Fuzzer/tests/CMakeLists.txt | 87 +++ .../tests/FuzzedDataProviderUnittest.cpp | 405 +++++++++++ .../Fuzzer/{test => tests}/FuzzerUnittest.cpp | 518 +++++++++++--- 196 files changed, 5971 insertions(+), 5026 deletions(-) create mode 100644 libFuzzer/Fuzzer/FuzzerBuiltins.h create mode 100644 libFuzzer/Fuzzer/FuzzerBuiltinsMsvc.h create mode 100644 libFuzzer/Fuzzer/FuzzerCommand.h create mode 100644 libFuzzer/Fuzzer/FuzzerDataFlowTrace.cpp create mode 100644 libFuzzer/Fuzzer/FuzzerDataFlowTrace.h delete mode 100644 libFuzzer/Fuzzer/FuzzerExtFunctionsDlsymWin.cpp delete mode 100644 libFuzzer/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp create mode 100644 libFuzzer/Fuzzer/FuzzerExtFunctionsWindows.cpp create mode 100644 libFuzzer/Fuzzer/FuzzerExtraCounters.cpp delete mode 100644 libFuzzer/Fuzzer/FuzzerFnAdapter.h create mode 100644 libFuzzer/Fuzzer/FuzzerFork.cpp create mode 100644 libFuzzer/Fuzzer/FuzzerFork.h delete mode 100644 libFuzzer/Fuzzer/FuzzerShmem.h delete mode 100644 libFuzzer/Fuzzer/FuzzerShmemPosix.cpp delete mode 100644 libFuzzer/Fuzzer/FuzzerShmemWindows.cpp delete mode 100644 libFuzzer/Fuzzer/FuzzerTraceState.cpp create mode 100644 libFuzzer/Fuzzer/FuzzerUtilFuchsia.cpp delete mode 100644 libFuzzer/Fuzzer/cxx.dict create mode 100644 libFuzzer/Fuzzer/dataflow/DataFlow.cpp create mode 100644 libFuzzer/Fuzzer/dataflow/DataFlow.h create mode 100644 libFuzzer/Fuzzer/dataflow/DataFlowCallbacks.cpp create mode 100755 libFuzzer/Fuzzer/scripts/unbalanced_allocs.py delete mode 100644 libFuzzer/Fuzzer/test/AFLDriverTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/AbsNegAndConstant64Test.cpp delete mode 100644 libFuzzer/Fuzzer/test/AbsNegAndConstantTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/AccumulateAllocationsTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/BogusInitializeTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/BufferOverflowOnInput.cpp delete mode 100644 libFuzzer/Fuzzer/test/CMakeLists.txt delete mode 100644 libFuzzer/Fuzzer/test/CallerCalleeTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/CounterTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/CustomCrossOverAndMutateTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/CustomCrossOverTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/CustomMutatorTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/CxxStringEqTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/DSO1.cpp delete mode 100644 libFuzzer/Fuzzer/test/DSO2.cpp delete mode 100644 libFuzzer/Fuzzer/test/DSOTestExtra.cpp delete mode 100644 libFuzzer/Fuzzer/test/DSOTestMain.cpp delete mode 100644 libFuzzer/Fuzzer/test/DivTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/EmptyTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/EquivalenceATest.cpp delete mode 100644 libFuzzer/Fuzzer/test/EquivalenceBTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/FourIndependentBranchesTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/FullCoverageSetTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/FuzzerFnAdapterUnittest.cpp delete mode 100644 libFuzzer/Fuzzer/test/InitializeTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/LargeTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/LeakTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/LeakTimeoutTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/LoadTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/Memcmp64BytesTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/MemcmpTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/NotinstrumentedTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/NthRunCrashTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/NullDerefOnEmptyTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/NullDerefTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/OneHugeAllocTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/OutOfMemoryTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/RepeatedBytesTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/RepeatedMemcmp.cpp delete mode 100644 libFuzzer/Fuzzer/test/ShrinkControlFlowTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/ShrinkValueProfileTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/SignedIntOverflowTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/SimpleCmpTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/SimpleDictionaryTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/SimpleFnAdapterTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/SimpleHashTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/SimpleTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/SimpleThreadedTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/SingleByteInputTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/SingleMemcmpTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/SingleStrcmpTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/SingleStrncmpTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/SpamyTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/StrcmpTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/StrncmpOOBTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/StrncmpTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/StrstrTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/SwapCmpTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/Switch2Test.cpp delete mode 100644 libFuzzer/Fuzzer/test/SwitchTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/ThreadedLeakTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/ThreadedTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/TimeoutEmptyTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/TimeoutTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/TraceMallocTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/TwoDifferentBugsTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/UninstrumentedTest.cpp delete mode 100644 libFuzzer/Fuzzer/test/afl-driver-extra-stats.test delete mode 100644 libFuzzer/Fuzzer/test/afl-driver-stderr.test delete mode 100644 libFuzzer/Fuzzer/test/caller-callee.test delete mode 100644 libFuzzer/Fuzzer/test/coverage.test delete mode 100644 libFuzzer/Fuzzer/test/cxxstring.test delete mode 100644 libFuzzer/Fuzzer/test/dict1.txt delete mode 100644 libFuzzer/Fuzzer/test/dump_coverage.test delete mode 100644 libFuzzer/Fuzzer/test/equivalence.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-customcrossover.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-customcrossoverandmutate.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-custommutator.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-dict.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-dirs.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-fdmask.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-finalstats.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-flags.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-fn-adapter.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-jobs.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-leak.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-oom-with-profile.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-oom.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-printcovpcs.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-runs.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-seed.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-segv.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-singleinputs.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-threaded.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-timeout.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-traces-hooks.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer-ubsan.test delete mode 100644 libFuzzer/Fuzzer/test/fuzzer.test delete mode 100644 libFuzzer/Fuzzer/test/hi.txt delete mode 100644 libFuzzer/Fuzzer/test/lit.cfg delete mode 100644 libFuzzer/Fuzzer/test/lit.site.cfg.in delete mode 100644 libFuzzer/Fuzzer/test/merge-posix.test delete mode 100644 libFuzzer/Fuzzer/test/merge.test delete mode 100644 libFuzzer/Fuzzer/test/minimize_crash.test delete mode 100644 libFuzzer/Fuzzer/test/no-coverage/CMakeLists.txt delete mode 100644 libFuzzer/Fuzzer/test/repeated-bytes.test delete mode 100644 libFuzzer/Fuzzer/test/shrink.test delete mode 100644 libFuzzer/Fuzzer/test/simple-cmp.test delete mode 100644 libFuzzer/Fuzzer/test/standalone.test delete mode 100644 libFuzzer/Fuzzer/test/swap-cmp.test delete mode 100644 libFuzzer/Fuzzer/test/trace-bb/CMakeLists.txt delete mode 100644 libFuzzer/Fuzzer/test/trace-malloc-2.test delete mode 100644 libFuzzer/Fuzzer/test/trace-malloc.test delete mode 100644 libFuzzer/Fuzzer/test/trace-pc/CMakeLists.txt delete mode 100644 libFuzzer/Fuzzer/test/ubsan/CMakeLists.txt delete mode 100644 libFuzzer/Fuzzer/test/ulimit.test delete mode 100644 libFuzzer/Fuzzer/test/uninstrumented/CMakeLists.txt delete mode 100644 libFuzzer/Fuzzer/test/unit/lit.cfg delete mode 100644 libFuzzer/Fuzzer/test/unit/lit.site.cfg.in delete mode 100644 libFuzzer/Fuzzer/test/value-profile-cmp.test delete mode 100644 libFuzzer/Fuzzer/test/value-profile-cmp2.test delete mode 100644 libFuzzer/Fuzzer/test/value-profile-cmp3.test delete mode 100644 libFuzzer/Fuzzer/test/value-profile-cmp4.test delete mode 100644 libFuzzer/Fuzzer/test/value-profile-div.test delete mode 100644 libFuzzer/Fuzzer/test/value-profile-load.test delete mode 100644 libFuzzer/Fuzzer/test/value-profile-mem.test delete mode 100644 libFuzzer/Fuzzer/test/value-profile-set.test delete mode 100644 libFuzzer/Fuzzer/test/value-profile-strcmp.test delete mode 100644 libFuzzer/Fuzzer/test/value-profile-strncmp.test delete mode 100644 libFuzzer/Fuzzer/test/value-profile-switch.test create mode 100644 libFuzzer/Fuzzer/tests/CMakeLists.txt create mode 100644 libFuzzer/Fuzzer/tests/FuzzedDataProviderUnittest.cpp rename libFuzzer/Fuzzer/{test => tests}/FuzzerUnittest.cpp (63%) diff --git a/libFuzzer/Fuzzer/CMakeLists.txt b/libFuzzer/Fuzzer/CMakeLists.txt index f490b36..3743db9 100644 --- a/libFuzzer/Fuzzer/CMakeLists.txt +++ b/libFuzzer/Fuzzer/CMakeLists.txt @@ -1,47 +1,159 @@ -set(LIBFUZZER_FLAGS_BASE "${CMAKE_CXX_FLAGS}") -# Disable the coverage and sanitizer instrumentation for the fuzzer itself. -set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fno-sanitize-coverage=trace-pc-guard,edge,trace-cmp,indirect-calls,8bit-counters -Werror") -if( LLVM_USE_SANITIZE_COVERAGE ) - if(NOT "${LLVM_USE_SANITIZER}" STREQUAL "Address") - message(FATAL_ERROR - "LibFuzzer and its tests require LLVM_USE_SANITIZER=Address and " - "LLVM_USE_SANITIZE_COVERAGE=YES to be set." - ) +set(LIBFUZZER_SOURCES + FuzzerCrossOver.cpp + FuzzerDataFlowTrace.cpp + FuzzerDriver.cpp + FuzzerExtFunctionsDlsym.cpp + FuzzerExtFunctionsWeak.cpp + FuzzerExtFunctionsWindows.cpp + FuzzerExtraCounters.cpp + FuzzerFork.cpp + FuzzerIO.cpp + FuzzerIOPosix.cpp + FuzzerIOWindows.cpp + FuzzerLoop.cpp + FuzzerMerge.cpp + FuzzerMutate.cpp + FuzzerSHA1.cpp + FuzzerTracePC.cpp + FuzzerUtil.cpp + FuzzerUtilDarwin.cpp + FuzzerUtilFuchsia.cpp + FuzzerUtilLinux.cpp + FuzzerUtilPosix.cpp + FuzzerUtilWindows.cpp) + +set(LIBFUZZER_HEADERS + FuzzerBuiltins.h + FuzzerBuiltinsMsvc.h + FuzzerCommand.h + FuzzerCorpus.h + FuzzerDataFlowTrace.h + FuzzerDefs.h + FuzzerDictionary.h + FuzzerExtFunctions.def + FuzzerExtFunctions.h + FuzzerFlags.def + FuzzerFork.h + FuzzerIO.h + FuzzerInterface.h + FuzzerInternal.h + FuzzerMerge.h + FuzzerMutate.h + FuzzerOptions.h + FuzzerRandom.h + FuzzerSHA1.h + FuzzerTracePC.h + FuzzerUtil.h + FuzzerValueBitMap.h) + +CHECK_CXX_SOURCE_COMPILES(" + static thread_local int blah; + int main() { + return 0; + } + " HAS_THREAD_LOCAL) + +set(LIBFUZZER_CFLAGS ${SANITIZER_COMMON_CFLAGS}) + +if(OS_NAME MATCHES "Linux|Fuchsia" AND + COMPILER_RT_LIBCXX_PATH AND + COMPILER_RT_LIBCXXABI_PATH) + list(APPEND LIBFUZZER_CFLAGS -nostdinc++ -D_LIBCPP_ABI_VERSION=Fuzzer) + # Remove -stdlib= which is unused when passing -nostdinc++. + string(REGEX REPLACE "-stdlib=[a-zA-Z+]*" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) +elseif(TARGET cxx-headers OR HAVE_LIBCXX) + set(LIBFUZZER_DEPS cxx-headers) +endif() + +append_list_if(COMPILER_RT_HAS_OMIT_FRAME_POINTER_FLAG -fno-omit-frame-pointer LIBFUZZER_CFLAGS) + +if (CMAKE_CXX_FLAGS MATCHES "fsanitize-coverage") + list(APPEND LIBFUZZER_CFLAGS -fno-sanitize-coverage=trace-pc-guard,edge,trace-cmp,indirect-calls,8bit-counters) +endif() + +if(MSVC) + # Silence warnings by turning off exceptions in MSVC headers and avoid an + # error by unecessarily defining thread_local when it isn't even used on + # Windows. + list(APPEND LIBFUZZER_CFLAGS -D_HAS_EXCEPTIONS=0) +else() + if(NOT HAS_THREAD_LOCAL) + list(APPEND LIBFUZZER_CFLAGS -Dthread_local=__thread) endif() - add_library(LLVMFuzzerNoMainObjects OBJECT - FuzzerCrossOver.cpp - FuzzerDriver.cpp - FuzzerExtFunctionsDlsym.cpp - FuzzerExtFunctionsDlsymWin.cpp - FuzzerExtFunctionsWeak.cpp - FuzzerIO.cpp - FuzzerIOPosix.cpp - FuzzerIOWindows.cpp - FuzzerLoop.cpp - FuzzerMerge.cpp - FuzzerMutate.cpp - FuzzerSHA1.cpp - FuzzerShmemPosix.cpp - FuzzerShmemWindows.cpp - FuzzerTracePC.cpp - FuzzerTraceState.cpp - FuzzerUtil.cpp - FuzzerUtilDarwin.cpp - FuzzerUtilLinux.cpp - FuzzerUtilPosix.cpp - FuzzerUtilWindows.cpp - ) - add_library(LLVMFuzzerNoMain STATIC - $ - ) - target_link_libraries(LLVMFuzzerNoMain ${LLVM_PTHREAD_LIB}) - add_library(LLVMFuzzer STATIC - FuzzerMain.cpp - $ +endif() + +set(FUZZER_SUPPORTED_OS ${SANITIZER_COMMON_SUPPORTED_OS}) + +add_compiler_rt_component(fuzzer) + +add_compiler_rt_object_libraries(RTfuzzer + OS ${FUZZER_SUPPORTED_OS} + ARCHS ${FUZZER_SUPPORTED_ARCH} + SOURCES ${LIBFUZZER_SOURCES} + ADDITIONAL_HEADERS ${LIBFUZZER_HEADERS} + CFLAGS ${LIBFUZZER_CFLAGS} + DEPS ${LIBFUZZER_DEPS}) + +add_compiler_rt_object_libraries(RTfuzzer_main + OS ${FUZZER_SUPPORTED_OS} + ARCHS ${FUZZER_SUPPORTED_ARCH} + SOURCES FuzzerMain.cpp + CFLAGS ${LIBFUZZER_CFLAGS} + DEPS ${LIBFUZZER_DEPS}) + +add_compiler_rt_runtime(clang_rt.fuzzer + STATIC + OS ${FUZZER_SUPPORTED_OS} + ARCHS ${FUZZER_SUPPORTED_ARCH} + OBJECT_LIBS RTfuzzer RTfuzzer_main + CFLAGS ${LIBFUZZER_CFLAGS} + PARENT_TARGET fuzzer) + +add_compiler_rt_runtime(clang_rt.fuzzer_no_main + STATIC + OS ${FUZZER_SUPPORTED_OS} + ARCHS ${FUZZER_SUPPORTED_ARCH} + OBJECT_LIBS RTfuzzer + CFLAGS ${LIBFUZZER_CFLAGS} + PARENT_TARGET fuzzer) + +if(OS_NAME MATCHES "Linux|Fuchsia" AND + COMPILER_RT_LIBCXX_PATH AND + COMPILER_RT_LIBCXXABI_PATH) + macro(partially_link_libcxx name dir arch) + if(${arch} MATCHES "i386") + set(EMULATION_ARGUMENT "-m" "elf_i386") + else() + set(EMULATION_ARGUMENT "") + endif() + set(cxx_${arch}_merge_dir "${CMAKE_CURRENT_BINARY_DIR}/cxx_${arch}_merge.dir") + file(MAKE_DIRECTORY ${cxx_${arch}_merge_dir}) + add_custom_command(TARGET clang_rt.${name}-${arch} POST_BUILD + COMMAND ${CMAKE_LINKER} ${EMULATION_ARGUMENT} --whole-archive "$" --no-whole-archive ${dir}/lib/libc++.a -r -o ${name}.o + COMMAND ${CMAKE_OBJCOPY} --localize-hidden ${name}.o + COMMAND ${CMAKE_COMMAND} -E remove "$" + COMMAND ${CMAKE_AR} qcs "$" ${name}.o + WORKING_DIRECTORY ${cxx_${arch}_merge_dir} ) - target_link_libraries(LLVMFuzzer ${LLVM_PTHREAD_LIB}) + endmacro() - if( LLVM_INCLUDE_TESTS ) - add_subdirectory(test) - endif() + foreach(arch ${FUZZER_SUPPORTED_ARCH}) + get_target_flags_for_arch(${arch} TARGET_CFLAGS) + set(LIBCXX_${arch}_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libcxx_fuzzer_${arch}) + add_custom_libcxx(libcxx_fuzzer_${arch} ${LIBCXX_${arch}_PREFIX} + CFLAGS ${TARGET_CFLAGS} + CMAKE_ARGS -DCMAKE_CXX_COMPILER_WORKS=ON + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DLIBCXX_ABI_NAMESPACE=Fuzzer) + target_compile_options(RTfuzzer.${arch} PRIVATE -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1) + add_dependencies(RTfuzzer.${arch} libcxx_fuzzer_${arch}-build) + target_compile_options(RTfuzzer_main.${arch} PRIVATE -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1) + add_dependencies(RTfuzzer_main.${arch} libcxx_fuzzer_${arch}-build) + partially_link_libcxx(fuzzer_no_main ${LIBCXX_${arch}_PREFIX} ${arch}) + partially_link_libcxx(fuzzer ${LIBCXX_${arch}_PREFIX} ${arch}) + endforeach() +endif() + +if(COMPILER_RT_INCLUDE_TESTS) + add_subdirectory(tests) endif() diff --git a/libFuzzer/Fuzzer/FuzzerBuiltins.h b/libFuzzer/Fuzzer/FuzzerBuiltins.h new file mode 100644 index 0000000..5f1ccef --- /dev/null +++ b/libFuzzer/Fuzzer/FuzzerBuiltins.h @@ -0,0 +1,35 @@ +//===- FuzzerBuiltins.h - Internal header for builtins ----------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Wrapper functions and marcos around builtin functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_BUILTINS_H +#define LLVM_FUZZER_BUILTINS_H + +#include "FuzzerDefs.h" + +#if !LIBFUZZER_MSVC +#include + +#define GET_CALLER_PC() __builtin_return_address(0) + +namespace fuzzer { + +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); } + +inline uint32_t Clzll(unsigned long long X) { return __builtin_clzll(X); } +inline uint32_t Clz(unsigned long long X) { return __builtin_clz(X); } +inline int Popcountll(unsigned long long X) { return __builtin_popcountll(X); } + +} // namespace fuzzer + +#endif // !LIBFUZZER_MSVC +#endif // LLVM_FUZZER_BUILTINS_H diff --git a/libFuzzer/Fuzzer/FuzzerBuiltinsMsvc.h b/libFuzzer/Fuzzer/FuzzerBuiltinsMsvc.h new file mode 100644 index 0000000..bc65c60 --- /dev/null +++ b/libFuzzer/Fuzzer/FuzzerBuiltinsMsvc.h @@ -0,0 +1,72 @@ +//===- FuzzerBuiltinsMSVC.h - Internal header for builtins ------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Wrapper functions and marcos that use intrinsics instead of builtin functions +// which cannot be compiled by MSVC. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_BUILTINS_MSVC_H +#define LLVM_FUZZER_BUILTINS_MSVC_H + +#include "FuzzerDefs.h" + +#if LIBFUZZER_MSVC +#include +#include +#include + +// __builtin_return_address() cannot be compiled with MSVC. Use the equivalent +// from +#define GET_CALLER_PC() _ReturnAddress() + +namespace fuzzer { + +inline uint8_t Bswap(uint8_t x) { return x; } +// Use alternatives to __builtin functions from and on +// Windows since the builtins are not supported by MSVC. +inline uint16_t Bswap(uint16_t x) { return _byteswap_ushort(x); } +inline uint32_t Bswap(uint32_t x) { return _byteswap_ulong(x); } +inline uint64_t Bswap(uint64_t x) { return _byteswap_uint64(x); } + +// The functions below were mostly copied from +// compiler-rt/lib/builtins/int_lib.h which defines the __builtin functions used +// outside of Windows. +inline uint32_t Clzll(uint64_t X) { + unsigned long LeadZeroIdx = 0; + +#if !defined(_M_ARM) && !defined(_M_X64) + // Scan the high 32 bits. + if (_BitScanReverse(&LeadZeroIdx, static_cast(X >> 32))) + return static_cast(63 - (LeadZeroIdx + 32)); // Create a bit offset from the MSB. + // Scan the low 32 bits. + if (_BitScanReverse(&LeadZeroIdx, static_cast(X))) + return static_cast(63 - LeadZeroIdx); + +#else + if (_BitScanReverse64(&LeadZeroIdx, X)) return 63 - LeadZeroIdx; +#endif + return 64; +} + +inline uint32_t Clz(uint32_t X) { + unsigned long LeadZeroIdx = 0; + if (_BitScanReverse(&LeadZeroIdx, X)) return 31 - LeadZeroIdx; + return 32; +} + +inline int Popcountll(unsigned long long X) { +#if !defined(_M_ARM) && !defined(_M_X64) + return __popcnt(X) + __popcnt(X >> 32); +#else + return __popcnt64(X); +#endif +} + +} // namespace fuzzer + +#endif // LIBFUZER_MSVC +#endif // LLVM_FUZZER_BUILTINS_MSVC_H diff --git a/libFuzzer/Fuzzer/FuzzerCommand.h b/libFuzzer/Fuzzer/FuzzerCommand.h new file mode 100644 index 0000000..8730886 --- /dev/null +++ b/libFuzzer/Fuzzer/FuzzerCommand.h @@ -0,0 +1,178 @@ +//===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// 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 +#include +#include +#include + +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 &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 &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 &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) const { + 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) const { + 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::iterator endMutableArgs() { + return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); + } + + Vector::const_iterator endMutableArgs() const { + return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); + } + + // The command arguments. Args[0] is the command name. + Vector 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 diff --git a/libFuzzer/Fuzzer/FuzzerCorpus.h b/libFuzzer/Fuzzer/FuzzerCorpus.h index 8c2f703..6a95ef3 100644 --- a/libFuzzer/Fuzzer/FuzzerCorpus.h +++ b/libFuzzer/Fuzzer/FuzzerCorpus.h @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // fuzzer::InputCorpus @@ -12,6 +11,7 @@ #ifndef LLVM_FUZZER_CORPUS #define LLVM_FUZZER_CORPUS +#include "FuzzerDataFlowTrace.h" #include "FuzzerDefs.h" #include "FuzzerIO.h" #include "FuzzerRandom.h" @@ -34,11 +34,15 @@ struct InputInfo { size_t NumExecutedMutations = 0; size_t NumSuccessfullMutations = 0; bool MayDeleteFile = false; + bool Reduced = false; + bool HasFocusFunction = false; + Vector UniqFeatureSet; + Vector DataFlowTraceForFocusFunction; }; class InputCorpus { + static const size_t kFeatureSetSize = 1 << 21; public: - static const size_t kFeatureSetSize = 1 << 16; InputCorpus(const std::string &OutputCorpus) : OutputCorpus(OutputCorpus) { memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature)); memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature)); @@ -66,23 +70,99 @@ class InputCorpus { 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 = false) { + InputInfo *AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile, + bool HasFocusFunction, + const Vector &FeatureSet, + const DataFlowTrace &DFT, const InputInfo *BaseII) { assert(!U.empty()); - uint8_t Hash[kSHA1NumBytes]; if (FeatureDebug) Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures); - ComputeSHA1(U.data(), U.size(), Hash); - Hashes.insert(Sha1ToString(Hash)); Inputs.push_back(new InputInfo()); InputInfo &II = *Inputs.back(); II.U = U; II.NumFeatures = NumFeatures; II.MayDeleteFile = MayDeleteFile; - memcpy(II.Sha1, Hash, kSHA1NumBytes); + 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(); + return &II; + } + + // 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 &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(); - ValidateFeatureSet(); } bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); } @@ -91,11 +171,9 @@ class InputCorpus { InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)]; assert(!II.U.empty()); return II; - }; + } // Returns an index of random unit from the corpus to mutate. - // Hypothesis: units added to the corpus last are more likely to be - // interesting. This function gives more weight to the more recent units. size_t ChooseUnitIdxToMutate(Random &Rand) { size_t Idx = static_cast(CorpusDistribution(Rand)); assert(Idx < Inputs.size()); @@ -105,9 +183,9 @@ class InputCorpus { void PrintStats() { for (size_t i = 0; i < Inputs.size(); i++) { const auto &II = *Inputs[i]; - Printf(" [%zd %s]\tsz: %zd\truns: %zd\tsucc: %zd\n", i, + 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.NumExecutedMutations, II.NumSuccessfullMutations, II.HasFocusFunction); } } @@ -123,10 +201,14 @@ class InputCorpus { Printf("\n"); } - void DeleteInput(size_t Idx) { - InputInfo &II = *Inputs[Idx]; + 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); @@ -144,29 +226,21 @@ class InputCorpus { 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; - CountingFeatures = true; return true; } return false; } - size_t NumFeatures() const { - size_t Res = 0; - for (size_t i = 0; i < kFeatureSetSize; i++) - Res += GetFeature(i) != 0; - return Res; - } - - void ResetFeatureSet() { - assert(Inputs.empty()); - memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature)); - memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature)); - } + size_t NumFeatures() const { return NumAddedFeatures; } + size_t NumFeatureUpdates() const { return NumUpdatedFeatures; } private: @@ -175,7 +249,6 @@ class InputCorpus { size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; } void ValidateFeatureSet() { - if (!CountingFeatures) return; if (FeatureDebug) PrintFeatureSet(); for (size_t Idx = 0; Idx < kFeatureSetSize; Idx++) @@ -191,28 +264,41 @@ class InputCorpus { // 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); - if (CountingFeatures) + 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++) - Weights[i] = Inputs[i]->NumFeatures * (i + 1); - else - std::iota(Weights.begin(), Weights.end(), 1); + 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( Intervals.begin(), Intervals.end(), Weights.begin()); } std::piecewise_constant_distribution CorpusDistribution; - std::vector Intervals; - std::vector Weights; + Vector Intervals; + Vector Weights; std::unordered_set Hashes; - std::vector Inputs; + Vector Inputs; - bool CountingFeatures = false; + size_t NumAddedFeatures = 0; + size_t NumUpdatedFeatures = 0; uint32_t InputSizesPerFeature[kFeatureSetSize]; uint32_t SmallestElementPerFeature[kFeatureSetSize]; diff --git a/libFuzzer/Fuzzer/FuzzerCrossOver.cpp b/libFuzzer/Fuzzer/FuzzerCrossOver.cpp index 8b0fd7d..83d9f8d 100644 --- a/libFuzzer/Fuzzer/FuzzerCrossOver.cpp +++ b/libFuzzer/Fuzzer/FuzzerCrossOver.cpp @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Cross over test inputs. diff --git a/libFuzzer/Fuzzer/FuzzerDataFlowTrace.cpp b/libFuzzer/Fuzzer/FuzzerDataFlowTrace.cpp new file mode 100644 index 0000000..99ff918 --- /dev/null +++ b/libFuzzer/Fuzzer/FuzzerDataFlowTrace.cpp @@ -0,0 +1,281 @@ +//===- FuzzerDataFlowTrace.cpp - DataFlowTrace ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::DataFlowTrace +//===----------------------------------------------------------------------===// + +#include "FuzzerDataFlowTrace.h" + +#include "FuzzerCommand.h" +#include "FuzzerIO.h" +#include "FuzzerRandom.h" +#include "FuzzerSHA1.h" +#include "FuzzerUtil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { +static const char *kFunctionsTxt = "functions.txt"; + +bool BlockCoverage::AppendCoverage(const std::string &S) { + std::stringstream SS(S); + return AppendCoverage(SS); +} + +// Coverage lines have this form: +// CN X Y Z T +// where N is the number of the function, T is the total number of instrumented +// BBs, and X,Y,Z, if present, are the indecies of covered BB. +// BB #0, which is the entry block, is not explicitly listed. +bool BlockCoverage::AppendCoverage(std::istream &IN) { + std::string L; + while (std::getline(IN, L, '\n')) { + if (L.empty()) + continue; + std::stringstream SS(L.c_str() + 1); + size_t FunctionId = 0; + SS >> FunctionId; + if (L[0] == 'F') { + FunctionsWithDFT.insert(FunctionId); + continue; + } + if (L[0] != 'C') continue; + Vector CoveredBlocks; + while (true) { + uint32_t BB = 0; + SS >> BB; + if (!SS) break; + CoveredBlocks.push_back(BB); + } + if (CoveredBlocks.empty()) return false; + uint32_t NumBlocks = CoveredBlocks.back(); + CoveredBlocks.pop_back(); + for (auto BB : CoveredBlocks) + if (BB >= NumBlocks) return false; + auto It = Functions.find(FunctionId); + auto &Counters = + It == Functions.end() + ? Functions.insert({FunctionId, Vector(NumBlocks)}) + .first->second + : It->second; + + if (Counters.size() != NumBlocks) return false; // wrong number of blocks. + + Counters[0]++; + for (auto BB : CoveredBlocks) + Counters[BB]++; + } + return true; +} + +// Assign weights to each function. +// General principles: +// * any uncovered function gets weight 0. +// * a function with lots of uncovered blocks gets bigger weight. +// * a function with a less frequently executed code gets bigger weight. +Vector BlockCoverage::FunctionWeights(size_t NumFunctions) const { + Vector Res(NumFunctions); + for (auto It : Functions) { + auto FunctionID = It.first; + auto Counters = It.second; + assert(FunctionID < NumFunctions); + auto &Weight = Res[FunctionID]; + // Give higher weight if the function has a DFT. + Weight = FunctionsWithDFT.count(FunctionID) ? 1000. : 1; + // Give higher weight to functions with less frequently seen basic blocks. + Weight /= SmallestNonZeroCounter(Counters); + // Give higher weight to functions with the most uncovered basic blocks. + Weight *= NumberOfUncoveredBlocks(Counters) + 1; + } + return Res; +} + +void DataFlowTrace::ReadCoverage(const std::string &DirPath) { + Vector Files; + GetSizedFilesFromDir(DirPath, &Files); + for (auto &SF : Files) { + auto Name = Basename(SF.File); + if (Name == kFunctionsTxt) continue; + if (!CorporaHashes.count(Name)) continue; + std::ifstream IF(SF.File); + Coverage.AppendCoverage(IF); + } +} + +static void DFTStringAppendToVector(Vector *DFT, + const std::string &DFTString) { + assert(DFT->size() == DFTString.size()); + for (size_t I = 0, Len = DFT->size(); I < Len; I++) + (*DFT)[I] = DFTString[I] == '1'; +} + +// converts a string of '0' and '1' into a Vector +static Vector DFTStringToVector(const std::string &DFTString) { + Vector DFT(DFTString.size()); + DFTStringAppendToVector(&DFT, DFTString); + return DFT; +} + +static bool ParseError(const char *Err, const std::string &Line) { + Printf("DataFlowTrace: parse error: %s: Line: %s\n", Err, Line.c_str()); + return false; +} + +// TODO(metzman): replace std::string with std::string_view for +// better performance. Need to figure our how to use string_view on Windows. +static bool ParseDFTLine(const std::string &Line, size_t *FunctionNum, + std::string *DFTString) { + if (!Line.empty() && Line[0] != 'F') + return false; // Ignore coverage. + size_t SpacePos = Line.find(' '); + if (SpacePos == std::string::npos) + return ParseError("no space in the trace line", Line); + if (Line.empty() || Line[0] != 'F') + return ParseError("the trace line doesn't start with 'F'", Line); + *FunctionNum = std::atol(Line.c_str() + 1); + const char *Beg = Line.c_str() + SpacePos + 1; + const char *End = Line.c_str() + Line.size(); + assert(Beg < End); + size_t Len = End - Beg; + for (size_t I = 0; I < Len; I++) { + if (Beg[I] != '0' && Beg[I] != '1') + return ParseError("the trace should contain only 0 or 1", Line); + } + *DFTString = Beg; + return true; +} + +bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, + Vector &CorporaFiles, Random &Rand) { + if (DirPath.empty()) return false; + Printf("INFO: DataFlowTrace: reading from '%s'\n", DirPath.c_str()); + Vector Files; + GetSizedFilesFromDir(DirPath, &Files); + std::string L; + size_t FocusFuncIdx = SIZE_MAX; + Vector FunctionNames; + + // Collect the hashes of the corpus files. + for (auto &SF : CorporaFiles) + CorporaHashes.insert(Hash(FileToVector(SF.File))); + + // Read functions.txt + std::ifstream IF(DirPlusFile(DirPath, kFunctionsTxt)); + size_t NumFunctions = 0; + while (std::getline(IF, L, '\n')) { + FunctionNames.push_back(L); + NumFunctions++; + if (*FocusFunction == L) + FocusFuncIdx = NumFunctions - 1; + } + if (!NumFunctions) + return false; + + if (*FocusFunction == "auto") { + // AUTOFOCUS works like this: + // * reads the coverage data from the DFT files. + // * assigns weights to functions based on coverage. + // * chooses a random function according to the weights. + ReadCoverage(DirPath); + auto Weights = Coverage.FunctionWeights(NumFunctions); + Vector Intervals(NumFunctions + 1); + std::iota(Intervals.begin(), Intervals.end(), 0); + auto Distribution = std::piecewise_constant_distribution( + Intervals.begin(), Intervals.end(), Weights.begin()); + FocusFuncIdx = static_cast(Distribution(Rand)); + *FocusFunction = FunctionNames[FocusFuncIdx]; + assert(FocusFuncIdx < NumFunctions); + Printf("INFO: AUTOFOCUS: %zd %s\n", FocusFuncIdx, + FunctionNames[FocusFuncIdx].c_str()); + for (size_t i = 0; i < NumFunctions; i++) { + if (!Weights[i]) continue; + Printf(" [%zd] W %g\tBB-tot %u\tBB-cov %u\tEntryFreq %u:\t%s\n", i, + Weights[i], Coverage.GetNumberOfBlocks(i), + Coverage.GetNumberOfCoveredBlocks(i), Coverage.GetCounter(i, 0), + FunctionNames[i].c_str()); + } + } + + if (!NumFunctions || FocusFuncIdx == SIZE_MAX || Files.size() <= 1) + return false; + + // Read traces. + size_t NumTraceFiles = 0; + size_t NumTracesWithFocusFunction = 0; + for (auto &SF : Files) { + auto Name = Basename(SF.File); + if (Name == kFunctionsTxt) continue; + if (!CorporaHashes.count(Name)) continue; // not in the corpus. + NumTraceFiles++; + // Printf("=== %s\n", Name.c_str()); + std::ifstream IF(SF.File); + while (std::getline(IF, L, '\n')) { + size_t FunctionNum = 0; + std::string DFTString; + if (ParseDFTLine(L, &FunctionNum, &DFTString) && + FunctionNum == FocusFuncIdx) { + NumTracesWithFocusFunction++; + + if (FunctionNum >= NumFunctions) + return ParseError("N is greater than the number of functions", L); + Traces[Name] = DFTStringToVector(DFTString); + // Print just a few small traces. + if (NumTracesWithFocusFunction <= 3 && DFTString.size() <= 16) + Printf("%s => |%s|\n", Name.c_str(), std::string(DFTString).c_str()); + break; // No need to parse the following lines. + } + } + } + Printf("INFO: DataFlowTrace: %zd trace files, %zd functions, " + "%zd traces with focus function\n", + NumTraceFiles, NumFunctions, NumTracesWithFocusFunction); + return NumTraceFiles > 0; +} + +int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath, + const Vector &CorporaFiles) { + Printf("INFO: collecting data flow: bin: %s dir: %s files: %zd\n", + DFTBinary.c_str(), DirPath.c_str(), CorporaFiles.size()); + static char DFSanEnv[] = "DFSAN_OPTIONS=fast16labels=1:warn_unimplemented=0"; + putenv(DFSanEnv); + MkDir(DirPath); + for (auto &F : CorporaFiles) { + // For every input F we need to collect the data flow and the coverage. + // Data flow collection may fail if we request too many DFSan tags at once. + // So, we start from requesting all tags in range [0,Size) and if that fails + // we then request tags in [0,Size/2) and [Size/2, Size), and so on. + // Function number => DFT. + auto OutPath = DirPlusFile(DirPath, Hash(FileToVector(F.File))); + std::unordered_map> DFTMap; + std::unordered_set Cov; + Command Cmd; + Cmd.addArgument(DFTBinary); + Cmd.addArgument(F.File); + Cmd.addArgument(OutPath); + Printf("CMD: %s\n", Cmd.toString().c_str()); + ExecuteCommand(Cmd); + } + // Write functions.txt if it's currently empty or doesn't exist. + auto FunctionsTxtPath = DirPlusFile(DirPath, kFunctionsTxt); + if (FileToString(FunctionsTxtPath).empty()) { + Command Cmd; + Cmd.addArgument(DFTBinary); + Cmd.setOutputFile(FunctionsTxtPath); + ExecuteCommand(Cmd); + } + return 0; +} + +} // namespace fuzzer diff --git a/libFuzzer/Fuzzer/FuzzerDataFlowTrace.h b/libFuzzer/Fuzzer/FuzzerDataFlowTrace.h new file mode 100644 index 0000000..d6e3de3 --- /dev/null +++ b/libFuzzer/Fuzzer/FuzzerDataFlowTrace.h @@ -0,0 +1,135 @@ +//===- FuzzerDataFlowTrace.h - Internal header for the Fuzzer ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// 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 "FuzzerIO.h" + +#include +#include +#include +#include + +namespace fuzzer { + +int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath, + const Vector &CorporaFiles); + +class BlockCoverage { + public: + bool AppendCoverage(std::istream &IN); + bool AppendCoverage(const std::string &S); + + size_t NumCoveredFunctions() const { return Functions.size(); } + + uint32_t GetCounter(size_t FunctionId, size_t BasicBlockId) { + auto It = Functions.find(FunctionId); + if (It == Functions.end()) return 0; + const auto &Counters = It->second; + if (BasicBlockId < Counters.size()) + return Counters[BasicBlockId]; + return 0; + } + + uint32_t GetNumberOfBlocks(size_t FunctionId) { + auto It = Functions.find(FunctionId); + if (It == Functions.end()) return 0; + const auto &Counters = It->second; + return Counters.size(); + } + + uint32_t GetNumberOfCoveredBlocks(size_t FunctionId) { + auto It = Functions.find(FunctionId); + if (It == Functions.end()) return 0; + const auto &Counters = It->second; + uint32_t Result = 0; + for (auto Cnt: Counters) + if (Cnt) + Result++; + return Result; + } + + Vector FunctionWeights(size_t NumFunctions) const; + void clear() { Functions.clear(); } + + private: + + typedef Vector CoverageVector; + + uint32_t NumberOfCoveredBlocks(const CoverageVector &Counters) const { + uint32_t Res = 0; + for (auto Cnt : Counters) + if (Cnt) + Res++; + return Res; + } + + uint32_t NumberOfUncoveredBlocks(const CoverageVector &Counters) const { + return Counters.size() - NumberOfCoveredBlocks(Counters); + } + + uint32_t SmallestNonZeroCounter(const CoverageVector &Counters) const { + assert(!Counters.empty()); + uint32_t Res = Counters[0]; + for (auto Cnt : Counters) + if (Cnt) + Res = Min(Res, Cnt); + assert(Res); + return Res; + } + + // Function ID => vector of counters. + // Each counter represents how many input files trigger the given basic block. + std::unordered_map Functions; + // Functions that have DFT entry. + std::unordered_set FunctionsWithDFT; +}; + +class DataFlowTrace { + public: + void ReadCoverage(const std::string &DirPath); + bool Init(const std::string &DirPath, std::string *FocusFunction, + Vector &CorporaFiles, Random &Rand); + void Clear() { Traces.clear(); } + const Vector *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 > Traces; + BlockCoverage Coverage; + std::unordered_set CorporaHashes; +}; +} // namespace fuzzer + +#endif // LLVM_FUZZER_DATA_FLOW_TRACE diff --git a/libFuzzer/Fuzzer/FuzzerDefs.h b/libFuzzer/Fuzzer/FuzzerDefs.h index 6c1f6a1..5dc2d8e 100644 --- a/libFuzzer/Fuzzer/FuzzerDefs.h +++ b/libFuzzer/Fuzzer/FuzzerDefs.h @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Basic definitions. @@ -16,52 +15,144 @@ #include #include #include +#include +#include #include #include + // 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 -#define LIBFUZZER_POSIX LIBFUZZER_APPLE || LIBFUZZER_LINUX +#if defined(_MSC_VER) && !defined(__clang__) +// MSVC compiler is being used. +#define LIBFUZZER_MSVC 1 +#else +#define LIBFUZZER_MSVC 0 +#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 -#define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt"))) +# if __has_attribute(target) +# define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt"))) +# else +# define ATTRIBUTE_TARGET_POPCNT +# endif #else -#define ATTRIBUTE_TARGET_POPCNT +# define ATTRIBUTE_TARGET_POPCNT #endif #ifdef __clang__ // avoid gcc warning. -# define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +# 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__ +#if LIBFUZZER_WINDOWS +#define ATTRIBUTE_NO_SANITIZE_ADDRESS +#else #define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) - -#define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_ADDRESS ATTRIBUTE_NO_SANITIZE_MEMORY - +#endif #if LIBFUZZER_WINDOWS +#define ATTRIBUTE_ALIGNED(X) __declspec(align(X)) #define ATTRIBUTE_INTERFACE __declspec(dllexport) +// This is used for __sancov_lowest_stack which is needed for +// -fsanitize-coverage=stack-depth. That feature is not yet available on +// Windows, so make the symbol static to avoid linking errors. +#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC static +#define ATTRIBUTE_NOINLINE __declspec(noinline) #else +#define ATTRIBUTE_ALIGNED(X) __attribute__((aligned(X))) #define ATTRIBUTE_INTERFACE __attribute__((visibility("default"))) +#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \ + ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) thread_local + +#define ATTRIBUTE_NOINLINE __attribute__((noinline)) +#endif + +#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 namespace fuzzer { @@ -81,21 +172,37 @@ struct ExternalFunctions; // Global interface to functions that may or may not be available. extern ExternalFunctions *EF; -typedef std::vector Unit; -typedef std::vector UnitVector; +// We are using a custom allocator to give a different symbol name to STL +// containers in order to avoid ODR violations. +template + class fuzzer_allocator: public std::allocator { + public: + fuzzer_allocator() = default; + + template + fuzzer_allocator(const fuzzer_allocator&) {} + + template + struct rebind { typedef fuzzer_allocator other; }; + }; + +template +using Vector = std::vector>; + +template +using Set = std::set, fuzzer_allocator>; + +typedef Vector Unit; +typedef Vector UnitVector; typedef int (*UserCallback)(const uint8_t *Data, size_t Size); int FuzzerDriver(int *argc, char ***argv, UserCallback Callback); -struct ScopedDoingMyOwnMemmem { - ScopedDoingMyOwnMemmem(); - ~ScopedDoingMyOwnMemmem(); -}; +uint8_t *ExtraCountersBegin(); +uint8_t *ExtraCountersEnd(); +void ClearExtraCounters(); -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); } +extern bool RunningUserCallback; } // namespace fuzzer diff --git a/libFuzzer/Fuzzer/FuzzerDictionary.h b/libFuzzer/Fuzzer/FuzzerDictionary.h index f5024b1..301c5d9 100644 --- a/libFuzzer/Fuzzer/FuzzerDictionary.h +++ b/libFuzzer/Fuzzer/FuzzerDictionary.h @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // fuzzer::Dictionary @@ -36,12 +35,6 @@ template class FixedWord { return Size == w.Size && 0 == memcmp(Data, w.Data, Size); } - bool operator<(const FixedWord &w) const { - if (Size != w.Size) - return Size < w.Size; - return memcmp(Data, w.Data, Size) < 0; - } - static size_t GetMaxSize() { return kMaxSize; } const uint8_t *data() const { return Data; } uint8_t size() const { return Size; } @@ -113,12 +106,12 @@ class Dictionary { }; // Parses one dictionary entry. -// If successfull, write the enty to Unit and returns true, +// 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 succesfully. -bool ParseDictionaryFile(const std::string &Text, std::vector *Units); +// were parsed successfully. +bool ParseDictionaryFile(const std::string &Text, Vector *Units); } // namespace fuzzer diff --git a/libFuzzer/Fuzzer/FuzzerDriver.cpp b/libFuzzer/Fuzzer/FuzzerDriver.cpp index 1a97a0b..44c9065 100644 --- a/libFuzzer/Fuzzer/FuzzerDriver.cpp +++ b/libFuzzer/Fuzzer/FuzzerDriver.cpp @@ -1,33 +1,41 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // FuzzerDriver and flag parsing. //===----------------------------------------------------------------------===// +#include "FuzzerCommand.h" #include "FuzzerCorpus.h" +#include "FuzzerFork.h" +#include "FuzzerIO.h" #include "FuzzerInterface.h" #include "FuzzerInternal.h" -#include "FuzzerIO.h" +#include "FuzzerMerge.h" #include "FuzzerMutate.h" #include "FuzzerRandom.h" -#include "FuzzerShmem.h" #include "FuzzerTracePC.h" #include #include #include +#include #include #include #include #include +#include // This function should be present in the libFuzzer so that the client // binary can test for its existence. +#if LIBFUZZER_MSVC +extern "C" void __libfuzzer_is_present() {} +#pragma comment(linker, "/include:__libfuzzer_is_present") +#else extern "C" __attribute__((used)) void __libfuzzer_is_present() {} +#endif // LIBFUZZER_MSVC namespace fuzzer { @@ -73,7 +81,7 @@ static const FlagDescription FlagDescriptions [] { static const size_t kNumFlags = sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]); -static std::vector *Inputs; +static Vector *Inputs; static std::string *ProgName; static void PrintHelp() { @@ -149,7 +157,7 @@ static bool ParseOneFlag(const char *Param) { int Val = MyStol(Str); *FlagDescriptions[F].IntFlag = Val; if (Flags.verbosity >= 2) - Printf("Flag: %s %d\n", Name, Val);; + Printf("Flag: %s %d\n", Name, Val); return true; } else if (FlagDescriptions[F].UIntFlag) { unsigned int Val = std::stoul(Str); @@ -174,7 +182,8 @@ static bool ParseOneFlag(const char *Param) { } // We don't use any library to minimize dependencies. -static void ParseFlags(const std::vector &Args) { +static void ParseFlags(const Vector &Args, + const ExternalFunctions *EF) { for (size_t F = 0; F < kNumFlags; F++) { if (FlagDescriptions[F].IntFlag) *FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default; @@ -184,9 +193,18 @@ static void ParseFlags(const std::vector &Args) { if (FlagDescriptions[F].StrFlag) *FlagDescriptions[F].StrFlag = nullptr; } - Inputs = new std::vector; + + // Disable len_control by default, if LLVMFuzzerCustomMutator is used. + if (EF->LLVMFuzzerCustomMutator) + Flags.len_control = 0; + + Inputs = new Vector; for (size_t A = 1; A < Args.size(); A++) { - if (ParseOneFlag(Args[A].c_str())) continue; + if (ParseOneFlag(Args[A].c_str())) { + if (Flags.ignore_remaining_args) + break; + continue; + } Inputs->push_back(Args[A]); } } @@ -201,16 +219,20 @@ static void PulseThread() { } } -static void WorkerThread(const std::string &Cmd, std::atomic *Counter, +static void WorkerThread(const Command &BaseCmd, std::atomic *Counter, unsigned NumJobs, std::atomic *HasErrors) { while (true) { unsigned C = (*Counter)++; if (C >= NumJobs) break; std::string Log = "fuzz-" + std::to_string(C) + ".log"; - std::string ToRun = Cmd + " > " + Log + " 2>&1\n"; - if (Flags.verbosity) - Printf("%s", ToRun.c_str()); - int ExitCode = ExecuteCommand(ToRun); + 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 Lock(Mu); @@ -220,7 +242,7 @@ static void WorkerThread(const std::string &Cmd, std::atomic *Counter, } } -std::string CloneArgsWithoutX(const std::vector &Args, +std::string CloneArgsWithoutX(const Vector &Args, const char *X1, const char *X2) { std::string Cmd; for (auto &S : Args) { @@ -231,16 +253,18 @@ std::string CloneArgsWithoutX(const std::vector &Args, return Cmd; } -static int RunInMultipleProcesses(const std::vector &Args, +static int RunInMultipleProcesses(const Vector &Args, unsigned NumWorkers, unsigned NumJobs) { std::atomic Counter(0); std::atomic HasErrors(false); - std::string Cmd = CloneArgsWithoutX(Args, "jobs", "workers"); - std::vector V; + Command Cmd(Args); + Cmd.removeFlag("jobs"); + Cmd.removeFlag("workers"); + Vector V; std::thread Pulse(PulseThread); Pulse.detach(); for (unsigned i = 0; i < NumWorkers; i++) - V.push_back(std::thread(WorkerThread, Cmd, &Counter, NumJobs, &HasErrors)); + V.push_back(std::thread(WorkerThread, std::ref(Cmd), &Counter, NumJobs, &HasErrors)); for (auto &T : V) T.join(); return HasErrors ? 1 : 0; @@ -265,7 +289,7 @@ int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) { Unit U = FileToVector(InputFilePath); if (MaxLen && MaxLen < U.size()) U.resize(MaxLen); - F->RunOne(U.data(), U.size()); + F->ExecuteCallback(U.data(), U.size()); F->TryDetectingAMemoryLeak(U.data(), U.size(), true); return 0; } @@ -278,25 +302,96 @@ static bool AllInputsAreFiles() { return true; } -int MinimizeCrashInput(const std::vector &Args, +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 &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 = TempPath(".txt"); + auto TmpFilePath = TempPath(".repro"); + Cmd.addArgument(TmpFilePath); + Cmd.setOutputFile(LogFilePath); + Cmd.combineOutAndErr(); + + std::string CurrentFilePath = InputFilePath; + auto U = FileToVector(CurrentFilePath); + size_t Size = U.size(); + + const Vector 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 &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); - std::string BaseCmd = - CloneArgsWithoutX(Args, "minimize_crash", "exact_artifact_path"); - auto InputPos = BaseCmd.find(" " + InputFilePath + " "); - assert(InputPos != std::string::npos); - BaseCmd.erase(InputPos, InputFilePath.size() + 1); + 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 += " -max_total_time=600"; + BaseCmd.addFlag("max_total_time", "600"); } - // BaseCmd += " > /dev/null 2>&1 "; + + auto LogFilePath = TempPath(".txt"); + BaseCmd.setOutputFile(LogFilePath); + BaseCmd.combineOutAndErr(); std::string CurrentFilePath = InputFilePath; while (true) { @@ -304,9 +399,11 @@ int MinimizeCrashInput(const std::vector &Args, Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n", CurrentFilePath.c_str(), U.size()); - auto Cmd = BaseCmd + " " + CurrentFilePath; + Command Cmd(BaseCmd); + Cmd.addArgument(CurrentFilePath); - Printf("CRASH_MIN: executing: %s\n", Cmd.c_str()); + 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()); @@ -315,13 +412,20 @@ int MinimizeCrashInput(const std::vector &Args, 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 = - Options.ArtifactPrefix + "minimized-from-" + Hash(U); - Cmd += " -minimize_crash_internal_step=1 -exact_artifact_path=" + - ArtifactPath; - Printf("CRASH_MIN: executing: %s\n", Cmd.c_str()); + 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; @@ -329,11 +433,26 @@ int MinimizeCrashInput(const std::vector &Args, } Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n", CurrentFilePath.c_str(), U.size()); - return 0; + 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\n\n\n\n\n*********************************\n"); + Printf("*********************************\n"); } + RemoveFile(LogFilePath); return 0; } @@ -346,7 +465,6 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { Printf("INFO: The input is small enough, exiting\n"); exit(0); } - Corpus->AddToCorpus(U, 0); F->SetMaxInputLen(U.size()); F->SetMaxMutationLen(U.size() - 1); F->MinimizeCrashLoop(U); @@ -355,28 +473,55 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { return 0; } -int AnalyzeDictionary(Fuzzer *F, const std::vector& Dict, +void Merge(Fuzzer *F, FuzzingOptions &Options, const Vector &Args, + const Vector &Corpora, const char *CFPathOrNull) { + if (Corpora.size() < 2) { + Printf("INFO: Merge requires two or more corpus dirs\n"); + exit(0); + } + + Vector OldCorpus, NewCorpus; + GetSizedFilesFromDir(Corpora[0], &OldCorpus); + for (size_t i = 1; i < Corpora.size(); i++) + GetSizedFilesFromDir(Corpora[i], &NewCorpus); + std::sort(OldCorpus.begin(), OldCorpus.end()); + std::sort(NewCorpus.begin(), NewCorpus.end()); + + std::string CFPath = CFPathOrNull ? CFPathOrNull : TempPath(".txt"); + Vector NewFiles; + Set NewFeatures, NewCov; + CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures, + {}, &NewCov, CFPath, true); + for (auto &Path : NewFiles) + F->WriteToOutputCorpus(FileToVector(Path, Options.MaxLen)); + // We are done, delete the control file if it was a temporary one. + if (!Flags.merge_control_file) + RemoveFile(CFPath); + + exit(0); +} + +int AnalyzeDictionary(Fuzzer *F, const Vector& 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. - std::vector Scores(Dict.size()); - std::vector Usages(Dict.size()); + Vector Scores(Dict.size()); + Vector Usages(Dict.size()); - std::vector InitialFeatures; - std::vector ModifiedFeatures; + Vector InitialFeatures; + Vector 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) -> bool { + TPC.CollectFeatures([&](size_t Feature) { InitialFeatures.push_back(Feature); - return true; }); for (size_t i = 0; i < Dict.size(); ++i) { - auto Data = C; + Vector 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. @@ -397,9 +542,8 @@ int AnalyzeDictionary(Fuzzer *F, const std::vector& Dict, // Get coverage for testcase with masked occurrences of dictionary unit. F->ExecuteCallback(Data.data(), Data.size()); ModifiedFeatures.clear(); - TPC.CollectFeatures([&](size_t Feature) -> bool { + TPC.CollectFeatures([&](size_t Feature) { ModifiedFeatures.push_back(Feature); - return true; }); if (InitialFeatures == ModifiedFeatures) @@ -423,6 +567,45 @@ int AnalyzeDictionary(Fuzzer *F, const std::vector& Dict, return 0; } +Vector ParseSeedInuts(const char *seed_inputs) { + // Parse -seed_inputs=file1,file2,... or -seed_inputs=@seed_inputs_file + Vector Files; + if (!seed_inputs) return Files; + std::string SeedInputs; + if (Flags.seed_inputs[0] == '@') + SeedInputs = FileToString(Flags.seed_inputs + 1); // File contains list. + else + SeedInputs = Flags.seed_inputs; // seed_inputs contains the list. + if (SeedInputs.empty()) { + Printf("seed_inputs is empty or @file does not exist.\n"); + exit(1); + } + // Parse SeedInputs. + size_t comma_pos = 0; + while ((comma_pos = SeedInputs.find_last_of(',')) != std::string::npos) { + Files.push_back(SeedInputs.substr(comma_pos + 1)); + SeedInputs = SeedInputs.substr(0, comma_pos); + } + Files.push_back(SeedInputs); + return Files; +} + +static Vector ReadCorpora(const Vector &CorpusDirs, + const Vector &ExtraSeedFiles) { + Vector SizedFiles; + 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 : ExtraSeedFiles) + if (auto Size = FileSize(File)) + SizedFiles.push_back({File, Size}); + return SizedFiles; +} + int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { using namespace fuzzer; assert(argc && argv && "Argument pointers cannot be nullptr"); @@ -430,14 +613,16 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { EF = new ExternalFunctions(); if (EF->LLVMFuzzerInitialize) EF->LLVMFuzzerInitialize(argc, argv); - const std::vector Args(*argv, *argv + *argc); + if (EF->__msan_scoped_disable_interceptor_checks) + EF->__msan_scoped_disable_interceptor_checks(); + const Vector 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); + ParseFlags(Args, EF); if (Flags.help) { PrintHelp(); return 0; @@ -457,34 +642,37 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { if (Flags.workers > 0 && Flags.jobs > 0) return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs); - const size_t kMaxSaneLen = 1 << 20; - const size_t kMinDefaultLen = 64; FuzzingOptions Options; Options.Verbosity = Flags.verbosity; Options.MaxLen = Flags.max_len; - Options.ExperimentalLenControl = Flags.experimental_len_control; - if (Flags.experimental_len_control && Flags.max_len == 64) - Options.MaxLen = 1 << 20; + Options.LenControl = Flags.len_control; Options.UnitTimeoutSec = Flags.timeout; Options.ErrorExitCode = Flags.error_exitcode; Options.TimeoutExitCode = Flags.timeout_exitcode; + Options.IgnoreTimeouts = Flags.ignore_timeouts; + Options.IgnoreOOMs = Flags.ignore_ooms; + Options.IgnoreCrashes = Flags.ignore_crashes; 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.UseIndirCalls = Flags.use_indir_calls; 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.OutputCSV = Flags.output_csv; 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) @@ -494,24 +682,34 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Options.ArtifactPrefix = Flags.artifact_prefix; if (Flags.exact_artifact_path) Options.ExactArtifactPath = Flags.exact_artifact_path; - std::vector Dictionary; + Vector 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(); + bool RunIndividualFiles = AllInputsAreFiles(); Options.SaveArtifacts = - !DoPlainRun || Flags.minimize_crash_internal_step; + !RunIndividualFiles || Flags.minimize_crash_internal_step; Options.PrintNewCovPcs = Flags.print_pcs; + Options.PrintNewCovFuncs = Flags.print_funcs; Options.PrintFinalStats = Flags.print_final_stats; Options.PrintCorpusStats = Flags.print_corpus_stats; Options.PrintCoverage = Flags.print_coverage; - Options.DumpCoverage = Flags.dump_coverage; if (Flags.exit_on_src_pos) Options.ExitOnSrcPos = Flags.exit_on_src_pos; if (Flags.exit_on_item) Options.ExitOnItem = Flags.exit_on_item; + if (Flags.focus_function) + Options.FocusFunction = Flags.focus_function; + if (Flags.data_flow_trace) + Options.DataFlowTrace = Flags.data_flow_trace; + if (Flags.features_dir) + Options.FeaturesDir = Flags.features_dir; + if (Flags.collect_data_flow) + Options.CollectDataFlow = Flags.collect_data_flow; + if (Flags.stop_file) + Options.StopFile = Flags.stop_file; unsigned Seed = Flags.seed; // Initialize Seed. @@ -521,6 +719,15 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { if (Flags.verbosity) Printf("INFO: Seed: %u\n", Seed); + if (Flags.collect_data_flow && !Flags.fork && !Flags.merge) { + if (RunIndividualFiles) + return CollectDataFlow(Flags.collect_data_flow, Flags.data_flow_trace, + ReadCorpora({}, *Inputs)); + else + return CollectDataFlow(Flags.collect_data_flow, Flags.data_flow_trace, + ReadCorpora(*Inputs, {})); + } + Random Rand(Seed); auto *MD = new MutationDispatcher(Rand, Options); auto *Corpus = new InputCorpus(Options.OutputCorpus); @@ -540,40 +747,22 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { 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 (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); - F->RunOne(SMR.GetByteArray(), Size); - SMR.PostServer(); - } - return 0; - } + if (Flags.cleanse_crash) + return CleanseCrashInput(Args, Options); - if (auto Name = Flags.use_equivalence_server) { - if (!SMR.Open(Name)) { - Printf("ERROR: can't open shared memory region\n"); - return 1; - } - Printf("INFO: EQUIVALENCE CLIENT UP\n"); - } - - if (DoPlainRun) { + if (RunIndividualFiles) { Options.SaveArtifacts = false; int Runs = std::max(1, Flags.runs); Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(), @@ -595,26 +784,30 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { exit(0); } - if (Flags.merge) { - if (Options.MaxLen == 0) - F->SetMaxInputLen(kMaxSaneLen); - if (Flags.merge_control_file) - F->CrashResistantMergeInternalStep(Flags.merge_control_file); - else - F->CrashResistantMerge(Args, *Inputs); - exit(0); - } + if (Flags.fork) + FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork); - size_t TemporaryMaxLen = Options.MaxLen ? Options.MaxLen : kMaxSaneLen; + if (Flags.merge) + Merge(F, Options, Args, *Inputs, Flags.merge_control_file); - UnitVector InitialCorpus; - for (auto &Inp : *Inputs) { - Printf("Loading corpus dir: %s\n", Inp.c_str()); - ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr, - TemporaryMaxLen, /*ExitOnError=*/false); + if (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; @@ -623,25 +816,12 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Printf("Dictionary analysis failed\n"); exit(1); } - Printf("Dictionary analysis suceeded\n"); + Printf("Dictionary analysis succeeded\n"); exit(0); } - if (Options.MaxLen == 0) { - size_t MaxLen = 0; - for (auto &U : InitialCorpus) - MaxLen = std::max(U.size(), MaxLen); - F->SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxLen), kMaxSaneLen)); - } - - if (InitialCorpus.empty()) { - InitialCorpus.push_back(Unit({'\n'})); // Valid ASCII input. - if (Options.Verbosity) - Printf("INFO: A corpus is not provided, starting from an empty corpus\n"); - } - F->ShuffleAndMinimize(&InitialCorpus); - InitialCorpus.clear(); // Don't need this memory any more. - F->Loop(); + auto CorporaFiles = ReadCorpora(*Inputs, ParseSeedInuts(Flags.seed_inputs)); + F->Loop(CorporaFiles); if (Flags.verbosity) Printf("Done %zd runs in %zd second(s)\n", F->getTotalNumberOfRuns(), diff --git a/libFuzzer/Fuzzer/FuzzerExtFunctions.def b/libFuzzer/Fuzzer/FuzzerExtFunctions.def index 3bc5302..51edf84 100644 --- a/libFuzzer/Fuzzer/FuzzerExtFunctions.def +++ b/libFuzzer/Fuzzer/FuzzerExtFunctions.def @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // This defines the external function pointers that @@ -17,23 +16,26 @@ // 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), + (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), + (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, int, (), 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_print_memory_profile, int, (size_t, size_t), false); +EXT_FUNC(__sanitizer_log_write, void, (const char *buf, size_t len), false); +EXT_FUNC(__sanitizer_purge_allocator, void, (), false); +EXT_FUNC(__sanitizer_print_memory_profile, void, (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); @@ -42,5 +44,7 @@ EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int, 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); +EXT_FUNC(__msan_unpoison_param, void, (size_t n), false); diff --git a/libFuzzer/Fuzzer/FuzzerExtFunctions.h b/libFuzzer/Fuzzer/FuzzerExtFunctions.h index 2672a38..c88aac4 100644 --- a/libFuzzer/Fuzzer/FuzzerExtFunctions.h +++ b/libFuzzer/Fuzzer/FuzzerExtFunctions.h @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Defines an interface to (possibly optional) functions. diff --git a/libFuzzer/Fuzzer/FuzzerExtFunctionsDlsym.cpp b/libFuzzer/Fuzzer/FuzzerExtFunctionsDlsym.cpp index 06bddd5..dcd7134 100644 --- a/libFuzzer/Fuzzer/FuzzerExtFunctionsDlsym.cpp +++ b/libFuzzer/Fuzzer/FuzzerExtFunctionsDlsym.cpp @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Implementation for operating systems that support dlsym(). We only use it on diff --git a/libFuzzer/Fuzzer/FuzzerExtFunctionsDlsymWin.cpp b/libFuzzer/Fuzzer/FuzzerExtFunctionsDlsymWin.cpp deleted file mode 100644 index 7752169..0000000 --- a/libFuzzer/Fuzzer/FuzzerExtFunctionsDlsymWin.cpp +++ /dev/null @@ -1,60 +0,0 @@ -//===- 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" -#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 diff --git a/libFuzzer/Fuzzer/FuzzerExtFunctionsWeak.cpp b/libFuzzer/Fuzzer/FuzzerExtFunctionsWeak.cpp index 7b02b6f..ea5b87b 100644 --- a/libFuzzer/Fuzzer/FuzzerExtFunctionsWeak.cpp +++ b/libFuzzer/Fuzzer/FuzzerExtFunctionsWeak.cpp @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Implementation for Linux. This relies on the linker's support for weak @@ -13,7 +12,8 @@ // to clients right now. //===----------------------------------------------------------------------===// #include "FuzzerDefs.h" -#if LIBFUZZER_LINUX +#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FUCHSIA || \ + LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD #include "FuzzerExtFunctions.h" #include "FuzzerIO.h" @@ -21,7 +21,7 @@ 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 + __attribute__((weak, visibility("default"))) RETURN_TYPE NAME FUNC_SIG #include "FuzzerExtFunctions.def" @@ -41,7 +41,8 @@ namespace fuzzer { ExternalFunctions::ExternalFunctions() { #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ this->NAME = ::NAME; \ - CheckFnPtr((void *)::NAME, #NAME, WARN); + CheckFnPtr(reinterpret_cast(reinterpret_cast(::NAME)), \ + #NAME, WARN); #include "FuzzerExtFunctions.def" @@ -50,4 +51,4 @@ ExternalFunctions::ExternalFunctions() { } // namespace fuzzer -#endif // LIBFUZZER_LINUX +#endif diff --git a/libFuzzer/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp b/libFuzzer/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp deleted file mode 100644 index e10f7b4..0000000 --- a/libFuzzer/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp +++ /dev/null @@ -1,56 +0,0 @@ -//===- 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 -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(::NAME, ::NAME##Def, #NAME, WARN); - -#include "FuzzerExtFunctions.def" - -#undef EXT_FUNC -} - -} // namespace fuzzer - -#endif // LIBFUZZER_WINDOWS diff --git a/libFuzzer/Fuzzer/FuzzerExtFunctionsWindows.cpp b/libFuzzer/Fuzzer/FuzzerExtFunctionsWindows.cpp new file mode 100644 index 0000000..55efe8f --- /dev/null +++ b/libFuzzer/Fuzzer/FuzzerExtFunctionsWindows.cpp @@ -0,0 +1,82 @@ +//=== FuzzerExtWindows.cpp - Interface to external functions --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Implementation of FuzzerExtFunctions for Windows. Uses alternatename when +// compiled with MSVC. Uses weak aliases when compiled with clang. Unfortunately +// the method each compiler supports is not supported by the other. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_WINDOWS + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" + +using namespace fuzzer; + +// Intermediate macro to ensure the parameter is expanded before stringified. +#define STRINGIFY_(A) #A +#define STRINGIFY(A) STRINGIFY_(A) + +#if LIBFUZZER_MSVC +// Copied from compiler-rt/lib/sanitizer_common/sanitizer_win_defs.h +#if defined(_M_IX86) || defined(__i386__) +#define WIN_SYM_PREFIX "_" +#else +#define WIN_SYM_PREFIX +#endif + +// Declare external functions as having alternativenames, so that we can +// determine if they are not defined. +#define EXTERNAL_FUNC(Name, Default) \ + __pragma(comment(linker, "/alternatename:" WIN_SYM_PREFIX STRINGIFY( \ + Name) "=" WIN_SYM_PREFIX STRINGIFY(Default))) +#else +// Declare external functions as weak to allow them to default to a specified +// function if not defined explicitly. We must use weak symbols because clang's +// support for alternatename is not 100%, see +// https://bugs.llvm.org/show_bug.cgi?id=40218 for more details. +#define EXTERNAL_FUNC(Name, Default) \ + __attribute__((weak, alias(STRINGIFY(Default)))) +#endif // LIBFUZZER_MSVC + +extern "C" { +#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); \ + } \ + EXTERNAL_FUNC(NAME, NAME##Def) RETURN_TYPE NAME FUNC_SIG + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +template +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(::NAME, ::NAME##Def, #NAME, WARN); + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS diff --git a/libFuzzer/Fuzzer/FuzzerExtraCounters.cpp b/libFuzzer/Fuzzer/FuzzerExtraCounters.cpp new file mode 100644 index 0000000..3f38f4f --- /dev/null +++ b/libFuzzer/Fuzzer/FuzzerExtraCounters.cpp @@ -0,0 +1,41 @@ +//===- FuzzerExtraCounters.cpp - Extra coverage counters ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// 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(ExtraCountersBegin()); + uintptr_t *End = reinterpret_cast(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 diff --git a/libFuzzer/Fuzzer/FuzzerFlags.def b/libFuzzer/Fuzzer/FuzzerFlags.def index 4428344..0e19a9c 100644 --- a/libFuzzer/Fuzzer/FuzzerFlags.def +++ b/libFuzzer/Fuzzer/FuzzerFlags.def @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the @@ -17,10 +16,18 @@ FUZZER_FLAG_INT(runs, -1, 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(experimental_len_control, 0, "experimental flag") +FUZZER_FLAG_INT(len_control, 100, "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. Default value is 0, if LLVMFuzzerCustomMutator is used.") +FUZZER_FLAG_STRING(seed_inputs, "A comma-separated list of input files " + "to use as an additional seed corpus. Alternatively, an \"@\" followed by " + "the name of a file containing the comma-seperated list.") 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.") @@ -30,27 +37,51 @@ FUZZER_FLAG_INT( "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 " +FUZZER_FLAG_INT(timeout_exitcode, 70, "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(fork, 0, "Experimental mode where fuzzing happens " + "in a subprocess") +FUZZER_FLAG_INT(ignore_timeouts, 1, "Ignore timeouts in fork mode") +FUZZER_FLAG_INT(ignore_ooms, 1, "Ignore OOMs in fork mode") +FUZZER_FLAG_INT(ignore_crashes, 0, "Ignore crashes in fork mode") FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be " "merged into the 1-st corpus. Only interesting units will be taken. " "This flag can be used to minimize a corpus.") -FUZZER_FLAG_STRING(merge_control_file, "internal flag") +FUZZER_FLAG_STRING(stop_file, "Stop fuzzing ASAP if this file exists") +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_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") + "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_STRING(features_dir, "internal flag. Used to dump feature sets on disk." + "Every time a new input is added to the corpus, a corresponding file in the features_dir" + " is created containing the unique features of that input." + " Features are stored in binary format.") FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters") -FUZZER_FLAG_INT(use_indir_calls, 1, "Use indirect caller-callee counters") FUZZER_FLAG_INT(use_memmem, 1, "Use hints from intercepting memmem, strstr, etc") FUZZER_FLAG_INT(use_value_profile, 0, "Experimental. Use value profile to guide fuzzing.") FUZZER_FLAG_INT(use_cmp, 1, "Use CMP traces to guide mutations") -FUZZER_FLAG_INT(shrink, 0, "Experimental. Try to shrink corpus elements.") +FUZZER_FLAG_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.") @@ -73,47 +104,57 @@ FUZZER_FLAG_STRING(exact_artifact_path, "as $(exact_artifact_path). This overrides -artifact_prefix " "and will not use checksum in the file name. Do not " "use the same path for several parallel processes.") -FUZZER_FLAG_INT(output_csv, 0, "Enable pulse output in CSV format.") FUZZER_FLAG_INT(print_pcs, 0, "If 1, print out newly covered PCs.") +FUZZER_FLAG_INT(print_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 at exit." - " Experimental, only with trace-pc-guard") -FUZZER_FLAG_INT(dump_coverage, 0, "If 1, dump coverage information at exit." - " Experimental, only with trace-pc-guard") +FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information as text" + " at exit.") +FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated.") FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.") -FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGSEGV.") +FUZZER_FLAG_INT(handle_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. asan's stderr/stdout.") + "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 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. " + "If -focus_function=auto and -data_flow_trace is used, libFuzzer " + "will choose the focus functions automatically.") -FUZZER_FLAG_STRING(run_equivalence_server, "Experimental") -FUZZER_FLAG_STRING(use_equivalence_server, "Experimental") FUZZER_FLAG_INT(analyze_dict, 0, "Experimental") - -FUZZER_DEPRECATED_FLAG(exit_on_first) -FUZZER_DEPRECATED_FLAG(save_minimized_corpus) -FUZZER_DEPRECATED_FLAG(sync_command) -FUZZER_DEPRECATED_FLAG(sync_timeout) -FUZZER_DEPRECATED_FLAG(test_single_input) -FUZZER_DEPRECATED_FLAG(drill) -FUZZER_DEPRECATED_FLAG(truncate_units) +FUZZER_DEPRECATED_FLAG(use_clang_coverage) +FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace") +FUZZER_FLAG_STRING(collect_data_flow, + "Experimental: collect the data flow trace") diff --git a/libFuzzer/Fuzzer/FuzzerFnAdapter.h b/libFuzzer/Fuzzer/FuzzerFnAdapter.h deleted file mode 100644 index eb2c219..0000000 --- a/libFuzzer/Fuzzer/FuzzerFnAdapter.h +++ /dev/null @@ -1,187 +0,0 @@ -//===- FuzzerAdapter.h - Arbitrary function Fuzzer adapter -------*- C++ -*===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// W A R N I N G : E X P E R I M E N T A L. -// -// Defines an adapter to fuzz functions with (almost) arbitrary signatures. -//===----------------------------------------------------------------------===// - -#ifndef LLVM_FUZZER_ADAPTER_H -#define LLVM_FUZZER_ADAPTER_H - -#include -#include - -#include -#include -#include -#include - -namespace fuzzer { - -/// Unpacks bytes from \p Data according to \p F argument types -/// and calls the function. -/// Use to automatically adapt LLVMFuzzerTestOneInput interface to -/// a specific function. -/// Supported argument types: primitive types, std::vector. -template bool Adapt(Fn F, const uint8_t *Data, size_t Size); - -// The implementation performs several steps: -// - function argument types are obtained (Args...) -// - data is unpacked into std::tuple one by one -// - function is called with std::tuple containing arguments. -namespace impl { - -// Single argument unpacking. - -template -size_t UnpackPrimitive(const uint8_t *Data, size_t Size, T *Value) { - if (Size < sizeof(T)) - return Size; - *Value = *reinterpret_cast(Data); - return Size - sizeof(T); -} - -/// Unpacks into a given Value and returns the Size - num_consumed_bytes. -/// Return value equal to Size signals inability to unpack the data (typically -/// because there are not enough bytes). -template -size_t UnpackSingle(const uint8_t *Data, size_t Size, T *Value); - -#define UNPACK_SINGLE_PRIMITIVE(Type) \ - template <> \ - size_t UnpackSingle(const uint8_t *Data, size_t Size, Type *Value) { \ - return UnpackPrimitive(Data, Size, Value); \ - } - -UNPACK_SINGLE_PRIMITIVE(char) -UNPACK_SINGLE_PRIMITIVE(signed char) -UNPACK_SINGLE_PRIMITIVE(unsigned char) - -UNPACK_SINGLE_PRIMITIVE(short int) -UNPACK_SINGLE_PRIMITIVE(unsigned short int) - -UNPACK_SINGLE_PRIMITIVE(int) -UNPACK_SINGLE_PRIMITIVE(unsigned int) - -UNPACK_SINGLE_PRIMITIVE(long int) -UNPACK_SINGLE_PRIMITIVE(unsigned long int) - -UNPACK_SINGLE_PRIMITIVE(bool) -UNPACK_SINGLE_PRIMITIVE(wchar_t) - -UNPACK_SINGLE_PRIMITIVE(float) -UNPACK_SINGLE_PRIMITIVE(double) -UNPACK_SINGLE_PRIMITIVE(long double) - -#undef UNPACK_SINGLE_PRIMITIVE - -template <> -size_t UnpackSingle>(const uint8_t *Data, size_t Size, - std::vector *Value) { - if (Size < 1) - return Size; - size_t Len = std::min(static_cast(*Data), Size - 1); - std::vector V(Data + 1, Data + 1 + Len); - Value->swap(V); - return Size - Len - 1; -} - -template <> -size_t UnpackSingle(const uint8_t *Data, size_t Size, - std::string *Value) { - if (Size < 1) - return Size; - size_t Len = std::min(static_cast(*Data), Size - 1); - std::string S(Data + 1, Data + 1 + Len); - Value->swap(S); - return Size - Len - 1; -} - -// Unpacking into arbitrary tuple. - -// Recursion guard. -template -typename std::enable_if::value, bool>::type -UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) { - return true; -} - -// Unpack tuple elements starting from Nth. -template -typename std::enable_if::value, bool>::type -UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) { - size_t NewSize = UnpackSingle(Data, Size, &std::get(*Tuple)); - if (NewSize == Size) { - return false; - } - - return UnpackImpl(Data + (Size - NewSize), NewSize, Tuple); -} - -// Unpacks into arbitrary tuple and returns true if successful. -template -bool Unpack(const uint8_t *Data, size_t Size, std::tuple *Tuple) { - return UnpackImpl<0, std::tuple>(Data, Size, Tuple); -} - -// Helper integer sequence templates. - -template struct Seq {}; - -template struct GenSeq : GenSeq {}; - -// GenSeq::type is Seq<0, 1, ..., N-1> -template struct GenSeq<0, S...> { typedef Seq type; }; - -// Function signature introspection. - -template struct FnTraits {}; - -template -struct FnTraits { - enum { Arity = sizeof...(Args) }; - typedef std::tuple ArgsTupleT; -}; - -// Calling a function with arguments in a tuple. - -template -void ApplyImpl(Fn F, const typename FnTraits::ArgsTupleT &Params, - Seq) { - F(std::get(Params)...); -} - -template -void Apply(Fn F, const typename FnTraits::ArgsTupleT &Params) { - // S is Seq<0, ..., Arity-1> - auto S = typename GenSeq::Arity>::type(); - ApplyImpl(F, Params, S); -} - -// Unpacking data into arguments tuple of correct type and calling the function. -template -bool UnpackAndApply(Fn F, const uint8_t *Data, size_t Size) { - typename FnTraits::ArgsTupleT Tuple; - if (!Unpack(Data, Size, &Tuple)) - return false; - - Apply(F, Tuple); - return true; -} - -} // namespace impl - -template bool Adapt(Fn F, const uint8_t *Data, size_t Size) { - return impl::UnpackAndApply(F, Data, Size); -} - -} // namespace fuzzer - -#endif diff --git a/libFuzzer/Fuzzer/FuzzerFork.cpp b/libFuzzer/Fuzzer/FuzzerFork.cpp new file mode 100644 index 0000000..95ed365 --- /dev/null +++ b/libFuzzer/Fuzzer/FuzzerFork.cpp @@ -0,0 +1,409 @@ +//===- FuzzerFork.cpp - run fuzzing in separate subprocesses --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Spawn and orchestrate separate fuzzing processes. +//===----------------------------------------------------------------------===// + +#include "FuzzerCommand.h" +#include "FuzzerFork.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include "FuzzerMerge.h" +#include "FuzzerSHA1.h" +#include "FuzzerTracePC.h" +#include "FuzzerUtil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +struct Stats { + size_t number_of_executed_units = 0; + size_t peak_rss_mb = 0; + size_t average_exec_per_sec = 0; +}; + +static Stats ParseFinalStatsFromLog(const std::string &LogPath) { + std::ifstream In(LogPath); + std::string Line; + Stats Res; + struct { + const char *Name; + size_t *Var; + } NameVarPairs[] = { + {"stat::number_of_executed_units:", &Res.number_of_executed_units}, + {"stat::peak_rss_mb:", &Res.peak_rss_mb}, + {"stat::average_exec_per_sec:", &Res.average_exec_per_sec}, + {nullptr, nullptr}, + }; + while (std::getline(In, Line, '\n')) { + if (Line.find("stat::") != 0) continue; + std::istringstream ISS(Line); + std::string Name; + size_t Val; + ISS >> Name >> Val; + for (size_t i = 0; NameVarPairs[i].Name; i++) + if (Name == NameVarPairs[i].Name) + *NameVarPairs[i].Var = Val; + } + return Res; +} + +struct FuzzJob { + // Inputs. + Command Cmd; + std::string CorpusDir; + std::string FeaturesDir; + std::string LogPath; + std::string SeedListPath; + std::string CFPath; + size_t JobId; + + int DftTimeInSeconds = 0; + + // Fuzzing Outputs. + int ExitCode; + + ~FuzzJob() { + RemoveFile(CFPath); + RemoveFile(LogPath); + RemoveFile(SeedListPath); + RmDirRecursive(CorpusDir); + RmDirRecursive(FeaturesDir); + } +}; + +struct GlobalEnv { + Vector Args; + Vector CorpusDirs; + std::string MainCorpusDir; + std::string TempDir; + std::string DFTDir; + std::string DataFlowBinary; + Set Features, Cov; + Set FilesWithDFT; + Vector Files; + Random *Rand; + std::chrono::system_clock::time_point ProcessStartTime; + int Verbosity = 0; + + size_t NumTimeouts = 0; + size_t NumOOMs = 0; + size_t NumCrashes = 0; + + + size_t NumRuns = 0; + + std::string StopFile() { return DirPlusFile(TempDir, "STOP"); } + + size_t secondsSinceProcessStartUp() const { + return std::chrono::duration_cast( + std::chrono::system_clock::now() - ProcessStartTime) + .count(); + } + + FuzzJob *CreateNewJob(size_t JobId) { + Command Cmd(Args); + Cmd.removeFlag("fork"); + Cmd.removeFlag("runs"); + Cmd.removeFlag("collect_data_flow"); + for (auto &C : CorpusDirs) // Remove all corpora from the args. + Cmd.removeArgument(C); + Cmd.addFlag("reload", "0"); // working in an isolated dir, no reload. + Cmd.addFlag("print_final_stats", "1"); + Cmd.addFlag("print_funcs", "0"); // no need to spend time symbolizing. + Cmd.addFlag("max_total_time", std::to_string(std::min((size_t)300, JobId))); + Cmd.addFlag("stop_file", StopFile()); + if (!DataFlowBinary.empty()) { + Cmd.addFlag("data_flow_trace", DFTDir); + if (!Cmd.hasFlag("focus_function")) + Cmd.addFlag("focus_function", "auto"); + } + auto Job = new FuzzJob; + std::string Seeds; + if (size_t CorpusSubsetSize = + std::min(Files.size(), (size_t)sqrt(Files.size() + 2))) { + auto Time1 = std::chrono::system_clock::now(); + for (size_t i = 0; i < CorpusSubsetSize; i++) { + auto &SF = Files[Rand->SkewTowardsLast(Files.size())]; + Seeds += (Seeds.empty() ? "" : ",") + SF; + CollectDFT(SF); + } + auto Time2 = std::chrono::system_clock::now(); + Job->DftTimeInSeconds = duration_cast(Time2 - Time1).count(); + } + if (!Seeds.empty()) { + Job->SeedListPath = + DirPlusFile(TempDir, std::to_string(JobId) + ".seeds"); + WriteToFile(Seeds, Job->SeedListPath); + Cmd.addFlag("seed_inputs", "@" + Job->SeedListPath); + } + Job->LogPath = DirPlusFile(TempDir, std::to_string(JobId) + ".log"); + Job->CorpusDir = DirPlusFile(TempDir, "C" + std::to_string(JobId)); + Job->FeaturesDir = DirPlusFile(TempDir, "F" + std::to_string(JobId)); + Job->CFPath = DirPlusFile(TempDir, std::to_string(JobId) + ".merge"); + Job->JobId = JobId; + + + Cmd.addArgument(Job->CorpusDir); + Cmd.addFlag("features_dir", Job->FeaturesDir); + + for (auto &D : {Job->CorpusDir, Job->FeaturesDir}) { + RmDirRecursive(D); + MkDir(D); + } + + Cmd.setOutputFile(Job->LogPath); + Cmd.combineOutAndErr(); + + Job->Cmd = Cmd; + + if (Verbosity >= 2) + Printf("Job %zd/%p Created: %s\n", JobId, Job, + Job->Cmd.toString().c_str()); + // Start from very short runs and gradually increase them. + return Job; + } + + void RunOneMergeJob(FuzzJob *Job) { + auto Stats = ParseFinalStatsFromLog(Job->LogPath); + NumRuns += Stats.number_of_executed_units; + + Vector TempFiles, MergeCandidates; + // Read all newly created inputs and their feature sets. + // Choose only those inputs that have new features. + GetSizedFilesFromDir(Job->CorpusDir, &TempFiles); + std::sort(TempFiles.begin(), TempFiles.end()); + for (auto &F : TempFiles) { + auto FeatureFile = F.File; + FeatureFile.replace(0, Job->CorpusDir.size(), Job->FeaturesDir); + auto FeatureBytes = FileToVector(FeatureFile, 0, false); + assert((FeatureBytes.size() % sizeof(uint32_t)) == 0); + Vector NewFeatures(FeatureBytes.size() / sizeof(uint32_t)); + memcpy(NewFeatures.data(), FeatureBytes.data(), FeatureBytes.size()); + for (auto Ft : NewFeatures) { + if (!Features.count(Ft)) { + MergeCandidates.push_back(F); + break; + } + } + } + // if (!FilesToAdd.empty() || Job->ExitCode != 0) + Printf("#%zd: cov: %zd ft: %zd corp: %zd exec/s %zd " + "oom/timeout/crash: %zd/%zd/%zd time: %zds job: %zd dft_time: %d\n", + NumRuns, Cov.size(), Features.size(), Files.size(), + Stats.average_exec_per_sec, NumOOMs, NumTimeouts, NumCrashes, + secondsSinceProcessStartUp(), Job->JobId, Job->DftTimeInSeconds); + + if (MergeCandidates.empty()) return; + + Vector FilesToAdd; + Set NewFeatures, NewCov; + CrashResistantMerge(Args, {}, MergeCandidates, &FilesToAdd, Features, + &NewFeatures, Cov, &NewCov, Job->CFPath, false); + for (auto &Path : FilesToAdd) { + auto U = FileToVector(Path); + auto NewPath = DirPlusFile(MainCorpusDir, Hash(U)); + WriteToFile(U, NewPath); + Files.push_back(NewPath); + } + Features.insert(NewFeatures.begin(), NewFeatures.end()); + Cov.insert(NewCov.begin(), NewCov.end()); + for (auto Idx : NewCov) + if (auto *TE = TPC.PCTableEntryByIdx(Idx)) + if (TPC.PcIsFuncEntry(TE)) + PrintPC(" NEW_FUNC: %p %F %L\n", "", + TPC.GetNextInstructionPc(TE->PC)); + + } + + + void CollectDFT(const std::string &InputPath) { + if (DataFlowBinary.empty()) return; + if (!FilesWithDFT.insert(InputPath).second) return; + Command Cmd(Args); + Cmd.removeFlag("fork"); + Cmd.removeFlag("runs"); + Cmd.addFlag("data_flow_trace", DFTDir); + Cmd.addArgument(InputPath); + for (auto &C : CorpusDirs) // Remove all corpora from the args. + Cmd.removeArgument(C); + Cmd.setOutputFile(DirPlusFile(TempDir, "dft.log")); + Cmd.combineOutAndErr(); + // Printf("CollectDFT: %s\n", Cmd.toString().c_str()); + ExecuteCommand(Cmd); + } + +}; + +struct JobQueue { + std::queue Qu; + std::mutex Mu; + std::condition_variable Cv; + + void Push(FuzzJob *Job) { + { + std::lock_guard Lock(Mu); + Qu.push(Job); + } + Cv.notify_one(); + } + FuzzJob *Pop() { + std::unique_lock Lk(Mu); + // std::lock_guard Lock(Mu); + Cv.wait(Lk, [&]{return !Qu.empty();}); + assert(!Qu.empty()); + auto Job = Qu.front(); + Qu.pop(); + return Job; + } +}; + +void WorkerThread(JobQueue *FuzzQ, JobQueue *MergeQ) { + while (auto Job = FuzzQ->Pop()) { + // Printf("WorkerThread: job %p\n", Job); + Job->ExitCode = ExecuteCommand(Job->Cmd); + MergeQ->Push(Job); + } +} + +// This is just a skeleton of an experimental -fork=1 feature. +void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, + const Vector &Args, + const Vector &CorpusDirs, int NumJobs) { + Printf("INFO: -fork=%d: fuzzing in separate process(s)\n", NumJobs); + + GlobalEnv Env; + Env.Args = Args; + Env.CorpusDirs = CorpusDirs; + Env.Rand = &Rand; + Env.Verbosity = Options.Verbosity; + Env.ProcessStartTime = std::chrono::system_clock::now(); + Env.DataFlowBinary = Options.CollectDataFlow; + + Vector SeedFiles; + for (auto &Dir : CorpusDirs) + GetSizedFilesFromDir(Dir, &SeedFiles); + std::sort(SeedFiles.begin(), SeedFiles.end()); + Env.TempDir = TempPath(".dir"); + Env.DFTDir = DirPlusFile(Env.TempDir, "DFT"); + RmDirRecursive(Env.TempDir); // in case there is a leftover from old runs. + MkDir(Env.TempDir); + MkDir(Env.DFTDir); + + + if (CorpusDirs.empty()) + MkDir(Env.MainCorpusDir = DirPlusFile(Env.TempDir, "C")); + else + Env.MainCorpusDir = CorpusDirs[0]; + + auto CFPath = DirPlusFile(Env.TempDir, "merge.txt"); + CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features, + {}, &Env.Cov, + CFPath, false); + RemoveFile(CFPath); + Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs, + Env.Files.size(), Env.TempDir.c_str()); + + int ExitCode = 0; + + JobQueue FuzzQ, MergeQ; + + auto StopJobs = [&]() { + for (int i = 0; i < NumJobs; i++) + FuzzQ.Push(nullptr); + MergeQ.Push(nullptr); + WriteToFile(Unit({1}), Env.StopFile()); + }; + + size_t JobId = 1; + Vector Threads; + for (int t = 0; t < NumJobs; t++) { + Threads.push_back(std::thread(WorkerThread, &FuzzQ, &MergeQ)); + FuzzQ.Push(Env.CreateNewJob(JobId++)); + } + + while (true) { + std::unique_ptr Job(MergeQ.Pop()); + if (!Job) + break; + ExitCode = Job->ExitCode; + if (ExitCode == Options.InterruptExitCode) { + Printf("==%lu== libFuzzer: a child was interrupted; exiting\n", GetPid()); + StopJobs(); + break; + } + Fuzzer::MaybeExitGracefully(); + + Env.RunOneMergeJob(Job.get()); + + // Continue if our crash is one of the ignorred ones. + if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode) + Env.NumTimeouts++; + else if (Options.IgnoreOOMs && ExitCode == Options.OOMExitCode) + Env.NumOOMs++; + else if (ExitCode != 0) { + Env.NumCrashes++; + if (Options.IgnoreCrashes) { + std::ifstream In(Job->LogPath); + std::string Line; + while (std::getline(In, Line, '\n')) + if (Line.find("ERROR:") != Line.npos || + Line.find("runtime error:") != Line.npos) + Printf("%s\n", Line.c_str()); + } else { + // And exit if we don't ignore this crash. + Printf("INFO: log from the inner process:\n%s", + FileToString(Job->LogPath).c_str()); + StopJobs(); + break; + } + } + + // Stop if we are over the time budget. + // This is not precise, since other threads are still running + // and we will wait while joining them. + // We also don't stop instantly: other jobs need to finish. + if (Options.MaxTotalTimeSec > 0 && + Env.secondsSinceProcessStartUp() >= (size_t)Options.MaxTotalTimeSec) { + Printf("INFO: fuzzed for %zd seconds, wrapping up soon\n", + Env.secondsSinceProcessStartUp()); + StopJobs(); + break; + } + if (Env.NumRuns >= Options.MaxNumberOfRuns) { + Printf("INFO: fuzzed for %zd iterations, wrapping up soon\n", + Env.NumRuns); + StopJobs(); + break; + } + + FuzzQ.Push(Env.CreateNewJob(JobId++)); + } + + for (auto &T : Threads) + T.join(); + + // The workers have terminated. Don't try to remove the directory before they + // terminate to avoid a race condition preventing cleanup on Windows. + RmDirRecursive(Env.TempDir); + + // Use the exit code from the last child process. + Printf("INFO: exiting: %d time: %zds\n", ExitCode, + Env.secondsSinceProcessStartUp()); + exit(ExitCode); +} + +} // namespace fuzzer diff --git a/libFuzzer/Fuzzer/FuzzerFork.h b/libFuzzer/Fuzzer/FuzzerFork.h new file mode 100644 index 0000000..b29a43e --- /dev/null +++ b/libFuzzer/Fuzzer/FuzzerFork.h @@ -0,0 +1,24 @@ +//===- FuzzerFork.h - run fuzzing in sub-processes --------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_FORK_H +#define LLVM_FUZZER_FORK_H + +#include "FuzzerDefs.h" +#include "FuzzerOptions.h" +#include "FuzzerRandom.h" + +#include + +namespace fuzzer { +void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, + const Vector &Args, + const Vector &CorpusDirs, int NumJobs); +} // namespace fuzzer + +#endif // LLVM_FUZZER_FORK_H diff --git a/libFuzzer/Fuzzer/FuzzerIO.cpp b/libFuzzer/Fuzzer/FuzzerIO.cpp index e3f609e..7e5ba30 100644 --- a/libFuzzer/Fuzzer/FuzzerIO.cpp +++ b/libFuzzer/Fuzzer/FuzzerIO.cpp @@ -1,17 +1,17 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // IO functions. //===----------------------------------------------------------------------===// -#include "FuzzerIO.h" #include "FuzzerDefs.h" #include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerUtil.h" #include #include #include @@ -31,14 +31,16 @@ long GetEpoch(const std::string &Path) { } Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) { - std::ifstream T(Path); + std::ifstream T(Path, std::ios::binary); if (ExitOnError && !T) { Printf("No such directory: %s; exiting\n", Path.c_str()); exit(1); } T.seekg(0, T.end); - size_t FileLen = T.tellg(); + auto EndPos = T.tellg(); + if (EndPos < 0) return {}; + size_t FileLen = EndPos; if (MaxSize) FileLen = std::min(FileLen, MaxSize); @@ -49,7 +51,7 @@ Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) { } std::string FileToString(const std::string &Path) { - std::ifstream T(Path); + std::ifstream T(Path, std::ios::binary); return std::string((std::istreambuf_iterator(T)), std::istreambuf_iterator()); } @@ -59,17 +61,26 @@ void CopyFileToErr(const std::string &Path) { } void WriteToFile(const Unit &U, const std::string &Path) { + WriteToFile(U.data(), U.size(), Path); +} + +void WriteToFile(const std::string &Data, const std::string &Path) { + WriteToFile(reinterpret_cast(Data.c_str()), Data.size(), + Path); +} + +void WriteToFile(const uint8_t *Data, size_t Size, 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"); + FILE *Out = fopen(Path.c_str(), "wb"); if (!Out) return; - fwrite(U.data(), sizeof(U[0]), U.size(), Out); + fwrite(Data, sizeof(Data[0]), Size, Out); fclose(Out); } -void ReadDirToVectorOfUnits(const char *Path, std::vector *V, +void ReadDirToVectorOfUnits(const char *Path, Vector *V, long *Epoch, size_t MaxSize, bool ExitOnError) { long E = Epoch ? *Epoch : 0; - std::vector Files; + Vector Files; ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true); size_t NumLoaded = 0; for (size_t i = 0; i < Files.size(); i++) { @@ -84,6 +95,15 @@ void ReadDirToVectorOfUnits(const char *Path, std::vector *V, } } + +void GetSizedFilesFromDir(const std::string &Dir, Vector *V) { + Vector 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; @@ -115,4 +135,25 @@ void Printf(const char *Fmt, ...) { fflush(OutputFile); } +void VPrintf(bool Verbose, const char *Fmt, ...) { + if (!Verbose) return; + va_list ap; + va_start(ap, Fmt); + vfprintf(OutputFile, Fmt, ap); + va_end(ap); + fflush(OutputFile); +} + +void RmDirRecursive(const std::string &Dir) { + IterateDirRecursive( + Dir, [](const std::string &Path) {}, + [](const std::string &Path) { RmDir(Path); }, + [](const std::string &Path) { RemoveFile(Path); }); +} + +std::string TempPath(const char *Extension) { + return DirPlusFile(TmpDir(), + "libFuzzerTemp." + std::to_string(GetPid()) + Extension); +} + } // namespace fuzzer diff --git a/libFuzzer/Fuzzer/FuzzerIO.h b/libFuzzer/Fuzzer/FuzzerIO.h index 3b66a52..fe0d7b4 100644 --- a/libFuzzer/Fuzzer/FuzzerIO.h +++ b/libFuzzer/Fuzzer/FuzzerIO.h @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // IO interface. @@ -25,9 +24,12 @@ std::string FileToString(const std::string &Path); void CopyFileToErr(const std::string &Path); +void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path); +// Write Data.c_str() to the file without terminating null character. +void WriteToFile(const std::string &Data, const std::string &Path); void WriteToFile(const Unit &U, const std::string &Path); -void ReadDirToVectorOfUnits(const char *Path, std::vector *V, +void ReadDirToVectorOfUnits(const char *Path, Vector *V, long *Epoch, size_t MaxSize, bool ExitOnError); // Returns "Dir/FileName" or equivalent for the current OS. @@ -40,6 +42,8 @@ std::string DirName(const std::string &FileName); // Returns path to a TmpDir. std::string TmpDir(); +std::string TempPath(const char *Extension); + bool IsInterestingCoverageFile(const std::string &FileName); void DupAndCloseStderr(); @@ -47,17 +51,39 @@ void DupAndCloseStderr(); void CloseStdout(); void Printf(const char *Fmt, ...); +void VPrintf(bool Verbose, 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, - std::vector *V, bool TopDir); + Vector *V, bool TopDir); + +void RmDirRecursive(const std::string &Dir); + +// Iterate files and dirs inside Dir, recursively. +// Call DirPreCallback/DirPostCallback on dirs before/after +// calling FileCallback on files. +void IterateDirRecursive(const std::string &Dir, + void (*DirPreCallback)(const std::string &Dir), + void (*DirPostCallback)(const std::string &Dir), + void (*FileCallback)(const std::string &Dir)); + +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 *V); char GetSeparator(); +// Similar to the basename utility: returns the file name w/o the dir prefix. +std::string Basename(const std::string &Path); FILE* OpenFile(int Fd, const char *Mode); @@ -66,11 +92,17 @@ int CloseFile(int Fd); int DuplicateFile(int Fd); void RemoveFile(const std::string &Path); +void RenameFile(const std::string &OldPath, const std::string &NewPath); void DiscardOutput(int Fd); intptr_t GetHandleFromFd(int fd); +void MkDir(const std::string &Path); +void RmDir(const std::string &Path); + +const std::string &getDevNull(); + } // namespace fuzzer #endif // LLVM_FUZZER_IO_H diff --git a/libFuzzer/Fuzzer/FuzzerIOPosix.cpp b/libFuzzer/Fuzzer/FuzzerIOPosix.cpp index c5ebdba..cfd69bb 100644 --- a/libFuzzer/Fuzzer/FuzzerIOPosix.cpp +++ b/libFuzzer/Fuzzer/FuzzerIOPosix.cpp @@ -1,15 +1,14 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // IO functions implementation using Posix API. //===----------------------------------------------------------------------===// #include "FuzzerDefs.h" -#if LIBFUZZER_POSIX +#if LIBFUZZER_POSIX || LIBFUZZER_FUCHSIA #include "FuzzerExtFunctions.h" #include "FuzzerIO.h" @@ -32,22 +31,46 @@ bool IsFile(const std::string &Path) { 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; +} + +std::string Basename(const std::string &Path) { + size_t Pos = Path.rfind(GetSeparator()); + if (Pos == std::string::npos) return Path; + assert(Pos < Path.size()); + return Path.substr(Pos + 1); +} + void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, - std::vector *V, bool TopDir) { + Vector *V, bool TopDir) { auto E = GetEpoch(Dir); if (Epoch) if (E && *Epoch >= E) return; DIR *D = opendir(Dir.c_str()); if (!D) { - Printf("No such directory: %s; exiting\n", Dir.c_str()); + 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) + 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_name != '.') + else if ((E->d_type == DT_DIR || + (E->d_type == DT_UNKNOWN && IsDirectory(Path))) && + *E->d_name != '.') ListFilesInDirRecursive(Path, Epoch, V, false); } closedir(D); @@ -55,6 +78,28 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, *Epoch = E; } + +void IterateDirRecursive(const std::string &Dir, + void (*DirPreCallback)(const std::string &Dir), + void (*DirPostCallback)(const std::string &Dir), + void (*FileCallback)(const std::string &Dir)) { + DirPreCallback(Dir); + DIR *D = opendir(Dir.c_str()); + if (!D) return; + 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))) + FileCallback(Path); + else if ((E->d_type == DT_DIR || + (E->d_type == DT_UNKNOWN && IsDirectory(Path))) && + *E->d_name != '.') + IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback); + } + closedir(D); + DirPostCallback(Dir); +} + char GetSeparator() { return '/'; } @@ -75,6 +120,10 @@ void RemoveFile(const std::string &Path) { unlink(Path.c_str()); } +void RenameFile(const std::string &OldPath, const std::string &NewPath) { + rename(OldPath.c_str(), NewPath.c_str()); +} + void DiscardOutput(int Fd) { FILE* Temp = fopen("/dev/null", "w"); if (!Temp) @@ -113,11 +162,23 @@ bool IsInterestingCoverageFile(const std::string &FileName) { return true; } - void RawPrint(const char *Str) { write(2, Str, strlen(Str)); } +void MkDir(const std::string &Path) { + mkdir(Path.c_str(), 0700); +} + +void RmDir(const std::string &Path) { + rmdir(Path.c_str()); +} + +const std::string &getDevNull() { + static const std::string devNull = "/dev/null"; + return devNull; +} + } // namespace fuzzer #endif // LIBFUZZER_POSIX diff --git a/libFuzzer/Fuzzer/FuzzerIOWindows.cpp b/libFuzzer/Fuzzer/FuzzerIOWindows.cpp index 75d4e3a..510afeb 100644 --- a/libFuzzer/Fuzzer/FuzzerIOWindows.cpp +++ b/libFuzzer/Fuzzer/FuzzerIOWindows.cpp @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // IO functions implementation for Windows. @@ -72,8 +71,35 @@ bool IsFile(const std::string &Path) { return IsFile(Path, Att); } +static bool IsDir(DWORD FileAttrs) { + if (FileAttrs == INVALID_FILE_ATTRIBUTES) return false; + return FileAttrs & FILE_ATTRIBUTE_DIRECTORY; +} + +std::string Basename(const std::string &Path) { + size_t Pos = Path.find_last_of("/\\"); + if (Pos == std::string::npos) return Path; + assert(Pos < Path.size()); + return Path.substr(Pos + 1); +} + +size_t FileSize(const std::string &Path) { + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) { + DWORD LastError = GetLastError(); + if (LastError != ERROR_FILE_NOT_FOUND) + Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), LastError); + return 0; + } + ULARGE_INTEGER size; + size.HighPart = attr.nFileSizeHigh; + size.LowPart = attr.nFileSizeLow; + return size.QuadPart; +} + void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, - std::vector *V, bool TopDir) { + Vector *V, bool TopDir) { auto E = GetEpoch(Dir); if (Epoch) if (E && *Epoch >= E) return; @@ -91,7 +117,7 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, { if (GetLastError() == ERROR_FILE_NOT_FOUND) return; - Printf("No such directory: %s; exiting\n", Dir.c_str()); + Printf("No such file or directory: %s; exiting\n", Dir.c_str()); exit(1); } @@ -121,6 +147,58 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, *Epoch = E; } + +void IterateDirRecursive(const std::string &Dir, + void (*DirPreCallback)(const std::string &Dir), + void (*DirPostCallback)(const std::string &Dir), + void (*FileCallback)(const std::string &Dir)) { + // TODO(metzman): Implement ListFilesInDirRecursive via this function. + DirPreCallback(Dir); + + DWORD DirAttrs = GetFileAttributesA(Dir.c_str()); + if (!IsDir(DirAttrs)) return; + + std::string TargetDir(Dir); + assert(!TargetDir.empty()); + if (TargetDir.back() != '\\') TargetDir.push_back('\\'); + TargetDir.push_back('*'); + + WIN32_FIND_DATAA FindInfo; + // Find the directory's first file. + HANDLE FindHandle = FindFirstFileA(TargetDir.c_str(), &FindInfo); + if (FindHandle == INVALID_HANDLE_VALUE) { + DWORD LastError = GetLastError(); + if (LastError != ERROR_FILE_NOT_FOUND) { + // If the directory isn't empty, then something abnormal is going on. + Printf("FindFirstFileA failed for %s (Error code: %lu).\n", Dir.c_str(), + LastError); + } + return; + } + + do { + std::string Path = DirPlusFile(Dir, FindInfo.cFileName); + DWORD PathAttrs = FindInfo.dwFileAttributes; + if (IsDir(PathAttrs)) { + // Is Path the current directory (".") or the parent ("..")? + if (strcmp(FindInfo.cFileName, ".") == 0 || + strcmp(FindInfo.cFileName, "..") == 0) + continue; + IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback); + } else if (PathAttrs != INVALID_FILE_ATTRIBUTES) { + FileCallback(Path); + } + } while (FindNextFileA(FindHandle, &FindInfo)); + + DWORD LastError = GetLastError(); + if (LastError != ERROR_NO_MORE_FILES) + Printf("FindNextFileA failed for %s (Error code: %lu).\n", Dir.c_str(), + LastError); + + FindClose(FindHandle); + DirPostCallback(Dir); +} + char GetSeparator() { return '\\'; } @@ -141,6 +219,10 @@ void RemoveFile(const std::string &Path) { _unlink(Path.c_str()); } +void RenameFile(const std::string &OldPath, const std::string &NewPath) { + rename(OldPath.c_str(), NewPath.c_str()); +} + void DiscardOutput(int Fd) { FILE* Temp = fopen("nul", "w"); if (!Temp) @@ -182,7 +264,7 @@ static size_t ParseFileName(const std::string &FileName, const size_t Offset) { return Pos - Offset; } -// Parse a directory ending in separator, like: SomeDir\ +// 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; @@ -197,7 +279,7 @@ static size_t ParseDir(const std::string &FileName, const size_t Offset) { return Pos - Offset; } -// Parse a servername and share, like: SomeServer\SomeShare\ +// 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) { @@ -314,8 +396,24 @@ bool IsInterestingCoverageFile(const std::string &FileName) { } void RawPrint(const char *Str) { - // Not tested, may or may not work. Fix if needed. - Printf("%s", Str); + _write(2, Str, strlen(Str)); +} + +void MkDir(const std::string &Path) { + if (CreateDirectoryA(Path.c_str(), nullptr)) return; + Printf("CreateDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(), + GetLastError()); +} + +void RmDir(const std::string &Path) { + if (RemoveDirectoryA(Path.c_str())) return; + Printf("RemoveDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(), + GetLastError()); +} + +const std::string &getDevNull() { + static const std::string devNull = "NUL"; + return devNull; } } // namespace fuzzer diff --git a/libFuzzer/Fuzzer/FuzzerInterface.h b/libFuzzer/Fuzzer/FuzzerInterface.h index c2c0a39..4f62822 100644 --- a/libFuzzer/Fuzzer/FuzzerInterface.h +++ b/libFuzzer/Fuzzer/FuzzerInterface.h @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Define the interface between libFuzzer and the library being tested. @@ -26,39 +25,52 @@ extern "C" { #endif // __cplusplus +// Define FUZZER_INTERFACE_VISIBILITY to set default visibility in a way that +// doesn't break MSVC. +#if defined(_WIN32) +#define FUZZER_INTERFACE_VISIBILITY __declspec(dllexport) +#else +#define FUZZER_INTERFACE_VISIBILITY __attribute__((visibility("default"))) +#endif + // Mandatory user-provided target function. // Executes the code under test with [Data, Data+Size) as the input. // libFuzzer will invoke this function *many* times with different inputs. // Must return 0. -int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +FUZZER_INTERFACE_VISIBILITY int +LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); // Optional user-provided initialization function. // If provided, this function will be called by libFuzzer once at startup. // It may read and modify argc/argv. // Must return 0. -int LLVMFuzzerInitialize(int *argc, char ***argv); +FUZZER_INTERFACE_VISIBILITY int LLVMFuzzerInitialize(int *argc, char ***argv); // Optional user-provided custom mutator. // Mutates raw data in [Data, Data+Size) inplace. // Returns the new size, which is not greater than MaxSize. // Given the same Seed produces the same mutation. -size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, - unsigned int Seed); +FUZZER_INTERFACE_VISIBILITY size_t +LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, + unsigned int Seed); // Optional user-provided custom cross-over function. // Combines pieces of Data1 & Data2 together into Out. // Returns the new size, which is not greater than MaxOutSize. // Should produce the same mutation given the same Seed. -size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1, - const uint8_t *Data2, size_t Size2, - uint8_t *Out, size_t MaxOutSize, - unsigned int Seed); +FUZZER_INTERFACE_VISIBILITY 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. -size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +FUZZER_INTERFACE_VISIBILITY size_t +LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); + +#undef FUZZER_INTERFACE_VISIBILITY #ifdef __cplusplus } // extern "C" diff --git a/libFuzzer/Fuzzer/FuzzerInternal.h b/libFuzzer/Fuzzer/FuzzerInternal.h index b15f614..31096ce 100644 --- a/libFuzzer/Fuzzer/FuzzerInternal.h +++ b/libFuzzer/Fuzzer/FuzzerInternal.h @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Define the main class fuzzer::Fuzzer and most functions. @@ -12,6 +11,7 @@ #ifndef LLVM_FUZZER_INTERNAL_H #define LLVM_FUZZER_INTERNAL_H +#include "FuzzerDataFlowTrace.h" #include "FuzzerDefs.h" #include "FuzzerExtFunctions.h" #include "FuzzerInterface.h" @@ -35,10 +35,9 @@ class Fuzzer { Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, FuzzingOptions Options); ~Fuzzer(); - void Loop(); + void Loop(Vector &CorporaFiles); + void ReadAndExecuteSeedCorpora(Vector &CorporaFiles); void MinimizeCrashLoop(const Unit &U); - void ShuffleAndMinimize(UnitVector *V); - void InitializeTraceState(); void RereadOutputCorpus(size_t MaxSize); size_t secondsSinceProcessStartUp() { @@ -61,16 +60,17 @@ class Fuzzer { 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); - size_t RunOne(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 std::vector &Corpora); - void CrashResistantMerge(const std::vector &Args, - const std::vector &Corpora); + void Merge(const Vector &Corpora); void CrashResistantMergeInternalStep(const std::string &ControlFilePath); MutationDispatcher &GetMD() { return MD; } void PrintFinalStats(); @@ -84,32 +84,25 @@ class Fuzzer { bool DuringInitialCorpusExecution); void HandleMalloc(size_t Size); - void AnnounceOutput(const uint8_t *Data, size_t Size); + static void MaybeExitGracefully(); + std::string WriteToOutputCorpus(const Unit &U); private: void AlarmCallback(); void CrashCallback(); + void ExitCallback(); + void CrashOnOverwrittenData(); void InterruptCallback(); void MutateAndTestOne(); + void PurgeAllocator(); void ReportNewCoverage(InputInfo *II, const Unit &U); - size_t RunOne(const Unit &U) { return RunOne(U.data(), U.size()); } - void WriteToOutputCorpus(const Unit &U); + void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size); void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix); - void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0); - void PrintStatusForNewUnit(const Unit &U); - void ShuffleCorpus(UnitVector *V); - void AddToCorpus(const Unit &U); + void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0, + size_t Features = 0); + void PrintStatusForNewUnit(const Unit &U, const char *Text); void CheckExitOnSrcPosOrItem(); - // Trace-based fuzzing: we run a unit with some kind of tracing - // enabled and record potentially useful mutations. Then - // We apply these mutations one by one to the unit and run it again. - - // Start tracing; forget all previously proposed mutations. - void StartTraceRecording(); - // Stop tracing. - void StopTraceRecording(); - static void StaticDeathCallback(); void DumpCurrentUnit(const char *Prefix); void DeathCallback(); @@ -118,18 +111,24 @@ class Fuzzer { uint8_t *CurrentUnitData = nullptr; std::atomic CurrentUnitSize; uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit. - bool RunningCB = false; + + 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; @@ -138,11 +137,36 @@ class Fuzzer { size_t MaxInputLen = 0; size_t MaxMutationLen = 0; + size_t TmpMaxMutationLen = 0; + + Vector UniqFeatureSetTmp; // Need to know our own thread. static thread_local bool IsMyThread; }; -}; // namespace fuzzer +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 diff --git a/libFuzzer/Fuzzer/FuzzerLoop.cpp b/libFuzzer/Fuzzer/FuzzerLoop.cpp index 5c9dbc0..451a4c1 100644 --- a/libFuzzer/Fuzzer/FuzzerLoop.cpp +++ b/libFuzzer/Fuzzer/FuzzerLoop.cpp @@ -1,30 +1,26 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Fuzzer's main loop. //===----------------------------------------------------------------------===// #include "FuzzerCorpus.h" -#include "FuzzerInternal.h" #include "FuzzerIO.h" +#include "FuzzerInternal.h" #include "FuzzerMutate.h" #include "FuzzerRandom.h" -#include "FuzzerShmem.h" #include "FuzzerTracePC.h" #include #include #include +#include #include #if defined(__has_include) -#if __has_include() -#include -#endif #if __has_include() #include #endif @@ -43,7 +39,7 @@ static const size_t kMaxUnitSizeToPrint = 256; thread_local bool Fuzzer::IsMyThread; -SharedMemoryRegion SMR; +bool RunningUserCallback = false; // Only one Fuzzer per process. static Fuzzer *F; @@ -72,18 +68,42 @@ struct MallocFreeTracer { std::atomic Mallocs; std::atomic 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 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) - EF->__sanitizer_print_stack_trace(); + PrintStackTrace(); } } @@ -91,25 +111,27 @@ 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) - EF->__sanitizer_print_stack_trace(); + PrintStackTrace(); } } // Crash on a single malloc that exceeds the rss limit. void Fuzzer::HandleMalloc(size_t Size) { - if (!Options.RssLimitMb || (Size >> 20) < (size_t)Options.RssLimitMb) + 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"); - if (EF->__sanitizer_print_stack_trace) - EF->__sanitizer_print_stack_trace(); + PrintStackTrace(); DumpCurrentUnit("oom-"); Printf("SUMMARY: libFuzzer: out-of-memory\n"); PrintFinalStats(); - _Exit(Options.ErrorExitCode); // Stop right now. + _Exit(Options.OOMExitCode); // Stop right now. } Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, @@ -117,7 +139,6 @@ Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, : CB(CB), Corpus(Corpus), MD(MD), Options(Options) { if (EF->__sanitizer_set_death_callback) EF->__sanitizer_set_death_callback(StaticDeathCallback); - InitializeTraceState(); assert(!F); F = this; TPC.ResetMaps(); @@ -125,23 +146,24 @@ Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks) EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook); TPC.SetUseCounters(Options.UseCounters); - TPC.SetUseValueProfile(Options.UseValueProfile); - TPC.SetPrintNewPCs(Options.PrintNewCovPcs); + TPC.SetUseValueProfileMask(Options.UseValueProfile); if (Options.Verbosity) TPC.PrintModuleInfo(); if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec) EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus); MaxInputLen = MaxMutationLen = Options.MaxLen; + TmpMaxMutationLen = 0; // Will be set once we load the corpus. AllocateCurrentUnitData(); CurrentUnitSize = 0; memset(BaseSha1, 0, sizeof(BaseSha1)); } -Fuzzer::~Fuzzer() { } +Fuzzer::~Fuzzer() {} void Fuzzer::AllocateCurrentUnitData() { - if (CurrentUnitData || MaxInputLen == 0) return; + if (CurrentUnitData || MaxInputLen == 0) + return; CurrentUnitData = new uint8_t[MaxInputLen]; } @@ -151,7 +173,9 @@ void Fuzzer::StaticDeathCallback() { } void Fuzzer::DumpCurrentUnit(const char *Prefix) { - if (!CurrentUnitData) return; // Happens when running individual inputs. + 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; @@ -179,43 +203,83 @@ void Fuzzer::StaticCrashSignalCallback() { 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()) + return; Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid()); - if (EF->__sanitizer_print_stack_trace) - EF->__sanitizer_print_stack_trace(); + 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); + _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 (!F->GracefulExitRequested) return; + Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid()); + RmDirRecursive(TempPath(".dir")); + F->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. + ScopedDisableMsanInterceptorChecks S; // RmDirRecursive may call opendir(). + RmDirRecursive(TempPath(".dir")); + // Stop right now, don't perform any at-exit actions. + _Exit(Options.InterruptExitCode); } 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; + // In Windows and Fuchsia, Alarm callback is executed by a different thread. + // NetBSD's current behavior needs this change too. +#if !LIBFUZZER_WINDOWS && !LIBFUZZER_NETBSD && !LIBFUZZER_FUCHSIA + if (!InFuzzingThread()) + return; #endif - if (!RunningCB) + if (!RunningUserCallback) return; // We have not started running units yet. size_t Seconds = duration_cast(system_clock::now() - UnitStartTime).count(); @@ -224,14 +288,16 @@ void Fuzzer::AlarmCallback() { 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); - if (EF->__sanitizer_print_stack_trace) - EF->__sanitizer_print_stack_trace(); + PrintStackTrace(); Printf("SUMMARY: libFuzzer: timeout\n"); PrintFinalStats(); _Exit(Options.TimeoutExitCode); // Stop right now. @@ -239,49 +305,45 @@ void Fuzzer::AlarmCallback() { } 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"); - if (EF->__sanitizer_print_memory_profile) - EF->__sanitizer_print_memory_profile(95, 8); + PrintMemoryProfile(); DumpCurrentUnit("oom-"); Printf("SUMMARY: libFuzzer: out-of-memory\n"); PrintFinalStats(); - _Exit(Options.ErrorExitCode); // Stop right now. + _Exit(Options.OOMExitCode); // Stop right now. } -void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) { +void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units, + size_t Features) { size_t ExecPerSec = execPerSec(); - if (Options.OutputCSV) { - static bool csvHeaderPrinted = false; - if (!csvHeaderPrinted) { - csvHeaderPrinted = true; - Printf("runs,block_cov,bits,cc_cov,corpus,execs_per_sec,tbms,reason\n"); - } - Printf("%zd,%zd,%zd,%zd,%s\n", TotalNumberOfRuns, - TPC.GetTotalPCCoverage(), - Corpus.size(), ExecPerSec, Where); - } - 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 (size_t N = Features ? Features : Corpus.NumFeatures()) + Printf(" ft: %zd", N); if (!Corpus.empty()) { Printf(" corp: %zd", Corpus.NumActiveUnits()); if (size_t N = Corpus.SizeInBytes()) { - if (N < (1<<14)) + 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); @@ -293,11 +355,10 @@ void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) { void Fuzzer::PrintFinalStats() { if (Options.PrintCoverage) TPC.PrintCoverage(); - if (Options.DumpCoverage) - TPC.DumpCoverage(); if (Options.PrintCorpusStats) Corpus.PrintStats(); - if (!Options.PrintFinalStats) return; + 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); @@ -312,7 +373,9 @@ void Fuzzer::SetMaxInputLen(size_t MaxInputLen) { this->MaxInputLen = MaxInputLen; this->MaxMutationLen = MaxInputLen; AllocateCurrentUnitData(); - Printf("INFO: -max_len is not provided, using %zd\n", MaxInputLen); + Printf("INFO: -max_len is not provided; " + "libFuzzer will not generate inputs larger than %zd bytes\n", + MaxInputLen); } void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) { @@ -322,18 +385,18 @@ void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) { void Fuzzer::CheckExitOnSrcPosOrItem() { if (!Options.ExitOnSrcPos.empty()) { - static auto *PCsSet = new std::set; - for (size_t i = 1, N = TPC.GetNumPCs(); i < N; i++) { - uintptr_t PC = TPC.GetPC(i); - if (!PC) continue; - if (!PCsSet->insert(PC).second) continue; - std::string Descr = DescribePC("%L", PC); + static auto *PCsSet = new Set; + auto HandlePC = [&](const TracePC::PCTableEntry *TE) { + if (!PCsSet->insert(TE->PC).second) + return; + std::string Descr = DescribePC("%F %L", TE->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)) { @@ -345,8 +408,9 @@ void Fuzzer::CheckExitOnSrcPosOrItem() { } void Fuzzer::RereadOutputCorpus(size_t MaxSize) { - if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return; - std::vector AdditionalCorpus; + if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) + return; + Vector AdditionalCorpus; ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus, &EpochOfLastReadOfOutputCorpus, MaxSize, /*ExitOnError*/ false); @@ -357,9 +421,8 @@ void Fuzzer::RereadOutputCorpus(size_t MaxSize) { if (U.size() > MaxSize) U.resize(MaxSize); if (!Corpus.HasUnit(U)) { - if (size_t NumFeatures = RunOne(U)) { + if (RunOne(U.data(), U.size())) { CheckExitOnSrcPosOrItem(); - Corpus.AddToCorpus(U, NumFeatures); Reloaded = true; } } @@ -368,51 +431,7 @@ void Fuzzer::RereadOutputCorpus(size_t MaxSize) { PrintStats("RELOAD"); } -void Fuzzer::ShuffleCorpus(UnitVector *V) { - std::shuffle(V->begin(), V->end(), MD.GetRand()); - if (Options.PreferSmall) - std::stable_sort(V->begin(), V->end(), [](const Unit &A, const Unit &B) { - return A.size() < B.size(); - }); -} - -void Fuzzer::ShuffleAndMinimize(UnitVector *InitialCorpus) { - Printf("#0\tREAD units: %zd\n", InitialCorpus->size()); - if (Options.ShuffleAtStartUp) - ShuffleCorpus(InitialCorpus); - - // Test the callback with empty input and never try it again. - uint8_t dummy; - ExecuteCallback(&dummy, 0); - - for (const auto &U : *InitialCorpus) { - if (size_t NumFeatures = RunOne(U)) { - CheckExitOnSrcPosOrItem(); - Corpus.AddToCorpus(U, NumFeatures); - } - TryDetectingAMemoryLeak(U.data(), U.size(), - /*DuringInitialCorpusExecution*/ true); - } - PrintStats("INITED"); - if (Corpus.empty()) { - Printf("ERROR: no interesting inputs were found. " - "Is the code instrumented for coverage? Exiting.\n"); - exit(1); - } -} - -size_t Fuzzer::RunOne(const uint8_t *Data, size_t Size) { - if (!Size) return 0; - TotalNumberOfRuns++; - - ExecuteCallback(Data, Size); - - size_t Res = 0; - if (size_t NumFeatures = TPC.CollectFeatures([&](size_t Feature) -> bool { - return Corpus.AddFeature(Feature, Size, Options.Shrink); - })) - Res = NumFeatures; - +void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) { auto TimeOfUnit = duration_cast(UnitStopTime - UnitStartTime).count(); if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && @@ -424,7 +443,67 @@ size_t Fuzzer::RunOne(const uint8_t *Data, size_t Size) { Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds); WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-"); } - return Res; +} + +static void WriteFeatureSetToFile(const std::string &FeaturesDir, + const std::string &FileName, + const Vector &FeatureSet) { + if (FeaturesDir.empty() || FeatureSet.empty()) return; + WriteToFile(reinterpret_cast(FeatureSet.data()), + FeatureSet.size() * sizeof(FeatureSet[0]), + DirPlusFile(FeaturesDir, FileName)); +} + +static void RenameFeatureSetFile(const std::string &FeaturesDir, + const std::string &OldFile, + const std::string &NewFile) { + if (FeaturesDir.empty()) return; + RenameFile(DirPlusFile(FeaturesDir, OldFile), + DirPlusFile(FeaturesDir, NewFile)); +} + +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(); + 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(); + auto NewII = Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, + MayDeleteFile, TPC.ObservedFocusFunction(), + UniqFeatureSetTmp, DFT, II); + WriteFeatureSetToFile(Options.FeaturesDir, Sha1ToString(NewII->Sha1), + NewII->UniqFeatureSet); + return true; + } + if (II && FoundUniqFeaturesOfII && + II->DataFlowTraceForFocusFunction.empty() && + FoundUniqFeaturesOfII == II->UniqFeatureSet.size() && + II->U.size() > Size) { + auto OldFeaturesFile = Sha1ToString(II->Sha1); + Corpus.Replace(II, {Data, Data + Size}); + RenameFeatureSetFile(Options.FeaturesDir, OldFeaturesFile, + Sha1ToString(II->Sha1)); + return true; + } + return false; } size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { @@ -433,40 +512,70 @@ size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { return CurrentUnitSize; } +void Fuzzer::CrashOnOverwrittenData() { + Printf("==%d== ERROR: libFuzzer: fuzz target overwrites its const input\n", + GetPid()); + PrintStackTrace(); + Printf("SUMMARY: libFuzzer: overwrites-const-input\n"); + DumpCurrentUnit("crash-"); + PrintFinalStats(); + _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 (EF->__msan_unpoison_param) + EF->__msan_unpoison_param(2); if (CurrentUnitData && CurrentUnitData != Data) memcpy(CurrentUnitData, Data, Size); CurrentUnitSize = Size; - AllocTracer.Start(Options.TraceMalloc); - UnitStartTime = system_clock::now(); - TPC.ResetMaps(); - RunningCB = true; - int Res = CB(DataCopy, Size); - RunningCB = false; - UnitStopTime = system_clock::now(); - (void)Res; - assert(Res == 0); - HasMoreMallocsThanFrees = AllocTracer.Stop(); + { + 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) { +std::string Fuzzer::WriteToOutputCorpus(const Unit &U) { if (Options.OnlyASCII) assert(IsASCII(U)); if (Options.OutputCorpus.empty()) - return; + return ""; std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U)); WriteToFile(U, Path); if (Options.Verbosity >= 2) - Printf("Written to %s\n", Path.c_str()); + Printf("Written %zd bytes to %s\n", U.size(), Path.c_str()); + return Path; } void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) { @@ -482,12 +591,12 @@ void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) { Printf("Base64: %s\n", Base64(U).c_str()); } -void Fuzzer::PrintStatusForNewUnit(const Unit &U) { +void Fuzzer::PrintStatusForNewUnit(const Unit &U, const char *Text) { if (!Options.PrintNEW) return; - PrintStats("NEW ", ""); + PrintStats(Text, ""); if (Options.Verbosity) { - Printf(" L: %zd ", U.size()); + Printf(" L: %zd/%zd ", U.size(), Corpus.MaxInputSize()); MD.PrintMutationSequence(); Printf("\n"); } @@ -496,27 +605,34 @@ void Fuzzer::PrintStatusForNewUnit(const Unit &U) { void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) { II->NumSuccessfullMutations++; MD.RecordSuccessfulMutationSequence(); - PrintStatusForNewUnit(U); + PrintStatusForNewUnit(U, II->Reduced ? "REDUCE" : "NEW "); WriteToOutputCorpus(U); NumberOfNewUnitsAdded++; - TPC.PrintNewPCs(); + 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 (!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. + 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 (!HasMoreMallocsThanFrees) + return; // a leak is unlikely. if (NumberOfLeakDetectionAttempts++ > 1000) { Options.DetectLeaks = false; Printf("INFO: libFuzzer disabled leak detection after every mutation.\n" @@ -537,27 +653,16 @@ void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, CurrentUnitSize = Size; DumpCurrentUnit("leak-"); PrintFinalStats(); - _Exit(Options.ErrorExitCode); // not exit() to disable lsan further on. + _Exit(Options.ErrorExitCode); // not exit() to disable lsan further on. } } -static size_t ComputeMutationLen(size_t MaxInputSize, size_t MaxMutationLen, - Random &Rand) { - assert(MaxInputSize <= MaxMutationLen); - if (MaxInputSize == MaxMutationLen) return MaxMutationLen; - size_t Result = MaxInputSize; - size_t R = Rand.Rand(); - if ((R % (1U << 7)) == 0) - Result++; - if ((R % (1U << 15)) == 0) - Result += 10 + Result / 2; - return Min(Result, MaxMutationLen); -} - void Fuzzer::MutateAndTestOne() { MD.StartMutationSequence(); auto &II = Corpus.ChooseUnitToMutate(MD.GetRand()); + if (Options.DoCrossOver) + MD.SetCrossOverWith(&Corpus.ChooseUnitToMutate(MD.GetRand()).U); const auto &U = II.U; memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1)); assert(CurrentUnitData); @@ -568,41 +673,136 @@ void Fuzzer::MutateAndTestOne() { assert(MaxMutationLen > 0); size_t CurrentMaxMutationLen = - Options.ExperimentalLenControl - ? ComputeMutationLen(Corpus.MaxInputSize(), MaxMutationLen, - MD.GetRand()) - : MaxMutationLen; + 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; - NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen); + if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() && + Size <= CurrentMaxMutationLen) + NewSize = MD.MutateWithMask(CurrentUnitData, Size, Size, + II.DataFlowTraceForFocusFunction); + + // If MutateWithMask either failed or wasn't called, call default Mutate. + if (!NewSize) + NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen); assert(NewSize > 0 && "Mutator returned empty unit"); - assert(NewSize <= CurrentMaxMutationLen && "Mutator return overisized unit"); + assert(NewSize <= CurrentMaxMutationLen && "Mutator return oversized unit"); Size = NewSize; - if (i == 0) - StartTraceRecording(); II.NumExecutedMutations++; - if (size_t NumFeatures = RunOne(CurrentUnitData, Size)) { - Corpus.AddToCorpus({CurrentUnitData, CurrentUnitData + Size}, NumFeatures, - /*MayDeleteFile=*/true); + + 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(system_clock::now() - + LastAllocatorPurgeAttemptTime) + .count() < Options.PurgeAllocatorIntervalSec) + return; + + if (Options.RssLimitMb <= 0 || + GetPeakRSSMb() > static_cast(Options.RssLimitMb) / 2) + EF->__sanitizer_purge_allocator(); + + LastAllocatorPurgeAttemptTime = system_clock::now(); +} + +void Fuzzer::ReadAndExecuteSeedCorpora(Vector &CorporaFiles) { + const size_t kMaxSaneLen = 1 << 20; + const size_t kMinDefaultLen = 4096; + size_t MaxSize = 0; + size_t MinSize = -1; + size_t TotalSize = 0; + for (auto &File : CorporaFiles) { + 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 (CorporaFiles.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", + CorporaFiles.size(), MinSize, MaxSize, TotalSize, GetPeakRSSMb()); + if (Options.ShuffleAtStartUp) + std::shuffle(CorporaFiles.begin(), CorporaFiles.end(), MD.GetRand()); + + if (Options.PreferSmall) { + std::stable_sort(CorporaFiles.begin(), CorporaFiles.end()); + assert(CorporaFiles.front().Size <= CorporaFiles.back().Size); + } + + // Load and execute inputs one by one. + for (auto &SF : CorporaFiles) { + 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); } - StopTraceRecording(); - TryDetectingAMemoryLeak(CurrentUnitData, Size, - /*DuringInitialCorpusExecution*/ false); + } + + 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() { - TPC.InitializePrintNewPCs(); +void Fuzzer::Loop(Vector &CorporaFiles) { + auto FocusFunctionOrAuto = Options.FocusFunction; + DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles, + MD.GetRand()); + TPC.SetFocusFunction(FocusFunctionOrAuto); + ReadAndExecuteSeedCorpora(CorporaFiles); + 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); + + TmpMaxMutationLen = + Min(MaxMutationLen, Max(size_t(4), Corpus.MaxInputSize())); + while (true) { auto Now = system_clock::now(); + if (!Options.StopFile.empty() && + !FileToVector(Options.StopFile, 1, false).empty()) + break; if (duration_cast(Now - LastCorpusReload).count() >= Options.ReloadIntervalSec) { RereadOutputCorpus(MaxInputLen); @@ -610,9 +810,26 @@ void Fuzzer::Loop() { } if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) break; - if (TimedOut()) 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"); @@ -620,55 +837,30 @@ void Fuzzer::Loop() { } void Fuzzer::MinimizeCrashLoop(const Unit &U) { - if (U.size() <= 1) return; + 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); - RunOne(CurrentUnitData, NewSize); + 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" { -size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { +ATTRIBUTE_INTERFACE size_t +LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { assert(fuzzer::F); return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize); } -// Experimental -void LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size) { - assert(fuzzer::F); - fuzzer::F->AnnounceOutput(Data, Size); -} -} // extern "C" +} // extern "C" diff --git a/libFuzzer/Fuzzer/FuzzerMain.cpp b/libFuzzer/Fuzzer/FuzzerMain.cpp index af86572..771a34a 100644 --- a/libFuzzer/Fuzzer/FuzzerMain.cpp +++ b/libFuzzer/Fuzzer/FuzzerMain.cpp @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // main() and flags. @@ -16,6 +15,6 @@ extern "C" { int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); } // extern "C" -int main(int argc, char **argv) { +ATTRIBUTE_INTERFACE int main(int argc, char **argv) { return fuzzer::FuzzerDriver(&argc, &argv, LLVMFuzzerTestOneInput); } diff --git a/libFuzzer/Fuzzer/FuzzerMerge.cpp b/libFuzzer/Fuzzer/FuzzerMerge.cpp index 58a228b..e3ad8b3 100644 --- a/libFuzzer/Fuzzer/FuzzerMerge.cpp +++ b/libFuzzer/Fuzzer/FuzzerMerge.cpp @@ -1,17 +1,17 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Merging corpora. //===----------------------------------------------------------------------===// -#include "FuzzerInternal.h" -#include "FuzzerIO.h" +#include "FuzzerCommand.h" #include "FuzzerMerge.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" #include "FuzzerTracePC.h" #include "FuzzerUtil.h" @@ -19,6 +19,7 @@ #include #include #include +#include namespace fuzzer { @@ -42,10 +43,12 @@ void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) { // 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. +// FT 0 1 4 6 8 # FileID COV1 COV2 ... +// COV 0 7 8 9 # FileID COV1 COV1 +// STARTED 1 456 # If FT is missing, the input crashed while processing. // STARTED 2 567 -// DONE 2 8 9 +// FT 2 8 9 +// COV 2 11 12 bool Merger::Parse(std::istream &IS, bool ParseCoverage) { LastFailure.clear(); std::string Line; @@ -70,11 +73,12 @@ bool Merger::Parse(std::istream &IS, bool ParseCoverage) { if (!std::getline(IS, Files[i].Name, '\n')) return false; - // Parse STARTED and DONE lines. + // Parse STARTED, FT, and COV lines. size_t ExpectedStartMarker = 0; const size_t kInvalidStartMarker = -1; size_t LastSeenStartMarker = kInvalidStartMarker; - std::vector TmpFeatures; + Vector TmpFeatures; + Set PCs; while (std::getline(IS, Line, '\n')) { std::istringstream ISS1(Line); std::string Marker; @@ -89,19 +93,25 @@ bool Merger::Parse(std::istream &IS, bool ParseCoverage) { LastSeenStartMarker = ExpectedStartMarker; assert(ExpectedStartMarker < Files.size()); ExpectedStartMarker++; - } else if (Marker == "DONE") { - // DONE FILE_ID COV1 COV2 COV3 ... + } else if (Marker == "FT") { + // FT 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) + while (ISS1 >> N) TmpFeatures.push_back(N); std::sort(TmpFeatures.begin(), TmpFeatures.end()); Files[CurrentFileIdx].Features = TmpFeatures; } + } else if (Marker == "COV") { + size_t CurrentFileIdx = N; + if (ParseCoverage) + while (ISS1 >> N) + if (PCs.insert(N).second) + Files[CurrentFileIdx].Cov.push_back(N); } else { return false; } @@ -120,24 +130,25 @@ size_t Merger::ApproximateMemoryConsumption() const { return Res; } -// Decides which files need to be merged (add thost to NewFiles). +// Decides which files need to be merged (add those to NewFiles). // Returns the number of new features added. -size_t Merger::Merge(std::vector *NewFiles) { +size_t Merger::Merge(const Set &InitialFeatures, + Set *NewFeatures, + const Set &InitialCov, Set *NewCov, + Vector *NewFiles) { NewFiles->clear(); assert(NumFilesInFirstCorpus <= Files.size()); - std::set AllFeatures; + Set 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; - std::vector Tmp; + Vector Tmp; std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(), AllFeatures.end(), std::inserter(Tmp, Tmp.begin())); Cur.swap(Tmp); @@ -159,12 +170,27 @@ size_t Merger::Merge(std::vector *NewFiles) { 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) + bool FoundNewFeatures = false; + for (auto Fe: Cur) { + if (AllFeatures.insert(Fe).second) { + FoundNewFeatures = true; + NewFeatures->insert(Fe); + } + } + if (FoundNewFeatures) NewFiles->push_back(Files[i].Name); + for (auto Cov : Files[i].Cov) + if (InitialCov.find(Cov) == InitialCov.end()) + NewCov->insert(Cov); } - return AllFeatures.size() - InitialNumFeatures; + return NewFeatures->size(); +} + +Set Merger::AllFeatures() const { + Set S; + for (auto &File : Files) + S.insert(File.Features.begin(), File.Features.end()); + return S; } // Inner process. May crash if the target crashes. @@ -184,103 +210,193 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { M.Files.size() - M.FirstNotProcessedFile); std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app); + Set AllFeatures; + auto PrintStatsWrapper = [this, &AllFeatures](const char* Where) { + this->PrintStats(Where, "\n", 0, AllFeatures.size()); + }; + Set AllPCs; for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) { + Fuzzer::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 ExecuteCommand may crash. + OF << "STARTED " << i << " " << U.size() << "\n"; + OF.flush(); // Flush is important since Command::Execute may crash. // Run. TPC.ResetMaps(); ExecuteCallback(U.data(), U.size()); - // Collect coverage. - std::set Features; - TPC.CollectFeatures([&](size_t Feature) -> bool { - Features.insert(Feature); - return true; + // 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 UniqFeatures; + TPC.CollectFeatures([&](size_t Feature) { + if (AllFeatures.insert(Feature).second) + UniqFeatures.insert(Feature); }); + TPC.UpdateObservedPCs(); // Show stats. - TotalNumberOfRuns++; if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1))) - PrintStats("pulse "); + PrintStatsWrapper("pulse "); + if (TotalNumberOfRuns == M.NumFilesInFirstCorpus) + PrintStatsWrapper("LOADED"); // Write the post-run marker and the coverage. - OF << "DONE " << i; - for (size_t F : Features) - OF << " " << std::hex << F; + OF << "FT " << i; + for (size_t F : UniqFeatures) + OF << " " << F; OF << "\n"; + OF << "COV " << i; + TPC.ForEachObservedPC([&](const TracePC::PCTableEntry *TE) { + if (AllPCs.insert(TE).second) + OF << " " << TPC.PCTableEntryIdx(TE); + }); + OF << "\n"; + OF.flush(); } + PrintStatsWrapper("DONE "); } -// Outer process. Does not call the target code and thus sohuld not fail. -void Fuzzer::CrashResistantMerge(const std::vector &Args, - const std::vector &Corpora) { - if (Corpora.size() <= 1) { - Printf("Merge requires two or more corpus dirs\n"); - return; - } - std::vector AllFiles; - ListFilesInDirRecursive(Corpora[0], nullptr, &AllFiles, /*TopDir*/true); - size_t NumFilesInFirstCorpus = AllFiles.size(); - for (size_t i = 1; i < Corpora.size(); i++) - ListFilesInDirRecursive(Corpora[i], nullptr, &AllFiles, /*TopDir*/true); - Printf("MERGE-OUTER: %zd files, %zd in the initial corpus\n", - AllFiles.size(), NumFilesInFirstCorpus); - auto CFPath = DirPlusFile(TmpDir(), - "libFuzzerTemp." + std::to_string(GetPid()) + ".txt"); - // Write the control file. +static size_t WriteNewControlFile(const std::string &CFPath, + const Vector &OldCorpus, + const Vector &NewCorpus, + const Vector &KnownFiles) { + std::unordered_set FilesToSkip; + for (auto &SF: KnownFiles) + FilesToSkip.insert(SF.Name); + + Vector FilesToUse; + auto MaybeUseFile = [=, &FilesToUse](std::string Name) { + if (FilesToSkip.find(Name) == FilesToSkip.end()) + FilesToUse.push_back(Name); + }; + for (auto &SF: OldCorpus) + MaybeUseFile(SF.File); + auto FilesToUseFromOldCorpus = FilesToUse.size(); + for (auto &SF: NewCorpus) + MaybeUseFile(SF.File); + RemoveFile(CFPath); std::ofstream ControlFile(CFPath); - ControlFile << AllFiles.size() << "\n"; - ControlFile << NumFilesInFirstCorpus << "\n"; - for (auto &Path: AllFiles) - ControlFile << Path << "\n"; + ControlFile << FilesToUse.size() << "\n"; + ControlFile << FilesToUseFromOldCorpus << "\n"; + for (auto &FN: FilesToUse) + ControlFile << FN << "\n"; + if (!ControlFile) { Printf("MERGE-OUTER: failed to write to the control file: %s\n", CFPath.c_str()); exit(1); } - ControlFile.close(); - // Execute the inner process untill it passes. + return FilesToUse.size(); +} + +// Outer process. Does not call the target code and thus should not fail. +void CrashResistantMerge(const Vector &Args, + const Vector &OldCorpus, + const Vector &NewCorpus, + Vector *NewFiles, + const Set &InitialFeatures, + Set *NewFeatures, + const Set &InitialCov, + Set *NewCov, + const std::string &CFPath, + bool V /*Verbose*/) { + if (NewCorpus.empty() && OldCorpus.empty()) return; // Nothing to merge. + size_t NumAttempts = 0; + Vector KnownFiles; + if (FileSize(CFPath)) { + VPrintf(V, "MERGE-OUTER: non-empty control file provided: '%s'\n", + CFPath.c_str()); + Merger M; + std::ifstream IF(CFPath); + if (M.Parse(IF, /*ParseCoverage=*/true)) { + VPrintf(V, "MERGE-OUTER: control file ok, %zd files total," + " first not processed file %zd\n", + M.Files.size(), M.FirstNotProcessedFile); + if (!M.LastFailure.empty()) + VPrintf(V, "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()) { + // Merge has already been completed with the given merge control file. + if (M.Files.size() == OldCorpus.size() + NewCorpus.size()) { + VPrintf( + V, + "MERGE-OUTER: nothing to do, merge has been completed before\n"); + exit(0); + } + + // Number of input files likely changed, start merge from scratch, but + // reuse coverage information from the given merge control file. + VPrintf( + V, + "MERGE-OUTER: starting merge from scratch, but reusing coverage " + "information from the given control file\n"); + KnownFiles = M.Files; + } else { + // There is a merge in progress, continue. + NumAttempts = M.Files.size() - M.FirstNotProcessedFile; + } + } else { + VPrintf(V, "MERGE-OUTER: bad control file, will overwrite it\n"); + } + } + + if (!NumAttempts) { + // The supplied control file is empty or bad, create a fresh one. + VPrintf(V, "MERGE-OUTER: " + "%zd files, %zd in the initial corpus, %zd processed earlier\n", + OldCorpus.size() + NewCorpus.size(), OldCorpus.size(), + KnownFiles.size()); + NumAttempts = WriteNewControlFile(CFPath, OldCorpus, NewCorpus, KnownFiles); + } + + // Execute the inner process until it passes. // Every inner process should execute at least one input. - std::string BaseCmd = CloneArgsWithoutX(Args, "keep-all-flags"); - bool Success = false; - for (size_t i = 1; i <= AllFiles.size(); i++) { - Printf("MERGE-OUTER: attempt %zd\n", i); - auto ExitCode = - ExecuteCommand(BaseCmd + " -merge_control_file=" + CFPath); + Command BaseCmd(Args); + BaseCmd.removeFlag("merge"); + BaseCmd.removeFlag("fork"); + BaseCmd.removeFlag("collect_data_flow"); + for (size_t Attempt = 1; Attempt <= NumAttempts; Attempt++) { + Fuzzer::MaybeExitGracefully(); + VPrintf(V, "MERGE-OUTER: attempt %zd\n", Attempt); + Command Cmd(BaseCmd); + Cmd.addFlag("merge_control_file", CFPath); + Cmd.addFlag("merge_inner", "1"); + if (!V) { + Cmd.setOutputFile(getDevNull()); + Cmd.combineOutAndErr(); + } + auto ExitCode = ExecuteCommand(Cmd); if (!ExitCode) { - Printf("MERGE-OUTER: succesfull in %zd attempt(s)\n", i); - Success = true; + VPrintf(V, "MERGE-OUTER: succesfull in %zd attempt(s)\n", Attempt); 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()); + VPrintf(V, "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()); - std::vector NewFiles; - size_t NumNewFeatures = M.Merge(&NewFiles); - Printf("MERGE-OUTER: %zd new files with %zd new features added\n", - NewFiles.size(), NumNewFeatures); - for (auto &F: NewFiles) - WriteToOutputCorpus(FileToVector(F)); - // We are done, delete the control file. - RemoveFile(CFPath); + VPrintf(V, + "MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n", + M.ApproximateMemoryConsumption() >> 20, GetPeakRSSMb()); + + M.Files.insert(M.Files.end(), KnownFiles.begin(), KnownFiles.end()); + M.Merge(InitialFeatures, NewFeatures, InitialCov, NewCov, NewFiles); + VPrintf(V, "MERGE-OUTER: %zd new files with %zd new features added; " + "%zd new coverage edges\n", + NewFiles->size(), NewFeatures->size(), NewCov->size()); } } // namespace fuzzer diff --git a/libFuzzer/Fuzzer/FuzzerMerge.h b/libFuzzer/Fuzzer/FuzzerMerge.h index 4cef9c4..c14dd58 100644 --- a/libFuzzer/Fuzzer/FuzzerMerge.h +++ b/libFuzzer/Fuzzer/FuzzerMerge.h @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Merging Corpora. @@ -43,17 +42,20 @@ #include "FuzzerDefs.h" #include +#include +#include +#include namespace fuzzer { struct MergeFileInfo { std::string Name; size_t Size = 0; - std::vector Features; + Vector Features, Cov; }; struct Merger { - std::vector Files; + Vector Files; size_t NumFilesInFirstCorpus = 0; size_t FirstNotProcessedFile = 0; std::string LastFailure; @@ -61,10 +63,24 @@ struct Merger { bool Parse(std::istream &IS, bool ParseCoverage); bool Parse(const std::string &Str, bool ParseCoverage); void ParseOrExit(std::istream &IS, bool ParseCoverage); - size_t Merge(std::vector *NewFiles); + size_t Merge(const Set &InitialFeatures, Set *NewFeatures, + const Set &InitialCov, Set *NewCov, + Vector *NewFiles); size_t ApproximateMemoryConsumption() const; + Set AllFeatures() const; }; +void CrashResistantMerge(const Vector &Args, + const Vector &OldCorpus, + const Vector &NewCorpus, + Vector *NewFiles, + const Set &InitialFeatures, + Set *NewFeatures, + const Set &InitialCov, + Set *NewCov, + const std::string &CFPath, + bool Verbose); + } // namespace fuzzer #endif // LLVM_FUZZER_MERGE_H diff --git a/libFuzzer/Fuzzer/FuzzerMutate.cpp b/libFuzzer/Fuzzer/FuzzerMutate.cpp index c9768e4..29541ea 100644 --- a/libFuzzer/Fuzzer/FuzzerMutate.cpp +++ b/libFuzzer/Fuzzer/FuzzerMutate.cpp @@ -1,20 +1,19 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Mutate a test input. //===----------------------------------------------------------------------===// -#include "FuzzerCorpus.h" #include "FuzzerDefs.h" #include "FuzzerExtFunctions.h" #include "FuzzerIO.h" #include "FuzzerMutate.h" #include "FuzzerOptions.h" +#include "FuzzerTracePC.h" namespace fuzzer { @@ -43,8 +42,6 @@ MutationDispatcher::MutationDispatcher(Random &Rand, {&MutationDispatcher::Mutate_CrossOver, "CrossOver"}, {&MutationDispatcher::Mutate_AddWordFromManualDictionary, "ManualDict"}, - {&MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary, - "TempAutoDict"}, {&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary, "PersAutoDict"}, }); @@ -64,7 +61,7 @@ MutationDispatcher::MutationDispatcher(Random &Rand, static char RandCh(Random &Rand) { if (Rand.RandBool()) return Rand(256); - const char *Special = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00"; + const char Special[] = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00"; return Special[Rand(sizeof(Special) - 1)]; } @@ -75,10 +72,10 @@ size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size, size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize) { - if (!Corpus || Corpus->size() < 2 || Size == 0) + if (Size == 0) return 0; - size_t Idx = Rand(Corpus->size()); - const Unit &Other = (*Corpus)[Idx]; + if (!CrossOverWith) return 0; + const Unit &Other = *CrossOverWith; if (Other.empty()) return 0; CustomCrossOverInPlaceHere.resize(MaxSize); @@ -165,11 +162,6 @@ size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data, return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize); } -size_t MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary( - uint8_t *Data, size_t Size, size_t MaxSize) { - return AddWordFromDictionary(TempAutoDictionary, Data, Size, MaxSize); -} - size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize, DictionaryEntry &DE) { @@ -202,7 +194,6 @@ DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( const void *Arg1Mutation, const void *Arg2Mutation, size_t ArgSize, const uint8_t *Data, size_t Size) { - ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem; bool HandleFirst = Rand.RandBool(); const void *ExistingBytes, *DesiredBytes; Word W; @@ -217,11 +208,12 @@ DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( size_t NumPositions = 0; for (const uint8_t *Cur = Data; Cur < End && NumPositions < kMaxNumPositions; Cur++) { - Cur = (uint8_t *)SearchMemory(Cur, End - Cur, ExistingBytes, ArgSize); + Cur = + (const uint8_t *)SearchMemory(Cur, End - Cur, ExistingBytes, ArgSize); if (!Cur) break; Positions[NumPositions++] = Cur - Data; } - if (!NumPositions) break; + if (!NumPositions) continue; return DictionaryEntry(W, Positions[Rand(NumPositions)]); } DictionaryEntry DE(W); @@ -250,7 +242,7 @@ size_t MutationDispatcher::Mutate_AddWordFromTORC( uint8_t *Data, size_t Size, size_t MaxSize) { Word W; DictionaryEntry DE; - switch (Rand(3)) { + switch (Rand(4)) { case 0: { auto X = TPC.TORC8.Get(Rand.Rand()); DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); @@ -266,6 +258,10 @@ size_t MutationDispatcher::Mutate_AddWordFromTORC( 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); } @@ -341,7 +337,9 @@ size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize, size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize) { if (Size > MaxSize || Size == 0) return 0; - if (Rand.RandBool()) + // 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); @@ -423,9 +421,9 @@ size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data, 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 (Size == 0) return 0; + if (!CrossOverWith) return 0; + const Unit &O = *CrossOverWith; if (O.empty()) return 0; MutateInPlaceHere.resize(MaxSize); auto &U = MutateInPlaceHere; @@ -468,7 +466,7 @@ void MutationDispatcher::RecordSuccessfulMutationSequence() { } void MutationDispatcher::PrintRecommendedDictionary() { - std::vector V; + Vector V; for (auto &DE : PersistentAutoDictionary) if (!ManualDictionary.ContainsWord(DE.GetW())) V.push_back(DE); @@ -508,7 +506,7 @@ size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size, // Mutates Data in place, returns new size. size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize, - const std::vector &Mutators) { + Vector &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. @@ -527,19 +525,38 @@ size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size, 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 &Mask) { + size_t MaskedSize = std::min(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 < MaskedSize; I++) + if (Mask[I]) + T[OneBits++] = Data[I]; + + if (!OneBits) return 0; + 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 < MaskedSize; I++) + if (Mask[I]) + Data[I] = T[J++]; + return Size; +} + void MutationDispatcher::AddWordToManualDictionary(const Word &W) { ManualDictionary.push_back( {W, std::numeric_limits::max()}); } -void MutationDispatcher::AddWordToAutoDictionary(DictionaryEntry DE) { - static const size_t kMaxAutoDictSize = 1 << 14; - if (TempAutoDictionary.size() >= kMaxAutoDictSize) return; - TempAutoDictionary.push_back(DE); -} - -void MutationDispatcher::ClearAutoDictionary() { - TempAutoDictionary.clear(); -} - } // namespace fuzzer diff --git a/libFuzzer/Fuzzer/FuzzerMutate.h b/libFuzzer/Fuzzer/FuzzerMutate.h index 8c8fb3f..6cbce80 100644 --- a/libFuzzer/Fuzzer/FuzzerMutate.h +++ b/libFuzzer/Fuzzer/FuzzerMutate.h @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // fuzzer::MutationDispatcher @@ -27,7 +26,7 @@ class MutationDispatcher { void StartMutationSequence(); /// Print the current sequence of mutations. void PrintMutationSequence(); - /// Indicate that the current sequence of mutations was successfull. + /// 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); @@ -52,10 +51,6 @@ class MutationDispatcher { size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size, size_t MaxSize); - /// Mutates data by adding a word from the temporary automatic dictionary. - size_t Mutate_AddWordFromTemporaryAutoDictionary(uint8_t *Data, size_t Size, - size_t MaxSize); - /// Mutates data by adding a word from the TORC. size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize); @@ -68,12 +63,19 @@ class MutationDispatcher { /// 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. + /// CrossOver Data with CrossOverWith. 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 &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); @@ -84,16 +86,13 @@ class MutationDispatcher { void AddWordToManualDictionary(const Word &W); - void AddWordToAutoDictionary(DictionaryEntry DE); - void ClearAutoDictionary(); void PrintRecommendedDictionary(); - void SetCorpus(const InputCorpus *Corpus) { this->Corpus = Corpus; } + void SetCrossOverWith(const Unit *U) { CrossOverWith = U; } Random &GetRand() { return Rand; } -private: - + private: struct Mutator { size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max); const char *Name; @@ -102,7 +101,7 @@ class MutationDispatcher { size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size, size_t MaxSize); size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize, - const std::vector &Mutators); + Vector &Mutators); size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To, size_t ToSize, size_t MaxToSize); @@ -131,24 +130,25 @@ class MutationDispatcher { // recreated periodically. Dictionary TempAutoDictionary; // Persistent dictionary modified by the fuzzer, consists of - // entries that led to successfull discoveries in the past mutations. + // entries that led to successful discoveries in the past mutations. Dictionary PersistentAutoDictionary; - std::vector CurrentMutatorSequence; - std::vector CurrentDictionaryEntrySequence; + Vector CurrentDictionaryEntrySequence; static const size_t kCmpDictionaryEntriesDequeSize = 16; DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize]; size_t CmpDictionaryEntriesDequeIdx = 0; - const InputCorpus *Corpus = nullptr; - std::vector MutateInPlaceHere; + const Unit *CrossOverWith = nullptr; + Vector MutateInPlaceHere; + Vector MutateWithMaskTemp; // CustomCrossOver needs its own buffer as a custom implementation may call // LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere. - std::vector CustomCrossOverInPlaceHere; + Vector CustomCrossOverInPlaceHere; - std::vector Mutators; - std::vector DefaultMutators; + Vector Mutators; + Vector DefaultMutators; + Vector CurrentMutatorSequence; }; } // namespace fuzzer diff --git a/libFuzzer/Fuzzer/FuzzerOptions.h b/libFuzzer/Fuzzer/FuzzerOptions.h index 872def0..beecc98 100644 --- a/libFuzzer/Fuzzer/FuzzerOptions.h +++ b/libFuzzer/Fuzzer/FuzzerOptions.h @@ -1,8 +1,7 @@ // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // fuzzer::FuzzingOptions @@ -18,20 +17,27 @@ namespace fuzzer { struct FuzzingOptions { int Verbosity = 1; size_t MaxLen = 0; - bool ExperimentalLenControl = false; + size_t LenControl = 1000; int UnitTimeoutSec = 300; - int TimeoutExitCode = 77; + int TimeoutExitCode = 70; + int OOMExitCode = 71; + int InterruptExitCode = 72; int ErrorExitCode = 77; + bool IgnoreTimeouts = true; + bool IgnoreOOMs = true; + bool IgnoreCrashes = false; int MaxTotalTimeSec = 0; int RssLimitMb = 0; + int MallocLimitMb = 0; bool DoCrossOver = true; int MutateDepth = 5; + bool ReduceDepth = false; bool UseCounters = false; - bool UseIndirCalls = true; bool UseMemmem = true; bool UseCmp = false; - bool UseValueProfile = false; + int UseValueProfile = false; bool Shrink = false; + bool ReduceInputs = false; int ReloadIntervalSec = 1; bool ShuffleAtStartUp = true; bool PreferSmall = true; @@ -43,15 +49,21 @@ struct FuzzingOptions { std::string ExactArtifactPath; std::string ExitOnSrcPos; std::string ExitOnItem; + std::string FocusFunction; + std::string DataFlowTrace; + std::string CollectDataFlow; + std::string FeaturesDir; + std::string StopFile; bool SaveArtifacts = true; bool PrintNEW = true; // Print a status line when new units are found; - bool OutputCSV = false; bool PrintNewCovPcs = false; + int PrintNewCovFuncs = 0; bool PrintFinalStats = false; bool PrintCorpusStats = false; bool PrintCoverage = false; bool DumpCoverage = false; bool DetectLeaks = true; + int PurgeAllocatorIntervalSec = 1; int TraceMalloc = 0; bool HandleAbrt = false; bool HandleBus = false; @@ -61,6 +73,8 @@ struct FuzzingOptions { bool HandleSegv = false; bool HandleTerm = false; bool HandleXfsz = false; + bool HandleUsr1 = false; + bool HandleUsr2 = false; }; } // namespace fuzzer diff --git a/libFuzzer/Fuzzer/FuzzerRandom.h b/libFuzzer/Fuzzer/FuzzerRandom.h index 8a1aa3e..659283e 100644 --- a/libFuzzer/Fuzzer/FuzzerRandom.h +++ b/libFuzzer/Fuzzer/FuzzerRandom.h @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // fuzzer::Random @@ -15,12 +14,17 @@ #include namespace fuzzer { -class Random : public std::mt19937 { +class Random : public std::minstd_rand { public: - Random(unsigned int seed) : std::mt19937(seed) {} - result_type operator()() { return this->std::mt19937::operator()(); } + Random(unsigned int seed) : std::minstd_rand(seed) {} + result_type operator()() { return this->std::minstd_rand::operator()(); } size_t Rand() { return this->operator()(); } size_t RandBool() { return Rand() % 2; } + size_t SkewTowardsLast(size_t n) { + size_t T = this->operator()(n * n); + size_t Res = sqrt(T); + return Res; + } size_t operator()(size_t n) { return n ? Rand() % n : 0; } intptr_t operator()(intptr_t From, intptr_t To) { assert(From < To); diff --git a/libFuzzer/Fuzzer/FuzzerSHA1.cpp b/libFuzzer/Fuzzer/FuzzerSHA1.cpp index d2f8e81..43e5e78 100644 --- a/libFuzzer/Fuzzer/FuzzerSHA1.cpp +++ b/libFuzzer/Fuzzer/FuzzerSHA1.cpp @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // This code is taken from public domain @@ -32,7 +31,8 @@ namespace { // Added for LibFuzzer #ifdef __BIG_ENDIAN__ # define SHA_BIG_ENDIAN -#elif defined __LITTLE_ENDIAN__ +// Windows is always little endian and MSVC doesn't have +#elif defined __LITTLE_ENDIAN__ || LIBFUZZER_WINDOWS /* override */ #elif defined __BYTE_ORDER # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ diff --git a/libFuzzer/Fuzzer/FuzzerSHA1.h b/libFuzzer/Fuzzer/FuzzerSHA1.h index 3b5e6e8..05cbacd 100644 --- a/libFuzzer/Fuzzer/FuzzerSHA1.h +++ b/libFuzzer/Fuzzer/FuzzerSHA1.h @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // SHA1 utils. diff --git a/libFuzzer/Fuzzer/FuzzerShmem.h b/libFuzzer/Fuzzer/FuzzerShmem.h deleted file mode 100644 index 53568e0..0000000 --- a/libFuzzer/Fuzzer/FuzzerShmem.h +++ /dev/null @@ -1,69 +0,0 @@ -//===- FuzzerShmem.h - shared memory interface ------------------*- C++ -* ===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// SharedMemoryRegion -//===----------------------------------------------------------------------===// - -#ifndef LLVM_FUZZER_SHMEM_H -#define LLVM_FUZZER_SHMEM_H - -#include -#include -#include - -#include "FuzzerDefs.h" - -namespace fuzzer { - -class SharedMemoryRegion { - public: - bool Create(const char *Name); - 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 diff --git a/libFuzzer/Fuzzer/FuzzerShmemPosix.cpp b/libFuzzer/Fuzzer/FuzzerShmemPosix.cpp deleted file mode 100644 index 2723bdd..0000000 --- a/libFuzzer/Fuzzer/FuzzerShmemPosix.cpp +++ /dev/null @@ -1,103 +0,0 @@ -//===- 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 -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fuzzer { - -std::string SharedMemoryRegion::Path(const char *Name) { - return DirPlusFile(TmpDir(), Name); -} - -std::string SharedMemoryRegion::SemName(const char *Name, int Idx) { - std::string Res(Name); - return Res + (char)('0' + Idx); -} - -bool SharedMemoryRegion::Map(int fd) { - Data = - (uint8_t *)mmap(0, 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] == (void *)-1) - return false; - } - IAmServer = true; - return true; -} - -bool SharedMemoryRegion::Open(const char *Name) { - int fd = open(Path(Name).c_str(), O_RDWR); - if (fd < 0) return false; - struct stat stat_res; - if (0 != fstat(fd, &stat_res)) - return false; - 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] == (void *)-1) - return false; - } - IAmServer = false; - return true; -} - -bool SharedMemoryRegion::Destroy(const char *Name) { - return 0 == unlink(Path(Name).c_str()); -} - -void SharedMemoryRegion::Post(int Idx) { - assert(Idx == 0 || Idx == 1); - sem_post((sem_t*)Semaphore[Idx]); -} - -void SharedMemoryRegion::Wait(int Idx) { - assert(Idx == 0 || Idx == 1); - 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 diff --git a/libFuzzer/Fuzzer/FuzzerShmemWindows.cpp b/libFuzzer/Fuzzer/FuzzerShmemWindows.cpp deleted file mode 100644 index 6325b4b..0000000 --- a/libFuzzer/Fuzzer/FuzzerShmemWindows.cpp +++ /dev/null @@ -1,64 +0,0 @@ -//===- 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 -#include -#include -#include - -namespace fuzzer { - -std::string SharedMemoryRegion::Path(const char *Name) { - return DirPlusFile(TmpDir(), Name); -} - -std::string SharedMemoryRegion::SemName(const char *Name, int Idx) { - std::string Res(Name); - return Res + (char)('0' + Idx); -} - -bool SharedMemoryRegion::Map(int fd) { - 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 diff --git a/libFuzzer/Fuzzer/FuzzerTracePC.cpp b/libFuzzer/Fuzzer/FuzzerTracePC.cpp index c234f16..f03be7a 100644 --- a/libFuzzer/Fuzzer/FuzzerTracePC.cpp +++ b/libFuzzer/Fuzzer/FuzzerTracePC.cpp @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Trace PCs. @@ -12,70 +11,100 @@ // //===----------------------------------------------------------------------===// +#include "FuzzerTracePC.h" +#include "FuzzerBuiltins.h" +#include "FuzzerBuiltinsMsvc.h" #include "FuzzerCorpus.h" #include "FuzzerDefs.h" #include "FuzzerDictionary.h" #include "FuzzerExtFunctions.h" #include "FuzzerIO.h" -#include "FuzzerTracePC.h" #include "FuzzerUtil.h" #include "FuzzerValueBitMap.h" -#include #include -#include - -// The coverage counters and PCs. -// These are declared as global variables named "__sancov_*" to simplify -// experiments with inlined instrumentation. -alignas(8) 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 +ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC uintptr_t __sancov_lowest_stack; namespace fuzzer { TracePC TPC; -uint8_t *TracePC::Counters() const { - return __sancov_trace_pc_guard_8bit_counters; +size_t TracePC::GetTotalPCCoverage() { + return ObservedPCs.size(); } -uintptr_t *TracePC::PCs() const { - return __sancov_trace_pc_pcs; + +void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) { + if (Start == Stop) return; + if (NumModules && + Modules[NumModules - 1].Start() == Start) + return; + assert(NumModules < + sizeof(Modules) / sizeof(Modules[0])); + auto &M = Modules[NumModules++]; + uint8_t *AlignedStart = RoundUpByPage(Start); + uint8_t *AlignedStop = RoundDownByPage(Stop); + size_t NumFullPages = AlignedStop > AlignedStart ? + (AlignedStop - AlignedStart) / PageSize() : 0; + bool NeedFirst = Start < AlignedStart || !NumFullPages; + bool NeedLast = Stop > AlignedStop && AlignedStop >= AlignedStart; + M.NumRegions = NumFullPages + NeedFirst + NeedLast;; + assert(M.NumRegions > 0); + M.Regions = new Module::Region[M.NumRegions]; + assert(M.Regions); + size_t R = 0; + if (NeedFirst) + M.Regions[R++] = {Start, std::min(Stop, AlignedStart), true, false}; + for (uint8_t *P = AlignedStart; P < AlignedStop; P += PageSize()) + M.Regions[R++] = {P, P + PageSize(), true, true}; + if (NeedLast) + M.Regions[R++] = {AlignedStop, Stop, true, false}; + assert(R == M.NumRegions); + assert(M.Size() == (size_t)(Stop - Start)); + assert(M.Stop() == Stop); + assert(M.Start() == Start); + NumInline8bitCounters += M.Size(); } -size_t TracePC::GetTotalPCCoverage() { - size_t Res = 0; - for (size_t i = 1, N = GetNumPCs(); i < N; i++) - if (PCs()[i]) - Res++; - return Res; -} - -void TracePC::HandleInit(uint32_t *Start, uint32_t *Stop) { - if (Start == Stop || *Start) return; - assert(NumModules < sizeof(Modules) / sizeof(Modules[0])); - for (uint32_t *P = Start; P < Stop; P++) { - 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::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop) { + const PCTableEntry *B = reinterpret_cast(Start); + const PCTableEntry *E = reinterpret_cast(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::PrintModuleInfo() { - Printf("INFO: Loaded %zd modules (%zd guards): ", NumModules, NumGuards); - for (size_t i = 0; i < NumModules; i++) - Printf("[%p, %p), ", Modules[i].Start, Modules[i].Stop); - Printf("\n"); + if (NumModules) { + Printf("INFO: Loaded %zd modules (%zd inline 8-bit counters): ", + NumModules, NumInline8bitCounters); + for (size_t i = 0; i < NumModules; i++) + Printf("%zd [%p, %p), ", Modules[i].Size(), Modules[i].Start(), + Modules[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 (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 @@ -86,141 +115,190 @@ void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) { ValueProfileMap.AddValueModPrime(Idx); } -void TracePC::InitializePrintNewPCs() { - if (!DoPrintNewPCs) return; - assert(!PrintedPCs); - PrintedPCs = new std::set; - for (size_t i = 1; i < GetNumPCs(); i++) - if (PCs()[i]) - PrintedPCs->insert(PCs()[i]); +/// \return the address of the previous instruction. +/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.h` +inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) { +#if defined(__arm__) + // T32 (Thumb) branch instructions might be 16 or 32 bit long, + // so we return (pc-2) in that case in order to be safe. + // For A32 mode we return (pc-4) because all instructions are 32 bit long. + return (PC - 3) & (~1); +#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__aarch64__) + // PCs are always 4 byte aligned. + return PC - 4; +#elif defined(__sparc__) || defined(__mips__) + return PC - 8; +#else + return PC - 1; +#endif } -void TracePC::PrintNewPCs() { - if (!DoPrintNewPCs) return; - assert(PrintedPCs); - for (size_t i = 1; i < GetNumPCs(); i++) - if (PCs()[i] && PrintedPCs->insert(PCs()[i]).second) - PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PCs()[i]); +/// \return the address of the next instruction. +/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.cpp` +ALWAYS_INLINE uintptr_t TracePC::GetNextInstructionPc(uintptr_t PC) { +#if defined(__mips__) + return PC + 8; +#elif defined(__powerpc__) || defined(__sparc__) || defined(__arm__) || \ + defined(__aarch64__) + return PC + 4; +#else + return PC + 1; +#endif } -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; +void TracePC::UpdateObservedPCs() { + Vector CoveredFuncs; + auto ObservePC = [&](const PCTableEntry *TE) { + if (ObservedPCs.insert(TE).second && DoPrintNewPCs) { + PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p", + GetNextInstructionPc(TE->PC)); + Printf("\n"); + } + }; + + auto Observe = [&](const PCTableEntry *TE) { + if (PcIsFuncEntry(TE)) + if (++ObservedFuncs[TE->PC] == 1 && NumPrintNewFuncs) + CoveredFuncs.push_back(TE->PC); + ObservePC(TE); + }; + + if (NumPCsInPCTables) { + if (NumInline8bitCounters == NumPCsInPCTables) { + for (size_t i = 0; i < NumModules; i++) { + auto &M = Modules[i]; + assert(M.Size() == + (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start)); + for (size_t r = 0; r < M.NumRegions; r++) { + auto &R = M.Regions[r]; + if (!R.Enabled) continue; + for (uint8_t *P = R.Start; P < R.Stop; P++) + if (*P) + Observe(&ModulePCTable[i].Start[M.Idx(P)]); + } + } + } } - std::map> CoveredPCsPerModule; - std::map ModuleOffsets; - std::set CoveredDirs, CoveredFiles, CoveredFunctions, - CoveredLines; - Printf("COVERAGE:\n"); - for (size_t i = 1; i < GetNumPCs(); i++) { - uintptr_t PC = PCs()[i]; - if (!PC) continue; - std::string FileStr = DescribePC("%s", PC); - if (!IsInterestingCoverageFile(FileStr)) continue; - std::string FixedPCStr = DescribePC("%p", PC); - std::string FunctionStr = DescribePC("%F", PC); - std::string LineStr = DescribePC("%l", 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(PC), ModulePathRaw, - sizeof(ModulePathRaw), &OffsetRaw)) - continue; - std::string Module = ModulePathRaw; - uintptr_t FixedPC = std::stoull(FixedPCStr, 0, 16); - uintptr_t PcOffset = reinterpret_cast(OffsetRaw); - ModuleOffsets[Module] = FixedPC - PcOffset; - CoveredPCsPerModule[Module].push_back(PcOffset); - CoveredFunctions.insert(FunctionStr); - CoveredFiles.insert(FileStr); - CoveredDirs.insert(DirName(FileStr)); - if (!CoveredLines.insert(FileStr + ":" + LineStr).second) - continue; - Printf("COVERED: %s %s:%s\n", FunctionStr.c_str(), - FileStr.c_str(), LineStr.c_str()); + + 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", GetNextInstructionPc(CoveredFuncs[i])); + Printf("\n"); + } +} + +uintptr_t TracePC::PCTableEntryIdx(const PCTableEntry *TE) { + size_t TotalTEs = 0; + for (size_t i = 0; i < NumPCTables; i++) { + auto &M = ModulePCTable[i]; + if (TE >= M.Start && TE < M.Stop) + return TotalTEs + TE - M.Start; + TotalTEs += M.Stop - M.Start; } + assert(0); + return 0; +} - std::string CoveredDirsStr; - for (auto &Dir : CoveredDirs) { - if (!CoveredDirsStr.empty()) - CoveredDirsStr += ","; - CoveredDirsStr += Dir; +const TracePC::PCTableEntry *TracePC::PCTableEntryByIdx(uintptr_t Idx) { + for (size_t i = 0; i < NumPCTables; i++) { + auto &M = ModulePCTable[i]; + size_t Size = M.Stop - M.Start; + if (Idx < Size) return &M.Start[Idx]; + Idx -= Size; } - Printf("COVERED_DIRS: %s\n", CoveredDirsStr.c_str()); - - for (auto &M : CoveredPCsPerModule) { - std::set UncoveredFiles, UncoveredFunctions; - std::map > UncoveredLines; // Func+File => lines - auto &ModuleName = M.first; - auto &CoveredOffsets = M.second; - uintptr_t ModuleOffset = ModuleOffsets[ModuleName]; - std::sort(CoveredOffsets.begin(), CoveredOffsets.end()); - Printf("MODULE_WITH_COVERAGE: %s\n", ModuleName.c_str()); - // sancov does not yet fully support DSOs. - // std::string Cmd = "sancov -print-coverage-pcs " + ModuleName; - std::string Cmd = DisassembleCmd(ModuleName) + " | " + - SearchRegexCmd("call.*__sanitizer_cov_trace_pc_guard"); - std::string SanCovOutput; - if (!ExecuteCommandAndReadOutput(Cmd, &SanCovOutput)) { - Printf("INFO: Command failed: %s\n", Cmd.c_str()); - continue; + return nullptr; +} + +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(PC), ModulePathRaw, + sizeof(ModulePathRaw), &OffsetRaw)) + return ""; + return ModulePathRaw; +} + +template +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(PcIsFuncEntry(FE) && "Not a function entry point"); + do { + NextFE++; + } while (NextFE < M.Stop && !(PcIsFuncEntry(NextFE))); + CB(FE, NextFE, ObservedFuncs[FE->PC]); } - std::istringstream ISS(SanCovOutput); - std::string S; - while (std::getline(ISS, S, '\n')) { - size_t PcOffsetEnd = S.find(':'); - if (PcOffsetEnd == std::string::npos) - continue; - S.resize(PcOffsetEnd); - uintptr_t PcOffset = std::stoull(S, 0, 16); - if (!std::binary_search(CoveredOffsets.begin(), CoveredOffsets.end(), - PcOffset)) { - uintptr_t PC = ModuleOffset + PcOffset; - auto FileStr = DescribePC("%s", PC); - if (!IsInterestingCoverageFile(FileStr)) continue; - if (CoveredFiles.count(FileStr) == 0) { - UncoveredFiles.insert(FileStr); - continue; - } - auto FunctionStr = DescribePC("%F", PC); - if (CoveredFunctions.count(FunctionStr) == 0) { - UncoveredFunctions.insert(FunctionStr); - continue; - } - std::string LineStr = DescribePC("%l", PC); - uintptr_t Line = std::stoi(LineStr); - std::string FileLineStr = FileStr + ":" + LineStr; - if (CoveredLines.count(FileLineStr) == 0) - UncoveredLines[FunctionStr + " " + FileStr].insert(Line); - } + } +} + +void TracePC::SetFocusFunction(const std::string &FuncName) { + // This function should be called once. + assert(!FocusFunctionCounterPtr); + if (FuncName.empty()) + return; + for (size_t M = 0; M < NumModules; M++) { + auto &PCTE = ModulePCTable[M]; + size_t N = PCTE.Stop - PCTE.Start; + for (size_t I = 0; I < N; I++) { + if (!(PcIsFuncEntry(&PCTE.Start[I]))) 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()); + FocusFunctionCounterPtr = Modules[M].Start() + I; + return; } - for (auto &FileLine: UncoveredLines) - for (int Line : FileLine.second) - Printf("UNCOVERED_LINE: %s:%d\n", FileLine.first.c_str(), Line); - for (auto &Func : UncoveredFunctions) - Printf("UNCOVERED_FUNC: %s\n", Func.c_str()); - for (auto &File : UncoveredFiles) - Printf("UNCOVERED_FILE: %s\n", File.c_str()); } } -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; +bool TracePC::ObservedFocusFunction() { + return FocusFunctionCounterPtr && *FocusFunctionCounterPtr; } -void TracePC::DumpCoverage() { - if (EF->__sanitizer_dump_coverage) { - std::vector 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::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 NumEdges = Last - First; + Vector UncoveredPCs; + for (auto TE = First; TE < Last; TE++) + if (!ObservedPCs.count(TE)) + UncoveredPCs.push_back(TE->PC); + Printf("%sCOVERED_FUNC: hits: %zd", Counter ? "" : "UN", Counter); + Printf(" edges: %zd/%zd", NumEdges - UncoveredPCs.size(), NumEdges); + Printf(" %s %s:%s\n", FunctionStr.c_str(), FileStr.c_str(), + LineStr.c_str()); + if (Counter) + for (auto PC : UncoveredPCs) + Printf(" UNCOVERED_PC: %s\n", + DescribePC("%s:%l", GetNextInstructionPc(PC)).c_str()); + }; + + IterateCoveredFunctions(CoveredFunctionCallback); } // Value profile. @@ -252,11 +330,16 @@ void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, Hash ^= (T << 8) | B2[i]; } size_t I = 0; - for (; I < Len; I++) - if (B1[I] != B2[I] || (StopAtZero && B1[I] == 0)) + uint8_t HammingDistance = 0; + for (; I < Len; I++) { + if (B1[I] != B2[I] || (StopAtZero && B1[I] == 0)) { + HammingDistance = Popcountll(B1[I] ^ B2[I]); break; + } + } size_t PC = reinterpret_cast(caller_pc); size_t Idx = (PC & 4095) | (I << 12); + Idx += HammingDistance; ValueProfileMap.AddValue(Idx); TORCW.Insert(Idx ^ Hash, Word(B1, Len), Word(B2, Len)); } @@ -266,13 +349,56 @@ 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); - ValueProfileMap.AddValue(Idx); + uint64_t HammingDistance = Popcountll(ArgXor); // [0,64] + uint64_t AbsoluteDistance = (Arg1 == Arg2 ? 0 : Clzll(Arg1 - Arg2) + 1); + ValueProfileMap.AddValue(PC * 128 + HammingDistance); + ValueProfileMap.AddValue(PC * 128 + 64 + AbsoluteDistance); +} + +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() { + IterateCounterRegions([](const Module::Region &R){ + if (R.Enabled) + memset(R.Start, 0, R.Stop - R.Start); + }); +} + +ATTRIBUTE_NO_SANITIZE_ALL +void TracePC::RecordInitialStack() { + int stack; + __sancov_lowest_stack = InitialStack = reinterpret_cast(&stack); +} + +uintptr_t TracePC::GetMaxStackOffset() const { + return InitialStack - __sancov_lowest_stack; // Stack grows down +} + +void WarnAboutDeprecatedInstrumentation(const char *flag) { + // Use RawPrint because Printf cannot be used on Windows before OutputFile is + // initialized. + RawPrint(flag); + RawPrint( + " is no longer supported by libFuzzer.\n" + "Please either migrate to a compiler that supports -fsanitize=fuzzer\n" + "or use an older version of libFuzzer\n"); + exit(1); } } // namespace fuzzer @@ -281,21 +407,39 @@ extern "C" { ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_ALL void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) { - uintptr_t PC = reinterpret_cast(__builtin_return_address(0)); - uint32_t Idx = *Guard; - __sancov_trace_pc_pcs[Idx] = PC; - __sancov_trace_pc_guard_8bit_counters[Idx]++; + fuzzer::WarnAboutDeprecatedInstrumentation( + "-fsanitize-coverage=trace-pc-guard"); +} + +// 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() { + fuzzer::WarnAboutDeprecatedInstrumentation("-fsanitize-coverage=trace-pc"); } ATTRIBUTE_INTERFACE void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) { - fuzzer::TPC.HandleInit(Start, Stop); + fuzzer::WarnAboutDeprecatedInstrumentation( + "-fsanitize-coverage=trace-pc-guard"); +} + +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(__builtin_return_address(0)); + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); fuzzer::TPC.HandleCallerCallee(PC, Callee); } @@ -303,7 +447,18 @@ 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(__builtin_return_address(0)); + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + 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(GET_CALLER_PC()); fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); } @@ -311,7 +466,15 @@ 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(__builtin_return_address(0)); + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + 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(GET_CALLER_PC()); fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); } @@ -319,7 +482,15 @@ 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(__builtin_return_address(0)); + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + 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(GET_CALLER_PC()); fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); } @@ -327,7 +498,15 @@ 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(__builtin_return_address(0)); + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + 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(GET_CALLER_PC()); fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); } @@ -338,31 +517,51 @@ 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) + // Skip the most common and the most boring case: all switch values are small. + // We may want to skip this at compile-time, but it will make the + // instrumentation less general. + if (Vals[N - 1] < 256) return; - uintptr_t PC = reinterpret_cast(__builtin_return_address(0)); + // Also skip small inputs values, they won't give good signal. + if (Val < 256) + return; + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); size_t i; - uint64_t Token = 0; + uint64_t Smaller = 0; + uint64_t Larger = ~(uint64_t)0; + // Find two switch values such that Smaller < Val < Larger. + // Use 0 and 0xfff..f as the defaults. for (i = 0; i < N; i++) { - Token = Val ^ Vals[i]; - if (Val < Vals[i]) + if (Val < Vals[i]) { + Larger = Vals[i]; break; + } + if (Val > Vals[i]) Smaller = Vals[i]; } - if (ValSizeInBits == 16) - fuzzer::TPC.HandleCmp(PC + i, static_cast(Token), (uint16_t)(0)); - else if (ValSizeInBits == 32) - fuzzer::TPC.HandleCmp(PC + i, static_cast(Token), (uint32_t)(0)); - else - fuzzer::TPC.HandleCmp(PC + i, Token, (uint64_t)(0)); + // Apply HandleCmp to {Val,Smaller} and {Val, Larger}, + // use i as the PC modifier for HandleCmp. + if (ValSizeInBits == 16) { + fuzzer::TPC.HandleCmp(PC + 2 * i, static_cast(Val), + (uint16_t)(Smaller)); + fuzzer::TPC.HandleCmp(PC + 2 * i + 1, static_cast(Val), + (uint16_t)(Larger)); + } else if (ValSizeInBits == 32) { + fuzzer::TPC.HandleCmp(PC + 2 * i, static_cast(Val), + (uint32_t)(Smaller)); + fuzzer::TPC.HandleCmp(PC + 2 * i + 1, static_cast(Val), + (uint32_t)(Larger)); + } else { + fuzzer::TPC.HandleCmp(PC + 2*i, Val, Smaller); + fuzzer::TPC.HandleCmp(PC + 2*i + 1, Val, Larger); + } } ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_div4(uint32_t Val) { - uintptr_t PC = reinterpret_cast(__builtin_return_address(0)); + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); fuzzer::TPC.HandleCmp(PC, Val, (uint32_t)0); } @@ -370,7 +569,7 @@ ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_div8(uint64_t Val) { - uintptr_t PC = reinterpret_cast(__builtin_return_address(0)); + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); fuzzer::TPC.HandleCmp(PC, Val, (uint64_t)0); } @@ -378,7 +577,74 @@ ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_gep(uintptr_t Idx) { - uintptr_t PC = reinterpret_cast(__builtin_return_address(0)); + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); 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(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(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(s2), len2); +} } // extern "C" diff --git a/libFuzzer/Fuzzer/FuzzerTracePC.h b/libFuzzer/Fuzzer/FuzzerTracePC.h index 8054aed..501f3b5 100644 --- a/libFuzzer/Fuzzer/FuzzerTracePC.h +++ b/libFuzzer/Fuzzer/FuzzerTracePC.h @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // fuzzer::TracePC @@ -17,6 +16,7 @@ #include "FuzzerValueBitMap.h" #include +#include namespace fuzzer { @@ -34,7 +34,7 @@ struct TableOfRecentCompares { T A, B; }; ATTRIBUTE_NO_SANITIZE_ALL - void Insert(size_t Idx, T Arg1, T Arg2) { + void Insert(size_t Idx, const T &Arg1, const T &Arg2) { Idx = Idx % kSize; Table[Idx].A = Arg1; Table[Idx].B = Arg2; @@ -45,31 +45,59 @@ struct TableOfRecentCompares { Pair Table[kSize]; }; +template +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; - - 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 void HandleCmp(uintptr_t PC, T Arg1, T Arg2); size_t GetTotalPCCoverage(); void SetUseCounters(bool UC) { UseCounters = UC; } - void SetUseValueProfile(bool VP) { UseValueProfile = VP; } + void SetUseValueProfileMask(uint32_t VPMask) { UseValueProfileMask = VPMask; } void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; } - template size_t CollectFeatures(Callback CB) const; + void SetPrintNewFuncs(size_t P) { NumPrintNewFuncs = P; } + void UpdateObservedPCs(); + template void CollectFeatures(Callback CB) const; void ResetMaps() { ValueProfileMap.Reset(); - memset(Counters(), 0, GetNumPCs()); + ClearExtraCounters(); + ClearInlineCounters(); } + void ClearInlineCounters(); + void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize); void PrintFeatureSet(); void PrintModuleInfo(); void PrintCoverage(); - void DumpCoverage(); + + template + void IterateCoveredFunctions(CallBack CB); void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, size_t n, bool StopAtZero); @@ -77,69 +105,181 @@ class TracePC { TableOfRecentCompares TORC4; TableOfRecentCompares TORC8; TableOfRecentCompares TORCW; + MemMemTable<1024> MMT; + + void RecordInitialStack(); + uintptr_t GetMaxStackOffset() const; - void PrintNewPCs(); - void InitializePrintNewPCs(); - size_t GetNumPCs() const { return Min(kNumPCs, NumGuards + 1); } - uintptr_t GetPC(size_t Idx) { - assert(Idx < GetNumPCs()); - return PCs()[Idx]; + template + void ForEachObservedPC(CallBack CB) { + for (auto PC : ObservedPCs) + CB(PC); } + void SetFocusFunction(const std::string &FuncName); + bool ObservedFocusFunction(); + + struct PCTableEntry { + uintptr_t PC, PCFlags; + }; + + uintptr_t PCTableEntryIdx(const PCTableEntry *TE); + const PCTableEntry *PCTableEntryByIdx(uintptr_t Idx); + static uintptr_t GetNextInstructionPc(uintptr_t PC); + bool PcIsFuncEntry(const PCTableEntry *TE) { return TE->PCFlags & 1; } + private: bool UseCounters = false; - bool UseValueProfile = false; + uint32_t UseValueProfileMask = false; bool DoPrintNewPCs = false; + size_t NumPrintNewFuncs = 0; + // Module represents the array of 8-bit counters split into regions + // such that every region, except maybe the first and the last one, is one + // full page. struct Module { - uint32_t *Start, *Stop; + struct Region { + uint8_t *Start, *Stop; + bool Enabled; + bool OneFullPage; + }; + Region *Regions; + size_t NumRegions; + uint8_t *Start() { return Regions[0].Start; } + uint8_t *Stop() { return Regions[NumRegions - 1].Stop; } + size_t Size() { return Stop() - Start(); } + size_t Idx(uint8_t *P) { + assert(P >= Start() && P < Stop()); + return P - Start(); + } }; Module Modules[4096]; size_t NumModules; // linker-initialized. - size_t NumGuards; // linker-initialized. + size_t NumInline8bitCounters; - uint8_t *Counters() const; - uintptr_t *PCs() const; + template + void IterateCounterRegions(Callback CB) { + for (size_t m = 0; m < NumModules; m++) + for (size_t r = 0; r < Modules[m].NumRegions; r++) + CB(Modules[m].Regions[r]); + } - std::set *PrintedPCs; + struct { const PCTableEntry *Start, *Stop; } ModulePCTable[4096]; + size_t NumPCTables; + size_t NumPCsInPCTables; + + Set ObservedPCs; + std::unordered_map ObservedFuncs; // PC => Counter. + + uint8_t *FocusFunctionCounterPtr = nullptr; ValueBitMap ValueProfileMap; + uintptr_t InitialStack; }; template -size_t TracePC::CollectFeatures(Callback CB) const { - size_t Res = 0; - const size_t Step = 8; - uint8_t *Counters = this->Counters(); - assert(reinterpret_cast(Counters) % Step == 0); - size_t N = GetNumPCs(); - N = (N + Step - 1) & ~(Step - 1); // Round up. - for (size_t Idx = 0; Idx < N; Idx += Step) { - uint64_t Bundle = *reinterpret_cast(&Counters[Idx]); - if (!Bundle) continue; - for (size_t i = Idx; i < Idx + Step; i++) { - uint8_t Counter = (Bundle >> ((i - Idx) * 8)) & 0xff; - if (!Counter) continue; - unsigned Bit = 0; - /**/ if (Counter >= 128) Bit = 7; - else if (Counter >= 32) Bit = 6; - else if (Counter >= 16) Bit = 5; - else if (Counter >= 8) Bit = 4; - else if (Counter >= 4) Bit = 3; - else if (Counter >= 3) Bit = 2; - else if (Counter >= 2) Bit = 1; - size_t Feature = (i * 8 + Bit); - if (CB(Feature)) - Res++; +// void Callback(size_t FirstFeature, size_t Idx, uint8_t Value); +ATTRIBUTE_NO_SANITIZE_ALL +size_t 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(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(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); + return End - Begin; +} + +// Given a non-zero Counter returns a number in the range [0,7]. +template +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 // void Callback(size_t Feature) +ATTRIBUTE_NO_SANITIZE_ADDRESS +ATTRIBUTE_NOINLINE +void TracePC::CollectFeatures(Callback HandleFeature) const { + 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; + + for (size_t i = 0; i < NumModules; i++) { + for (size_t r = 0; r < Modules[i].NumRegions; r++) { + if (!Modules[i].Regions[r].Enabled) continue; + FirstFeature += 8 * ForEachNonZeroByte(Modules[i].Regions[r].Start, + Modules[i].Regions[r].Stop, + FirstFeature, Handle8bitCounter); } } - if (UseValueProfile) + + FirstFeature += + 8 * ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(), + FirstFeature, Handle8bitCounter); + + if (UseValueProfileMask) { ValueProfileMap.ForEach([&](size_t Idx) { - if (CB(N * 8 + Idx)) - Res++; + HandleFeature(FirstFeature + Idx); }); - return Res; + 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; diff --git a/libFuzzer/Fuzzer/FuzzerTraceState.cpp b/libFuzzer/Fuzzer/FuzzerTraceState.cpp deleted file mode 100644 index 2b8caa4..0000000 --- a/libFuzzer/Fuzzer/FuzzerTraceState.cpp +++ /dev/null @@ -1,170 +0,0 @@ -//===- FuzzerTraceState.cpp - Trace-based fuzzer mutator ------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// Data tracing. -//===----------------------------------------------------------------------===// - -#include "FuzzerDictionary.h" -#include "FuzzerInternal.h" -#include "FuzzerIO.h" -#include "FuzzerMutate.h" -#include "FuzzerTracePC.h" -#include -#include -#include -#include -#include - -namespace fuzzer { - -// Declared as static globals for faster checks inside the hooks. -static bool RecordingMemmem = false; -static bool DoingMyOwnMemmem = false; - -ScopedDoingMyOwnMemmem::ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = true; } -ScopedDoingMyOwnMemmem::~ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = false; } - -class TraceState { -public: - TraceState(MutationDispatcher &MD, const FuzzingOptions &Options, - const Fuzzer *F) - : MD(MD), Options(Options), F(F) {} - - void StartTraceRecording() { - if (!Options.UseMemmem) - return; - RecordingMemmem = true; - InterestingWords.clear(); - MD.ClearAutoDictionary(); - } - - void StopTraceRecording() { - if (!RecordingMemmem) - return; - for (auto &W : InterestingWords) - MD.AddWordToAutoDictionary({W}); - } - - void AddInterestingWord(const uint8_t *Data, size_t Size) { - if (!RecordingMemmem || !F->InFuzzingThread()) return; - if (Size <= 1) return; - Size = std::min(Size, Word::GetMaxSize()); - Word W(Data, Size); - InterestingWords.insert(W); - } - - private: - - // TODO: std::set is too inefficient, need to have a custom DS here. - std::set InterestingWords; - MutationDispatcher &MD; - const FuzzingOptions Options; - const Fuzzer *F; -}; - -static TraceState *TS; - -void Fuzzer::StartTraceRecording() { - if (!TS) return; - TS->StartTraceRecording(); -} - -void Fuzzer::StopTraceRecording() { - if (!TS) return; - TS->StopTraceRecording(); -} - -void Fuzzer::InitializeTraceState() { - if (!Options.UseMemmem) return; - TS = new TraceState(MD, Options, this); -} - -static size_t InternalStrnlen(const char *S, size_t MaxLen) { - size_t Len = 0; - for (; Len < MaxLen && S[Len]; Len++) {} - return Len; -} - -} // namespace fuzzer - -using fuzzer::TS; - -extern "C" { - -// We may need to avoid defining weak hooks to stay compatible with older clang. -#ifndef LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS -# define LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS 1 -#endif - -#if LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS - -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 (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 (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 (result == 0) return; // No reason to mutate. - size_t Len1 = strlen(s1); - size_t Len2 = strlen(s2); - size_t N = std::min(Len1, Len2); - if (N <= 1) return; // Not interesting. - 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) { - 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) { - 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) { - TS->AddInterestingWord(reinterpret_cast(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) { - TS->AddInterestingWord(reinterpret_cast(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::DoingMyOwnMemmem) return; - TS->AddInterestingWord(reinterpret_cast(s2), len2); -} - -#endif // LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS -} // extern "C" diff --git a/libFuzzer/Fuzzer/FuzzerUtil.cpp b/libFuzzer/Fuzzer/FuzzerUtil.cpp index f5fd3a8..7aa84a1 100644 --- a/libFuzzer/Fuzzer/FuzzerUtil.cpp +++ b/libFuzzer/Fuzzer/FuzzerUtil.cpp @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Misc utils. @@ -16,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -124,7 +124,7 @@ bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) { return true; } -bool ParseDictionaryFile(const std::string &Text, std::vector *Units) { +bool ParseDictionaryFile(const std::string &Text, Vector *Units) { if (Text.empty()) { Printf("ParseDictionaryFile: file does not exist or is empty\n"); return false; @@ -179,9 +179,13 @@ std::string Base64(const Unit &U) { return Res; } +static std::mutex SymbolizeMutex; + std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) { - if (!EF->__sanitizer_symbolize_pc) return ""; - char PcDescr[1024]; + std::unique_lock l(SymbolizeMutex, std::try_to_lock); + if (!EF->__sanitizer_symbolize_pc || !l.owns_lock()) + return ""; + char PcDescr[1024] = {}; EF->__sanitizer_symbolize_pc(reinterpret_cast(PC), SymbolizedFMT, PcDescr, sizeof(PcDescr)); PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case. @@ -195,6 +199,18 @@ void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) { Printf(FallbackFMT, PC); } +void PrintStackTrace() { + std::unique_lock 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 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) { @@ -205,14 +221,11 @@ unsigned NumberOfCpuCores() { return N; } -bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out) { - FILE *Pipe = OpenProcessPipe(Command.c_str(), "r"); - if (!Pipe) return false; - char Buff[1024]; - size_t N; - while ((N = fread(Buff, 1, sizeof(Buff), Pipe)) > 0) - Out->append(Buff, N); - return true; +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 diff --git a/libFuzzer/Fuzzer/FuzzerUtil.h b/libFuzzer/Fuzzer/FuzzerUtil.h index f84fd9e..85c5571 100644 --- a/libFuzzer/Fuzzer/FuzzerUtil.h +++ b/libFuzzer/Fuzzer/FuzzerUtil.h @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Util functions. @@ -12,6 +11,9 @@ #ifndef LLVM_FUZZER_UTIL_H #define LLVM_FUZZER_UTIL_H +#include "FuzzerBuiltins.h" +#include "FuzzerBuiltinsMsvc.h" +#include "FuzzerCommand.h" #include "FuzzerDefs.h" namespace fuzzer { @@ -39,9 +41,11 @@ void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC); std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC); -unsigned NumberOfCpuCores(); +void PrintStackTrace(); + +void PrintMemoryProfile(); -bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out); +unsigned NumberOfCpuCores(); // Platform specific functions. void SetSignalHandler(const FuzzingOptions& Options); @@ -52,25 +56,51 @@ unsigned long GetPid(); size_t GetPeakRSSMb(); -int ExecuteCommand(const std::string &Command); +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 std::vector &Args, +std::string CloneArgsWithoutX(const Vector &Args, const char *X1, const char *X2); -inline std::string CloneArgsWithoutX(const std::vector &Args, +inline std::string CloneArgsWithoutX(const Vector &Args, const char *X) { return CloneArgsWithoutX(Args, X, X); } +inline std::pair 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 - Clz(X) - 1; } + +inline size_t PageSize() { return 4096; } +inline uint8_t *RoundUpByPage(uint8_t *P) { + uintptr_t X = reinterpret_cast(P); + size_t Mask = PageSize() - 1; + X = (X + Mask) & ~Mask; + return reinterpret_cast(X); +} +inline uint8_t *RoundDownByPage(uint8_t *P) { + uintptr_t X = reinterpret_cast(P); + size_t Mask = PageSize() - 1; + X = X & ~Mask; + return reinterpret_cast(X); +} + } // namespace fuzzer #endif // LLVM_FUZZER_UTIL_H diff --git a/libFuzzer/Fuzzer/FuzzerUtilDarwin.cpp b/libFuzzer/Fuzzer/FuzzerUtilDarwin.cpp index 9674368..171db23 100644 --- a/libFuzzer/Fuzzer/FuzzerUtilDarwin.cpp +++ b/libFuzzer/Fuzzer/FuzzerUtilDarwin.cpp @@ -1,20 +1,21 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Misc utils for Darwin. //===----------------------------------------------------------------------===// #include "FuzzerDefs.h" #if LIBFUZZER_APPLE - +#include "FuzzerCommand.h" #include "FuzzerIO.h" #include #include #include +#include +#include #include // There is no header for this on macOS so declare here @@ -36,7 +37,8 @@ static sigset_t OldBlockedSignalsSet; // signal handlers when the first thread enters and restores them when the last // thread finishes execution of the function and ensures this is not racey by // using a mutex. -int ExecuteCommand(const std::string &Command) { +int ExecuteCommand(const Command &Cmd) { + std::string CmdLine = Cmd.toString(); posix_spawnattr_t SpawnAttributes; if (posix_spawnattr_init(&SpawnAttributes)) return -1; @@ -96,12 +98,17 @@ int ExecuteCommand(const std::string &Command) { pid_t Pid; char **Environ = environ; // Read from global - const char *CommandCStr = Command.c_str(); - const char *Argv[] = {"sh", "-c", CommandCStr, NULL}; + 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, - (char *const *)Argv, Environ); + Argv, Environ); (void)posix_spawnattr_destroy(&SpawnAttributes); if (!ErrorCode) { pid_t SavedPid = Pid; @@ -120,6 +127,8 @@ int ExecuteCommand(const std::string &Command) { // 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. diff --git a/libFuzzer/Fuzzer/FuzzerUtilFuchsia.cpp b/libFuzzer/Fuzzer/FuzzerUtilFuchsia.cpp new file mode 100644 index 0000000..79fd950 --- /dev/null +++ b/libFuzzer/Fuzzer/FuzzerUtilFuchsia.cpp @@ -0,0 +1,502 @@ +//===- FuzzerUtilFuchsia.cpp - Misc utils for Fuchsia. --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils implementation using Fuchsia/Zircon APIs. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" + +#if LIBFUZZER_FUCHSIA + +#include "FuzzerInternal.h" +#include "FuzzerUtil.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 { + +// 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 the exception channel. We need to claim to be a "debugger" so the + // kernel will allow us to modify and resume dying threads (see below). Once + // the channel is set, we can signal the main thread to continue and wait + // for the exception to arrive. + ScopedHandle Channel; + zx_handle_t Self = _zx_process_self(); + ExitOnErr(_zx_task_create_exception_channel( + Self, ZX_EXCEPTION_CHANNEL_DEBUGGER, &Channel.Handle), + "_zx_task_create_exception_channel"); + + ExitOnErr(_zx_object_signal(*Event, 0, ZX_USER_SIGNAL_0), + "_zx_object_signal"); + + // This thread lives as long as the process in order to keep handling + // crashes. In practice, the first crashed thread to reach the end of the + // StaticCrashHandler will end the process. + while (true) { + ExitOnErr(_zx_object_wait_one(Channel.Handle, ZX_CHANNEL_READABLE, + ZX_TIME_INFINITE, nullptr), + "_zx_object_wait_one"); + + zx_exception_info_t ExceptionInfo; + ScopedHandle Exception; + ExitOnErr(_zx_channel_read(Channel.Handle, 0, &ExceptionInfo, + &Exception.Handle, sizeof(ExceptionInfo), 1, + nullptr, nullptr), + "_zx_channel_read"); + + // Ignore informational synthetic exceptions. + if (ZX_EXCP_THREAD_STARTING == ExceptionInfo.type || + ZX_EXCP_THREAD_EXITING == ExceptionInfo.type || + ZX_EXCP_PROCESS_STARTING == ExceptionInfo.type) { + continue; + } + + // 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_exception_get_thread(Exception.Handle, &Thread.Handle), + "_zx_exception_get_thread"); + + 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(StackPtr), &GeneralRegisters, + sizeof(GeneralRegisters)); + GeneralRegisters.rsp = StackPtr; + GeneralRegisters.rip = reinterpret_cast(CrashTrampolineAsm); + +#elif defined(__aarch64__) + uintptr_t StackPtr = + (GeneralRegisters.sp - sizeof(GeneralRegisters)) & -(uintptr_t)16; + __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, + sizeof(GeneralRegisters)); + GeneralRegisters.sp = StackPtr; + GeneralRegisters.pc = reinterpret_cast(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"); + + // Set the exception to HANDLED so it resumes the thread on close. + uint32_t ExceptionState = ZX_EXCEPTION_STATE_HANDLED; + ExitOnErr(_zx_object_set_property(Exception.Handle, ZX_PROP_EXCEPTION_STATE, + &ExceptionState, sizeof(ExceptionState)), + "zx_object_set_property"); + } +} + +} // namespace + +// Platform specific functions. +void SetSignalHandler(const FuzzingOptions &Options) { + // Make sure information from libFuzzer and the sanitizers are easy to + // reassemble. `__sanitizer_log_write` has the added benefit of ensuring the + // DSO map is always available for the symbolizer. + // A uint64_t fits in 20 chars, so 64 is plenty. + char Buf[64]; + memset(Buf, 0, sizeof(Buf)); + snprintf(Buf, sizeof(Buf), "==%lu== INFO: libFuzzer starting.\n", GetPid()); + if (EF->__sanitizer_log_write) + __sanitizer_log_write(Buf, sizeof(Buf)); + Printf("%s", Buf); + + // 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, 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 +class RunOnDestruction { + public: + explicit RunOnDestruction(Fn fn) : fn_(fn) {} + ~RunOnDestruction() { fn_(); } + + private: + Fn fn_; +}; + +template +RunOnDestruction at_scope_exit(Fn fn) { + return RunOnDestruction(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 Argv(new const char *[Argc + 1]); + for (size_t i = 0; i < Argc; ++i) + Argv[i] = Args[i].c_str(); + Argv[Argc] = nullptr; + + // Determine output. On Fuchsia, the fuzzer is typically run as a component + // that lacks a mutable working directory. Fortunately, when this is the case + // a mutable output directory must be specified using "-artifact_prefix=...", + // so write the log file(s) there. + // However, we don't want to apply this logic for absolute paths. + int FdOut = STDOUT_FILENO; + if (Cmd.hasOutputFile()) { + std::string Path = Cmd.getOutputFile(); + bool IsAbsolutePath = Path.length() > 1 && Path[0] == '/'; + if (!IsAbsolutePath && Cmd.hasFlag("artifact_prefix")) + Path = Cmd.getFlagValue("artifact_prefix") + "/" + Path; + + FdOut = open(Path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0); + if (FdOut == -1) { + Printf("libFuzzer: failed to open %s: %s\n", Path.c_str(), + strerror(errno)); + return ZX_ERR_IO; + } + } + auto CloseFdOut = at_scope_exit([FdOut]() { + if (FdOut != STDOUT_FILENO) + 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, 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 diff --git a/libFuzzer/Fuzzer/FuzzerUtilLinux.cpp b/libFuzzer/Fuzzer/FuzzerUtilLinux.cpp index dfe7e6f..d5a15d1 100644 --- a/libFuzzer/Fuzzer/FuzzerUtilLinux.cpp +++ b/libFuzzer/Fuzzer/FuzzerUtilLinux.cpp @@ -1,24 +1,32 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Misc utils for Linux. //===----------------------------------------------------------------------===// #include "FuzzerDefs.h" -#if LIBFUZZER_LINUX +#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \ + LIBFUZZER_OPENBSD +#include "FuzzerCommand.h" #include +#include +#include + namespace fuzzer { -int ExecuteCommand(const std::string &Command) { - return system(Command.c_str()); +int ExecuteCommand(const Command &Cmd) { + std::string CmdLine = Cmd.toString(); + int exit_code = system(CmdLine.c_str()); + if (WIFEXITED(exit_code)) + return WEXITSTATUS(exit_code); + return exit_code; } } // namespace fuzzer -#endif // LIBFUZZER_LINUX +#endif diff --git a/libFuzzer/Fuzzer/FuzzerUtilPosix.cpp b/libFuzzer/Fuzzer/FuzzerUtilPosix.cpp index 0161309..cefe7ae 100644 --- a/libFuzzer/Fuzzer/FuzzerUtilPosix.cpp +++ b/libFuzzer/Fuzzer/FuzzerUtilPosix.cpp @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Misc utils implementation using Posix API. @@ -12,14 +11,15 @@ #if LIBFUZZER_POSIX #include "FuzzerIO.h" #include "FuzzerInternal.h" +#include "FuzzerTracePC.h" #include #include #include #include #include #include -#include #include +#include #include #include #include @@ -33,6 +33,15 @@ static void AlarmHandler(int, siginfo_t *, void *) { Fuzzer::StaticAlarmCallback(); } +static void (*upstream_segv_handler)(int, siginfo_t *, void *); + +static void SegvHandler(int sig, siginfo_t *si, void *ucontext) { + assert(si->si_signo == SIGSEGV); + if (upstream_segv_handler) + return upstream_segv_handler(sig, si, ucontext); + Fuzzer::StaticCrashSignalCallback(); +} + static void CrashHandler(int, siginfo_t *, void *) { Fuzzer::StaticCrashSignalCallback(); } @@ -41,14 +50,35 @@ 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; - memset(&sigact, 0, sizeof(sigact)); + 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) { + if (signum != SIGSEGV) + return; + upstream_segv_handler = sigact.sa_sigaction; + } + } else { + if (sigact.sa_handler != SIG_DFL && sigact.sa_handler != SIG_IGN && + sigact.sa_handler != SIG_ERR) + return; + } + + sigact = {}; + sigact.sa_flags = SA_SIGINFO; sigact.sa_sigaction = callback; if (sigaction(signum, &sigact, 0)) { Printf("libFuzzer: sigaction failed with %d\n", errno); @@ -75,7 +105,7 @@ void SetSignalHandler(const FuzzingOptions& Options) { if (Options.HandleTerm) SetSigaction(SIGTERM, InterruptHandler); if (Options.HandleSegv) - SetSigaction(SIGSEGV, CrashHandler); + SetSigaction(SIGSEGV, SegvHandler); if (Options.HandleBus) SetSigaction(SIGBUS, CrashHandler); if (Options.HandleAbrt) @@ -86,6 +116,10 @@ void SetSignalHandler(const FuzzingOptions& Options) { 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) { @@ -98,7 +132,8 @@ size_t GetPeakRSSMb() { struct rusage usage; if (getrusage(RUSAGE_SELF, &usage)) return 0; - if (LIBFUZZER_LINUX) { + if (LIBFUZZER_LINUX || LIBFUZZER_FREEBSD || LIBFUZZER_NETBSD || + LIBFUZZER_OPENBSD) { // ru_maxrss is in KiB return usage.ru_maxrss >> 10; } else if (LIBFUZZER_APPLE) { diff --git a/libFuzzer/Fuzzer/FuzzerUtilWindows.cpp b/libFuzzer/Fuzzer/FuzzerUtilWindows.cpp index 08bb3cf..ed90044 100644 --- a/libFuzzer/Fuzzer/FuzzerUtilWindows.cpp +++ b/libFuzzer/Fuzzer/FuzzerUtilWindows.cpp @@ -1,15 +1,15 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Misc utils implementation for Windows. //===----------------------------------------------------------------------===// #include "FuzzerDefs.h" #if LIBFUZZER_WINDOWS +#include "FuzzerCommand.h" #include "FuzzerIO.h" #include "FuzzerInternal.h" #include @@ -18,11 +18,12 @@ #include #include #include -#include #include #include #include -#include + +// This must be included after windows.h. +#include namespace fuzzer { @@ -84,11 +85,11 @@ void CALLBACK AlarmHandler(PVOID, BOOLEAN) { class TimerQ { HANDLE TimerQueue; public: - TimerQ() : TimerQueue(NULL) {}; + TimerQ() : TimerQueue(NULL) {} ~TimerQ() { if (TimerQueue) DeleteTimerQueueEx(TimerQueue, NULL); - }; + } void SetTimer(int Seconds) { if (!TimerQueue) { TimerQueue = CreateTimerQueue(); @@ -103,7 +104,7 @@ class TimerQ { Printf("libFuzzer: CreateTimerQueueTimer failed.\n"); exit(1); } - }; + } }; static TimerQ Timer; @@ -150,8 +151,9 @@ FILE *OpenProcessPipe(const char *Command, const char *Mode) { return _popen(Command, Mode); } -int ExecuteCommand(const std::string &Command) { - return system(Command.c_str()); +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, @@ -176,7 +178,9 @@ const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, } std::string DisassembleCmd(const std::string &FileName) { - if (ExecuteCommand("dumpbin /summary > nul") == 0) + Vector command_vector; + command_vector.push_back("dumpbin /summary > nul"); + if (ExecuteCommand(Command(command_vector)) == 0) return "dumpbin /disasm " + FileName; Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n"); exit(1); diff --git a/libFuzzer/Fuzzer/FuzzerValueBitMap.h b/libFuzzer/Fuzzer/FuzzerValueBitMap.h index 8f7ff74..bc039f1 100644 --- a/libFuzzer/Fuzzer/FuzzerValueBitMap.h +++ b/libFuzzer/Fuzzer/FuzzerValueBitMap.h @@ -1,9 +1,8 @@ //===- 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. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // ValueBitMap. @@ -35,7 +34,7 @@ struct ValueBitMap { uintptr_t WordIdx = Idx / kBitsInWord; uintptr_t BitIdx = Idx % kBitsInWord; uintptr_t Old = Map[WordIdx]; - uintptr_t New = Old | (1UL << BitIdx); + uintptr_t New = Old | (1ULL << BitIdx); Map[WordIdx] = New; return New != Old; } @@ -49,30 +48,10 @@ struct ValueBitMap { assert(Idx < kMapSizeInBits); uintptr_t WordIdx = Idx / kBitsInWord; uintptr_t BitIdx = Idx % kBitsInWord; - return Map[WordIdx] & (1UL << BitIdx); + return Map[WordIdx] & (1ULL << BitIdx); } - size_t GetNumBitsSinceLastMerge() const { return NumBits; } - - // Merges 'Other' into 'this', clears 'Other', updates NumBits, - // returns true if new bits were added. - ATTRIBUTE_TARGET_POPCNT - bool MergeFrom(ValueBitMap &Other) { - uintptr_t Res = 0; - size_t OldNumBits = NumBits; - for (size_t i = 0; i < kMapSizeInWords; i++) { - auto O = Other.Map[i]; - auto M = Map[i]; - if (O) { - Map[i] = (M |= O); - Other.Map[i] = 0; - } - if (M) - Res += __builtin_popcountll(M); - } - NumBits = Res; - return OldNumBits < NumBits; - } + size_t SizeInBits() const { return kMapSizeInBits; } template ATTRIBUTE_NO_SANITIZE_ALL @@ -85,8 +64,7 @@ struct ValueBitMap { } private: - size_t NumBits = 0; - uintptr_t Map[kMapSizeInWords] __attribute__((aligned(512))); + ATTRIBUTE_ALIGNED(512) uintptr_t Map[kMapSizeInWords]; }; } // namespace fuzzer diff --git a/libFuzzer/Fuzzer/README.txt b/libFuzzer/Fuzzer/README.txt index 79f49b5..3eee01c 100644 --- a/libFuzzer/Fuzzer/README.txt +++ b/libFuzzer/Fuzzer/README.txt @@ -1,2 +1 @@ -Move to http://llvm.org/docs/LibFuzzer.html - +See http://llvm.org/docs/LibFuzzer.html diff --git a/libFuzzer/Fuzzer/afl/afl_driver.cpp b/libFuzzer/Fuzzer/afl/afl_driver.cpp index b3a54e5..f21dfc5 100644 --- a/libFuzzer/Fuzzer/afl/afl_driver.cpp +++ b/libFuzzer/Fuzzer/afl/afl_driver.cpp @@ -1,9 +1,8 @@ //===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception //===----------------------------------------------------------------------===// /* This file allows to fuzz libFuzzer-style target functions @@ -12,8 +11,8 @@ Usage: ################################################################################ cat << EOF > test_fuzzer.cc -#include #include +#include extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (size > 0 && data[0] == 'H') if (size > 1 && data[1] == 'I') @@ -22,8 +21,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { return 0; } EOF -# Build your target with -fsanitize-coverage=trace-pc using fresh clang. -clang -g -fsanitize-coverage=trace-pc test_fuzzer.cc -c +# Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang. +clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c # Build afl-llvm-rt.o.c from the AFL distribution. clang -c -w $AFL_HOME/llvm_mode/afl-llvm-rt.o.c # Build this file, link it with afl-llvm-rt.o.o and the target code. @@ -32,55 +31,63 @@ clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o rm -rf IN OUT; mkdir IN OUT; echo z > IN/z; $AFL_HOME/afl-fuzz -i IN -o OUT ./a.out ################################################################################ -Environment Variables: -There are a few environment variables that can be set to use features that -afl-fuzz doesn't have. - AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file specified. If the file does not exist, it is created. This is useful for getting -stack traces (when using ASAN for example) or original error messages on hard to -reproduce bugs. +stack traces (when using ASAN for example) or original error messages on hard +to reproduce bugs. Note that any content written to stderr will be written to +this file instead of stderr's usual location. -AFL_DRIVER_EXTRA_STATS_FILENAME: Setting this causes afl_driver to write extra -statistics to the file specified. Currently these are peak_rss_mb -(the peak amount of virtual memory used in MB) and slowest_unit_time_secs. If -the file does not exist it is created. If the file does exist then -afl_driver assumes it was restarted by afl-fuzz and will try to read old -statistics from the file. If that fails then the process will quit. +AFL_DRIVER_CLOSE_FD_MASK: Similar to libFuzzer's -close_fd_mask behavior option. +If 1, close stdout at startup. If 2 close stderr; if 3 close both. */ #include -#include +#include #include +#include #include #include #include -#include -#include -#include -#include + +#include +#include +#include + // Platform detection. Copied from FuzzerInternal.h #ifdef __linux__ #define LIBFUZZER_LINUX 1 #define LIBFUZZER_APPLE 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 #elif __APPLE__ #define LIBFUZZER_LINUX 0 #define LIBFUZZER_APPLE 1 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#elif __NetBSD__ +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_NETBSD 1 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#elif __FreeBSD__ +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 1 +#define LIBFUZZER_OPENBSD 0 +#elif __OpenBSD__ +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 1 #else #error "Support for your platform has not been implemented" #endif -// Used to avoid repeating error checking boilerplate. If cond is false, a -// fatal error has occured in the program. In this event print error_message -// to stderr and abort(). Otherwise do nothing. Note that setting -// AFL_DRIVER_STDERR_DUPLICATE_FILENAME may cause error_message to be appended -// to the file as well, if the error occurs after the duplication is performed. -#define CHECK_ERROR(cond, error_message) \ - if (!(cond)) { \ - fprintf(stderr, (error_message)); \ - abort(); \ - } - // libFuzzer interface is thin, so we don't include any libFuzzer headers. extern "C" { int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); @@ -94,140 +101,41 @@ static volatile char suppress_warning2 = AFL_PERSISTENT[0]; // Notify AFL about deferred forkserver. static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; -extern "C" void __afl_manual_init(); +extern "C" void __afl_manual_init(); static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0]; // Input buffer. static const size_t kMaxAflInputSize = 1 << 20; static uint8_t AflInputBuf[kMaxAflInputSize]; -// Variables we need for writing to the extra stats file. -static FILE *extra_stats_file = NULL; -static uint32_t previous_peak_rss = 0; -static time_t slowest_unit_time_secs = 0; -static const int kNumExtraStats = 2; -static const char *kExtraStatsFormatString = "peak_rss_mb : %u\n" - "slowest_unit_time_sec : %u\n"; - -// Copied from FuzzerUtil.cpp. -size_t GetPeakRSSMb() { - struct rusage usage; - if (getrusage(RUSAGE_SELF, &usage)) - return 0; - if (LIBFUZZER_LINUX) { - // ru_maxrss is in KiB - return usage.ru_maxrss >> 10; - } else if (LIBFUZZER_APPLE) { - // ru_maxrss is in bytes - return usage.ru_maxrss >> 20; - } - assert(0 && "GetPeakRSSMb() is not implemented for your platform"); - return 0; -} - -// Based on SetSigaction in FuzzerUtil.cpp -static void SetSigaction(int signum, - void (*callback)(int, siginfo_t *, void *)) { - struct sigaction sigact; - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_sigaction = callback; - if (sigaction(signum, &sigact, 0)) { - fprintf(stderr, "libFuzzer: sigaction failed with %d\n", errno); - exit(1); - } -} - -// Write extra stats to the file specified by the user. If none is specified -// this function will never be called. -static void write_extra_stats() { - uint32_t peak_rss = GetPeakRSSMb(); - - if (peak_rss < previous_peak_rss) - peak_rss = previous_peak_rss; - - int chars_printed = fprintf(extra_stats_file, kExtraStatsFormatString, - peak_rss, slowest_unit_time_secs); - - CHECK_ERROR(chars_printed != 0, "Failed to write extra_stats_file"); - - CHECK_ERROR(fclose(extra_stats_file) == 0, - "Failed to close extra_stats_file"); -} - -// Call write_extra_stats before we exit. -static void crash_handler(int, siginfo_t *, void *) { - // Make sure we don't try calling write_extra_stats again if we crashed while - // trying to call it. - static bool first_crash = true; - CHECK_ERROR(first_crash, - "Crashed in crash signal handler. This is a bug in the fuzzer."); - - first_crash = false; - write_extra_stats(); -} - -// If the user has specified an extra_stats_file through the environment -// variable AFL_DRIVER_EXTRA_STATS_FILENAME, then perform necessary set up -// to write stats to it on exit. If no file is specified, do nothing. Otherwise -// install signal and exit handlers to write to the file when the process exits. -// Then if the file doesn't exist create it and set extra stats to 0. But if it -// does exist then read the initial values of the extra stats from the file -// and check that the file is writable. -static void maybe_initialize_extra_stats() { - // If AFL_DRIVER_EXTRA_STATS_FILENAME isn't set then we have nothing to do. - char *extra_stats_filename = getenv("AFL_DRIVER_EXTRA_STATS_FILENAME"); - if (!extra_stats_filename) - return; - - // Open the file and find the previous peak_rss_mb value. - // This is necessary because the fuzzing process is restarted after N - // iterations are completed. So we may need to get this value from a previous - // process to be accurate. - extra_stats_file = fopen(extra_stats_filename, "r"); - - // If extra_stats_file already exists: read old stats from it. - if (extra_stats_file) { - int matches = fscanf(extra_stats_file, kExtraStatsFormatString, - &previous_peak_rss, &slowest_unit_time_secs); - - // Make sure we have read a real extra stats file and that we have used it - // to set slowest_unit_time_secs and previous_peak_rss. - CHECK_ERROR(matches == kNumExtraStats, "Extra stats file is corrupt"); - - CHECK_ERROR(fclose(extra_stats_file) == 0, "Failed to close file"); - - // Now open the file for writing. - extra_stats_file = fopen(extra_stats_filename, "w"); - CHECK_ERROR(extra_stats_file, - "Failed to open extra stats file for writing"); - } else { - // Looks like this is the first time in a fuzzing job this is being called. - extra_stats_file = fopen(extra_stats_filename, "w+"); - CHECK_ERROR(extra_stats_file, "failed to create extra stats file"); +// Use this optionally defined function to output sanitizer messages even if +// user asks to close stderr. +__attribute__((weak)) extern "C" void __sanitizer_set_report_fd(void *); + +// Keep track of where stderr content is being written to, so that +// dup_and_close_stderr can use the correct one. +static FILE *output_file = stderr; + +// Experimental feature to use afl_driver without AFL's deferred mode. +// Needs to run before __afl_auto_init. +__attribute__((constructor(0))) static void __decide_deferred_forkserver(void) { + if (getenv("AFL_DRIVER_DONT_DEFER")) { + if (unsetenv("__AFL_DEFER_FORKSRV")) { + perror("Failed to unset __AFL_DEFER_FORKSRV"); + abort(); + } } - - // Make sure that crash_handler gets called on any kind of fatal error. - int crash_signals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGILL, SIGFPE, SIGINT, - SIGTERM}; - - const size_t num_signals = sizeof(crash_signals) / sizeof(crash_signals[0]); - - for (size_t idx = 0; idx < num_signals; idx++) - SetSigaction(crash_signals[idx], crash_handler); - - // Make sure it gets called on other kinds of exits. - atexit(write_extra_stats); } // If the user asks us to duplicate stderr, then do it. static void maybe_duplicate_stderr() { - char* stderr_duplicate_filename = + char *stderr_duplicate_filename = getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); if (!stderr_duplicate_filename) return; - FILE* stderr_duplicate_stream = + FILE *stderr_duplicate_stream = freopen(stderr_duplicate_filename, "a+", stderr); if (!stderr_duplicate_stream) { @@ -236,6 +144,54 @@ static void maybe_duplicate_stderr() { "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); abort(); } + output_file = stderr_duplicate_stream; +} + +// Most of these I/O functions were inspired by/copied from libFuzzer's code. +static void discard_output(int fd) { + FILE *temp = fopen("/dev/null", "w"); + if (!temp) + abort(); + dup2(fileno(temp), fd); + fclose(temp); +} + +static void close_stdout() { discard_output(STDOUT_FILENO); } + +// Prevent the targeted code from writing to "stderr" but allow sanitizers and +// this driver to do so. +static void dup_and_close_stderr() { + int output_fileno = fileno(output_file); + int output_fd = dup(output_fileno); + if (output_fd <= 0) + abort(); + FILE *new_output_file = fdopen(output_fd, "w"); + if (!new_output_file) + abort(); + if (!__sanitizer_set_report_fd) + return; + __sanitizer_set_report_fd(reinterpret_cast(output_fd)); + discard_output(output_fileno); +} + +static void Printf(const char *Fmt, ...) { + va_list ap; + va_start(ap, Fmt); + vfprintf(output_file, Fmt, ap); + va_end(ap); + fflush(output_file); +} + +// Close stdout and/or stderr if user asks for it. +static void maybe_close_fd_mask() { + char *fd_mask_str = getenv("AFL_DRIVER_CLOSE_FD_MASK"); + if (!fd_mask_str) + return; + int fd_mask = atoi(fd_mask_str); + if (fd_mask & 2) + dup_and_close_stderr(); + if (fd_mask & 1) + close_stdout(); } // Define LLVMFuzzerMutate to avoid link failures for targets that use it @@ -245,31 +201,64 @@ extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { return 0; } +// Execute any files provided as parameters. +static int ExecuteFilesOnyByOne(int argc, char **argv) { + for (int i = 1; i < argc; i++) { + std::ifstream in(argv[i], std::ios::binary); + in.seekg(0, in.end); + size_t length = in.tellg(); + in.seekg (0, in.beg); + std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl; + // Allocate exactly length bytes so that we reliably catch buffer overflows. + std::vector bytes(length); + in.read(bytes.data(), bytes.size()); + assert(in); + LLVMFuzzerTestOneInput(reinterpret_cast(bytes.data()), + bytes.size()); + std::cout << "Execution successful" << std::endl; + } + return 0; +} + int main(int argc, char **argv) { - fprintf(stderr, "======================= INFO =========================\n" - "This binary is built for AFL-fuzz.\n" - "To run the target function on a single input execute this:\n" - " %s < INPUT_FILE\n" - "To run the fuzzing execute this:\n" - " afl-fuzz [afl-flags] %s [N] " - "-- run N fuzzing iterations before " - "re-spawning the process (default: 1000)\n" - "======================================================\n", - argv[0], argv[0]); + Printf( + "======================= INFO =========================\n" + "This binary is built for AFL-fuzz.\n" + "To run the target function on individual input(s) execute this:\n" + " %s < INPUT_FILE\n" + "or\n" + " %s INPUT_FILE1 [INPUT_FILE2 ... ]\n" + "To fuzz with afl-fuzz execute this:\n" + " afl-fuzz [afl-flags] %s [-N]\n" + "afl-fuzz will run N iterations before " + "re-spawning the process (default: 1000)\n" + "======================================================\n", + argv[0], argv[0], argv[0]); + + maybe_duplicate_stderr(); + maybe_close_fd_mask(); if (LLVMFuzzerInitialize) LLVMFuzzerInitialize(&argc, &argv); // Do any other expensive one-time initialization here. - maybe_duplicate_stderr(); - maybe_initialize_extra_stats(); - - __afl_manual_init(); + if (!getenv("AFL_DRIVER_DONT_DEFER")) + __afl_manual_init(); int N = 1000; - if (argc >= 2) - N = atoi(argv[1]); + if (argc == 2 && argv[1][0] == '-') + N = atoi(argv[1] + 1); + else if(argc == 2 && (N = atoi(argv[1])) > 0) + Printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N); + else if (argc > 1) + return ExecuteFilesOnyByOne(argc, argv); + assert(N > 0); - time_t unit_time_secs; + + // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization + // on the first execution of LLVMFuzzerTestOneInput is ignored. + uint8_t dummy_input[1] = {0}; + LLVMFuzzerTestOneInput(dummy_input, 1); + int num_runs = 0; while (__afl_persistent_loop(N)) { ssize_t n_read = read(0, AflInputBuf, kMaxAflInputSize); @@ -278,25 +267,10 @@ int main(int argc, char **argv) { // overflows. Don't use unique_ptr/etc to avoid extra dependencies. uint8_t *copy = new uint8_t[n_read]; memcpy(copy, AflInputBuf, n_read); - - struct timeval unit_start_time; - CHECK_ERROR(gettimeofday(&unit_start_time, NULL) == 0, - "Calling gettimeofday failed"); - num_runs++; LLVMFuzzerTestOneInput(copy, n_read); - - struct timeval unit_stop_time; - CHECK_ERROR(gettimeofday(&unit_stop_time, NULL) == 0, - "Calling gettimeofday failed"); - - // Update slowest_unit_time_secs if we see a new max. - unit_time_secs = unit_stop_time.tv_sec - unit_start_time.tv_sec; - if (slowest_unit_time_secs < unit_time_secs) - slowest_unit_time_secs = unit_time_secs; - delete[] copy; } } - fprintf(stderr, "%s: successfully executed %d input(s)\n", argv[0], num_runs); + Printf("%s: successfully executed %d input(s)\n", argv[0], num_runs); } diff --git a/libFuzzer/Fuzzer/build.sh b/libFuzzer/Fuzzer/build.sh index 4556af5..504e54e 100755 --- a/libFuzzer/Fuzzer/build.sh +++ b/libFuzzer/Fuzzer/build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh LIBFUZZER_SRC_DIR=$(dirname $0) CXX="${CXX:-clang}" for f in $LIBFUZZER_SRC_DIR/*.cpp; do diff --git a/libFuzzer/Fuzzer/cxx.dict b/libFuzzer/Fuzzer/cxx.dict deleted file mode 100644 index 41350f4..0000000 --- a/libFuzzer/Fuzzer/cxx.dict +++ /dev/null @@ -1,122 +0,0 @@ -"++" -"--" -"<<" -">>" -"+=" -"-=" -"*=" -"/=" -">>=" -"<<=" -"&=" -"|=" -"^=" -"%=" -"!=" -"&&" -"||" -"==" -">=" -"<=" -"->" -"alignas" -"alignof" -"and" -"and_eq" -"asm" -"auto" -"bitand" -"bitor" -"bool" -"break" -"case" -"catch" -"char" -"char16_t" -"char32_t" -"class" -"compl" -"concept" -"const" -"constexpr" -"const_cast" -"continue" -"decltype" -"default" -"delete" -"do" -"double" -"dynamic_cast" -"else" -"enum" -"explicit" -"export" -"extern" -"false" -"float" -"for" -"friend" -"goto" -"if" -"inline" -"int" -"long" -"mutable" -"namespace" -"new" -"noexcept" -"not" -"not_eq" -"nullptr" -"operator" -"or" -"or_eq" -"private" -"protected" -"public" -"register" -"reinterpret_cast" -"requires" -"return" -"short" -"signed" -"sizeof" -"static" -"static_assert" -"static_cast" -"struct" -"switch" -"template" -"this" -"thread_local" -"throw" -"true" -"try" -"typedef" -"typeid" -"typename" -"union" -"unsigned" -"using" -"virtual" -"void" -"volatile" -"wchar_t" -"while" -"xor" -"xor_eq" -"if" -"elif" -"else" -"endif" -"defined" -"ifdef" -"ifndef" -"define" -"undef" -"include" -"line" -"error" -"pragma" -"override" -"final" diff --git a/libFuzzer/Fuzzer/dataflow/DataFlow.cpp b/libFuzzer/Fuzzer/dataflow/DataFlow.cpp new file mode 100644 index 0000000..8bf4e25 --- /dev/null +++ b/libFuzzer/Fuzzer/dataflow/DataFlow.cpp @@ -0,0 +1,205 @@ +/*===- DataFlow.cpp - a standalone DataFlow tracer -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// An experimental data-flow tracer for fuzz targets. +// It is based on DFSan and SanitizerCoverage. +// https://clang.llvm.org/docs/DataFlowSanitizer.html +// https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-data-flow +// +// It executes the fuzz target on the given input while monitoring the +// data flow for every instrumented comparison instruction. +// +// The output shows which functions depend on which bytes of the input, +// and also provides basic-block coverage for every input. +// +// Build: +// 1. Compile this file (DataFlow.cpp) with -fsanitize=dataflow and -O2. +// 2. Compile DataFlowCallbacks.cpp with -O2 -fPIC. +// 3. Build the fuzz target with -g -fsanitize=dataflow +// -fsanitize-coverage=trace-pc-guard,pc-table,bb,trace-cmp +// 4. Link those together with -fsanitize=dataflow +// +// -fsanitize-coverage=trace-cmp inserts callbacks around every comparison +// instruction, DFSan modifies the calls to pass the data flow labels. +// The callbacks update the data flow label for the current function. +// See e.g. __dfsw___sanitizer_cov_trace_cmp1 below. +// +// -fsanitize-coverage=trace-pc-guard,pc-table,bb instruments function +// entries so that the comparison callback knows that current function. +// -fsanitize-coverage=...,bb also allows to collect basic block coverage. +// +// +// Run: +// # Collect data flow and coverage for INPUT_FILE +// # write to OUTPUT_FILE (default: stdout) +// export DFSAN_OPTIONS=fast16labels=1:warn_unimplemented=0 +// ./a.out INPUT_FILE [OUTPUT_FILE] +// +// # Print all instrumented functions. llvm-symbolizer must be present in PATH +// ./a.out +// +// Example output: +// =============== +// F0 11111111111111 +// F1 10000000000000 +// C0 1 2 3 4 5 +// C1 8 +// =============== +// "FN xxxxxxxxxx": tells what bytes of the input does the function N depend on. +// "CN X Y Z T": tells that a function N has basic blocks X, Y, and Z covered +// in addition to the function's entry block, out of T total instrumented +// blocks. +// +//===----------------------------------------------------------------------===*/ + +#include +#include +#include +#include +#include + +#include // backtrace_symbols_fd + +#include "DataFlow.h" + +extern "C" { +extern int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size); +__attribute__((weak)) extern int LLVMFuzzerInitialize(int *argc, char ***argv); +} // extern "C" + +CallbackData __dft; +static size_t InputLen; +static size_t NumIterations; +static dfsan_label **FuncLabelsPerIter; // NumIterations x NumFuncs; + +static inline bool BlockIsEntry(size_t BlockIdx) { + return __dft.PCsBeg[BlockIdx * 2 + 1] & PCFLAG_FUNC_ENTRY; +} + +const int kNumLabels = 16; + +// Prints all instrumented functions. +static int PrintFunctions() { + // We don't have the symbolizer integrated with dfsan yet. + // So use backtrace_symbols_fd and pipe it through llvm-symbolizer. + // TODO(kcc): this is pretty ugly and may break in lots of ways. + // We'll need to make a proper in-process symbolizer work with DFSan. + FILE *Pipe = popen("sed 's/(+/ /g; s/).*//g' " + "| llvm-symbolizer " + "| grep 'dfs\\$' " + "| sed 's/dfs\\$//g' " + "| c++filt", + "w"); + for (size_t I = 0; I < __dft.NumGuards; I++) { + uintptr_t PC = __dft.PCsBeg[I * 2]; + if (!BlockIsEntry(I)) continue; + void *const Buf[1] = {(void*)PC}; + backtrace_symbols_fd(Buf, 1, fileno(Pipe)); + } + pclose(Pipe); + return 0; +} + +static void PrintBinary(FILE *Out, dfsan_label L, size_t Len) { + char buf[kNumLabels + 1]; + assert(Len <= kNumLabels); + for (int i = 0; i < kNumLabels; i++) + buf[i] = (L & (1 << i)) ? '1' : '0'; + buf[Len] = 0; + fprintf(Out, "%s", buf); +} + +static void PrintDataFlow(FILE *Out) { + for (size_t Func = 0; Func < __dft.NumFuncs; Func++) { + bool HasAny = false; + for (size_t Iter = 0; Iter < NumIterations; Iter++) + if (FuncLabelsPerIter[Iter][Func]) + HasAny = true; + if (!HasAny) + continue; + fprintf(Out, "F%zd ", Func); + size_t LenOfLastIteration = kNumLabels; + if (auto Tail = InputLen % kNumLabels) + LenOfLastIteration = Tail; + for (size_t Iter = 0; Iter < NumIterations; Iter++) + PrintBinary(Out, FuncLabelsPerIter[Iter][Func], + Iter == NumIterations - 1 ? LenOfLastIteration : kNumLabels); + fprintf(Out, "\n"); + } +} + +static void PrintCoverage(FILE *Out) { + ssize_t CurrentFuncGuard = -1; + ssize_t CurrentFuncNum = -1; + ssize_t NumBlocksInCurrentFunc = -1; + for (size_t FuncBeg = 0; FuncBeg < __dft.NumGuards;) { + CurrentFuncNum++; + assert(BlockIsEntry(FuncBeg)); + size_t FuncEnd = FuncBeg + 1; + for (; FuncEnd < __dft.NumGuards && !BlockIsEntry(FuncEnd); FuncEnd++) + ; + if (__dft.BBExecuted[FuncBeg]) { + fprintf(Out, "C%zd", CurrentFuncNum); + for (size_t I = FuncBeg + 1; I < FuncEnd; I++) + if (__dft.BBExecuted[I]) + fprintf(Out, " %zd", I - FuncBeg); + fprintf(Out, " %zd\n", FuncEnd - FuncBeg); + } + FuncBeg = FuncEnd; + } +} + +int main(int argc, char **argv) { + if (LLVMFuzzerInitialize) + LLVMFuzzerInitialize(&argc, &argv); + if (argc == 1) + return PrintFunctions(); + assert(argc == 2 || argc == 3); + + const char *Input = argv[1]; + fprintf(stderr, "INFO: reading '%s'\n", Input); + FILE *In = fopen(Input, "r"); + assert(In); + fseek(In, 0, SEEK_END); + InputLen = ftell(In); + fseek(In, 0, SEEK_SET); + unsigned char *Buf = (unsigned char*)malloc(InputLen); + size_t NumBytesRead = fread(Buf, 1, InputLen, In); + assert(NumBytesRead == InputLen); + fclose(In); + + NumIterations = (NumBytesRead + kNumLabels - 1) / kNumLabels; + FuncLabelsPerIter = + (dfsan_label **)calloc(NumIterations, sizeof(dfsan_label *)); + for (size_t Iter = 0; Iter < NumIterations; Iter++) + FuncLabelsPerIter[Iter] = + (dfsan_label *)calloc(__dft.NumFuncs, sizeof(dfsan_label)); + + for (size_t Iter = 0; Iter < NumIterations; Iter++) { + fprintf(stderr, "INFO: running '%s' %zd/%zd\n", Input, Iter, NumIterations); + dfsan_flush(); + dfsan_set_label(0, Buf, InputLen); + __dft.FuncLabels = FuncLabelsPerIter[Iter]; + + size_t BaseIdx = Iter * kNumLabels; + size_t LastIdx = BaseIdx + kNumLabels < NumBytesRead ? BaseIdx + kNumLabels + : NumBytesRead; + assert(BaseIdx < LastIdx); + for (size_t Idx = BaseIdx; Idx < LastIdx; Idx++) + dfsan_set_label(1 << (Idx - BaseIdx), Buf + Idx, 1); + LLVMFuzzerTestOneInput(Buf, InputLen); + } + free(Buf); + + bool OutIsStdout = argc == 2; + fprintf(stderr, "INFO: writing dataflow to %s\n", + OutIsStdout ? "" : argv[2]); + FILE *Out = OutIsStdout ? stdout : fopen(argv[2], "w"); + PrintDataFlow(Out); + PrintCoverage(Out); + if (!OutIsStdout) fclose(Out); +} diff --git a/libFuzzer/Fuzzer/dataflow/DataFlow.h b/libFuzzer/Fuzzer/dataflow/DataFlow.h new file mode 100644 index 0000000..35849fd --- /dev/null +++ b/libFuzzer/Fuzzer/dataflow/DataFlow.h @@ -0,0 +1,32 @@ +/*===- DataFlow.h - a standalone DataFlow trace -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Internal header file to connect DataFlow.cpp and DataFlowCallbacks.cpp. +//===----------------------------------------------------------------------===*/ + +#ifndef __LIBFUZZER_DATAFLOW_H +#define __LIBFUZZER_DATAFLOW_H + +#include +#include +#include + +// This data is shared between DataFlowCallbacks.cpp and DataFlow.cpp. +struct CallbackData { + size_t NumFuncs, NumGuards; + const uintptr_t *PCsBeg, *PCsEnd; + dfsan_label *FuncLabels; // Array of NumFuncs elements. + bool *BBExecuted; // Array of NumGuards elements. +}; + +extern CallbackData __dft; + +enum { + PCFLAG_FUNC_ENTRY = 1, +}; + +#endif // __LIBFUZZER_DATAFLOW_H diff --git a/libFuzzer/Fuzzer/dataflow/DataFlowCallbacks.cpp b/libFuzzer/Fuzzer/dataflow/DataFlowCallbacks.cpp new file mode 100644 index 0000000..170b4d8 --- /dev/null +++ b/libFuzzer/Fuzzer/dataflow/DataFlowCallbacks.cpp @@ -0,0 +1,86 @@ +/*===- DataFlowCallbacks.cpp - a standalone DataFlow trace -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Instrumentation callbacks for DataFlow.cpp. +// These functions should not be instrumented by DFSan, so we +// keep them in a separate file and compile it w/o DFSan. +//===----------------------------------------------------------------------===*/ +#include "DataFlow.h" + +#include +#include +#include + +static __thread size_t CurrentFunc; +static uint32_t *GuardsBeg, *GuardsEnd; +static inline bool BlockIsEntry(size_t BlockIdx) { + return __dft.PCsBeg[BlockIdx * 2 + 1] & PCFLAG_FUNC_ENTRY; +} + +extern "C" { + +void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, + uint32_t *stop) { + assert(__dft.NumFuncs == 0 && "This tool does not support DSOs"); + assert(start < stop && "The code is not instrumented for coverage"); + if (start == stop || *start) return; // Initialize only once. + GuardsBeg = start; + GuardsEnd = stop; +} + +void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg, + const uintptr_t *pcs_end) { + if (__dft.NumGuards) return; // Initialize only once. + __dft.NumGuards = GuardsEnd - GuardsBeg; + __dft.PCsBeg = pcs_beg; + __dft.PCsEnd = pcs_end; + assert(__dft.NumGuards == (__dft.PCsEnd - __dft.PCsBeg) / 2); + for (size_t i = 0; i < __dft.NumGuards; i++) { + if (BlockIsEntry(i)) { + __dft.NumFuncs++; + GuardsBeg[i] = __dft.NumFuncs; + } + } + __dft.BBExecuted = (bool*)calloc(__dft.NumGuards, sizeof(bool)); + fprintf(stderr, "INFO: %zd instrumented function(s) observed " + "and %zd basic blocks\n", __dft.NumFuncs, __dft.NumGuards); +} + +void __sanitizer_cov_trace_pc_indir(uint64_t x){} // unused. + +void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { + size_t GuardIdx = guard - GuardsBeg; + // assert(GuardIdx < __dft.NumGuards); + __dft.BBExecuted[GuardIdx] = true; + if (!*guard) return; // not a function entry. + uint32_t FuncNum = *guard - 1; // Guards start from 1. + // assert(FuncNum < __dft.NumFuncs); + CurrentFunc = FuncNum; +} + +void __dfsw___sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases, + dfsan_label L1, dfsan_label UnusedL) { + assert(CurrentFunc < __dft.NumFuncs); + __dft.FuncLabels[CurrentFunc] |= L1; +} + +#define HOOK(Name, Type) \ + void Name(Type Arg1, Type Arg2, dfsan_label L1, dfsan_label L2) { \ + __dft.FuncLabels[CurrentFunc] |= L1 | L2; \ + } + //assert(CurrentFunc < __dft.NumFuncs); + +HOOK(__dfsw___sanitizer_cov_trace_const_cmp1, uint8_t) +HOOK(__dfsw___sanitizer_cov_trace_const_cmp2, uint16_t) +HOOK(__dfsw___sanitizer_cov_trace_const_cmp4, uint32_t) +HOOK(__dfsw___sanitizer_cov_trace_const_cmp8, uint64_t) +HOOK(__dfsw___sanitizer_cov_trace_cmp1, uint8_t) +HOOK(__dfsw___sanitizer_cov_trace_cmp2, uint16_t) +HOOK(__dfsw___sanitizer_cov_trace_cmp4, uint32_t) +HOOK(__dfsw___sanitizer_cov_trace_cmp8, uint64_t) + +} // extern "C" diff --git a/libFuzzer/Fuzzer/scripts/unbalanced_allocs.py b/libFuzzer/Fuzzer/scripts/unbalanced_allocs.py new file mode 100755 index 0000000..579e481 --- /dev/null +++ b/libFuzzer/Fuzzer/scripts/unbalanced_allocs.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +#===- lib/fuzzer/scripts/unbalanced_allocs.py ------------------------------===# +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===------------------------------------------------------------------------===# +# +# Post-process -trace_malloc=2 output and printout only allocations and frees +# unbalanced inside of fuzzer runs. +# Usage: +# my_fuzzer -trace_malloc=2 -runs=10 2>&1 | unbalanced_allocs.py -skip=5 +# +#===------------------------------------------------------------------------===# + +import argparse +import sys + +_skip = 0 + +def PrintStack(line, stack): + global _skip + if _skip > 0: + return + print('Unbalanced ' + line.rstrip()); + for l in stack: + print(l.rstrip()) + +def ProcessStack(line, f): + stack = [] + while line and line.startswith(' #'): + stack += [line] + line = f.readline() + return line, stack + +def ProcessFree(line, f, allocs): + if not line.startswith('FREE['): + return f.readline() + + addr = int(line.split()[1], 16) + next_line, stack = ProcessStack(f.readline(), f) + if addr in allocs: + del allocs[addr] + else: + PrintStack(line, stack) + return next_line + +def ProcessMalloc(line, f, allocs): + if not line.startswith('MALLOC['): + return ProcessFree(line, f, allocs) + + addr = int(line.split()[1], 16) + assert not addr in allocs + + next_line, stack = ProcessStack(f.readline(), f) + allocs[addr] = (line, stack) + return next_line + +def ProcessRun(line, f): + if not line.startswith('MallocFreeTracer: START'): + return ProcessMalloc(line, f, {}) + + allocs = {} + print(line.rstrip()) + line = f.readline() + while line: + if line.startswith('MallocFreeTracer: STOP'): + global _skip + _skip = _skip - 1 + for _, (l, s) in allocs.items(): + PrintStack(l, s) + print(line.rstrip()) + return f.readline() + line = ProcessMalloc(line, f, allocs) + return line + +def ProcessFile(f): + line = f.readline() + while line: + line = ProcessRun(line, f); + +def main(argv): + parser = argparse.ArgumentParser() + parser.add_argument('--skip', default=0, help='number of runs to ignore') + args = parser.parse_args() + global _skip + _skip = int(args.skip) + 1 + ProcessFile(sys.stdin) + +if __name__ == '__main__': + main(sys.argv) diff --git a/libFuzzer/Fuzzer/standalone/StandaloneFuzzTargetMain.c b/libFuzzer/Fuzzer/standalone/StandaloneFuzzTargetMain.c index 0d76ea4..efe512c 100644 --- a/libFuzzer/Fuzzer/standalone/StandaloneFuzzTargetMain.c +++ b/libFuzzer/Fuzzer/standalone/StandaloneFuzzTargetMain.c @@ -1,9 +1,8 @@ /*===- StandaloneFuzzTargetMain.c - standalone main() for fuzz targets. ---===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // This main() function can be linked to a fuzz target (i.e. a library @@ -33,6 +32,7 @@ int main(int argc, char **argv) { fseek(f, 0, SEEK_SET); unsigned char *buf = (unsigned char*)malloc(len); size_t n_read = fread(buf, 1, len, f); + fclose(f); assert(n_read == len); LLVMFuzzerTestOneInput(buf, len); free(buf); diff --git a/libFuzzer/Fuzzer/test/AFLDriverTest.cpp b/libFuzzer/Fuzzer/test/AFLDriverTest.cpp deleted file mode 100644 index 3dd0b61..0000000 --- a/libFuzzer/Fuzzer/test/AFLDriverTest.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Contains dummy functions used to avoid dependency on AFL. -#include -#include - -extern "C" void __afl_manual_init() {} - -extern "C" int __afl_persistent_loop(unsigned int) { - return 0; -} - -// This declaration exists to prevent the Darwin linker -// from complaining about this being a missing weak symbol. -extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { - return 0; -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - return 0; -} diff --git a/libFuzzer/Fuzzer/test/AbsNegAndConstant64Test.cpp b/libFuzzer/Fuzzer/test/AbsNegAndConstant64Test.cpp deleted file mode 100644 index 69b0d59..0000000 --- a/libFuzzer/Fuzzer/test/AbsNegAndConstant64Test.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// abs(x) < 0 and y == Const puzzle, 64-bit variant. -#include -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size < 16) return 0; - int64_t x; - uint64_t y; - memcpy(&x, Data, sizeof(x)); - memcpy(&y, Data + sizeof(x), sizeof(y)); - if (llabs(x) < 0 && y == 0xbaddcafedeadbeefULL) { - printf("BINGO; Found the target, exiting; x = 0x%lx y 0x%lx\n", x, y); - exit(1); - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/AbsNegAndConstantTest.cpp b/libFuzzer/Fuzzer/test/AbsNegAndConstantTest.cpp deleted file mode 100644 index 69075a4..0000000 --- a/libFuzzer/Fuzzer/test/AbsNegAndConstantTest.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// abs(x) < 0 and y == Const puzzle. -#include -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size < 8) return 0; - int x; - unsigned y; - memcpy(&x, Data, sizeof(x)); - memcpy(&y, Data + sizeof(x), sizeof(y)); - if (abs(x) < 0 && y == 0xbaddcafe) { - printf("BINGO; Found the target, exiting; x = 0x%x y 0x%x\n", x, y); - exit(1); - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/AccumulateAllocationsTest.cpp b/libFuzzer/Fuzzer/test/AccumulateAllocationsTest.cpp deleted file mode 100644 index 604d8fa..0000000 --- a/libFuzzer/Fuzzer/test/AccumulateAllocationsTest.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Test with a more mallocs than frees, but no leak. -#include -#include - -const int kAllocatedPointersSize = 10000; -int NumAllocatedPointers = 0; -int *AllocatedPointers[kAllocatedPointersSize]; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (NumAllocatedPointers < kAllocatedPointersSize) - AllocatedPointers[NumAllocatedPointers++] = new int; - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/BogusInitializeTest.cpp b/libFuzzer/Fuzzer/test/BogusInitializeTest.cpp deleted file mode 100644 index c7e81a5..0000000 --- a/libFuzzer/Fuzzer/test/BogusInitializeTest.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Make sure LLVMFuzzerInitialize does not change argv[0]. -#include -#include - -extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { - ***argv = 'X'; - return 0; -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - return 0; -} diff --git a/libFuzzer/Fuzzer/test/BufferOverflowOnInput.cpp b/libFuzzer/Fuzzer/test/BufferOverflowOnInput.cpp deleted file mode 100644 index b9d1405..0000000 --- a/libFuzzer/Fuzzer/test/BufferOverflowOnInput.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find the string "Hi!". -#include -#include -#include -#include -#include - -static volatile bool SeedLargeBuffer; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - assert(Data); - if (Size >= 4) - SeedLargeBuffer = true; - if (Size == 3 && SeedLargeBuffer && Data[3]) { - std::cout << "Woops, reading Data[3] w/o crashing\n"; - exit(1); - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/CMakeLists.txt b/libFuzzer/Fuzzer/test/CMakeLists.txt deleted file mode 100644 index 2a31a80..0000000 --- a/libFuzzer/Fuzzer/test/CMakeLists.txt +++ /dev/null @@ -1,267 +0,0 @@ -# Build all these tests with -O0, otherwise optimizations may merge some -# basic blocks and we'll fail to discover the targets. -# We change the flags for every build type because we might be doing -# a multi-configuration build (e.g. Xcode) where CMAKE_BUILD_TYPE doesn't -# mean anything. -set(variables_to_filter - CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_DEBUG - CMAKE_CXX_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS_MINSIZEREL - LIBFUZZER_FLAGS_BASE - ) -foreach (VARNAME ${variables_to_filter}) - string(REGEX REPLACE "([-/]O)[123s]" "\\10" ${VARNAME} "${${VARNAME}}") -endforeach() - -# Enable the coverage instrumentation (it is disabled for the Fuzzer lib). -set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp,trace-div,trace-gep -gline-tables-only") - -if(MSVC) - # For tests use the CRT specified for release build - # (asan doesn't support MDd and MTd) - if ("${LLVM_USE_CRT_RELEASE}" STREQUAL "") - set(CRT_FLAG " /MD ") - else() - set(CRT_FLAG " /${LLVM_USE_CRT_RELEASE} ") - endif() - # In order to use the sanitizers in Windows, we need to link against many - # runtime libraries which will depend on the target being created - # (executable or dll) and the c runtime library used (MT/MD). - # By default, cmake uses link.exe for linking, which fails because we don't - # specify the appropiate dependencies. - # As we don't want to consider all of that possible situations which depends - # on the implementation of the compiler-rt, the simplest option is to change - # the rules for linking executables and shared libraries, using the compiler - # instead of link.exe. Clang will consider the sanitizer flags, and - # automatically provide the required libraries to the linker. - set(CMAKE_CXX_LINK_EXECUTABLE " ${CMAKE_CXX_FLAGS} ${CRT_FLAG} -o /link ") - set(CMAKE_CXX_CREATE_SHARED_LIBRARY " ${CMAKE_CXX_FLAGS} ${CRT_FLAG} /LD -o /link ") -endif() - -add_custom_target(TestBinaries) - -# add_libfuzzer_test( -# SOURCES source0.cpp [source1.cpp ...] -# ) -# -# Declares a LibFuzzer test executable with target name LLVMFuzzer-. -# -# One or more source files to be compiled into the binary must be declared -# after the SOURCES keyword. -function(add_libfuzzer_test name) - set(multi_arg_options "SOURCES") - cmake_parse_arguments( - "add_libfuzzer_test" "" "" "${multi_arg_options}" ${ARGN}) - if ("${add_libfuzzer_test_SOURCES}" STREQUAL "") - message(FATAL_ERROR "Source files must be specified") - endif() - add_executable(LLVMFuzzer-${name} - ${add_libfuzzer_test_SOURCES} - ) - target_link_libraries(LLVMFuzzer-${name} LLVMFuzzer) - # Place binary where llvm-lit expects to find it - set_target_properties(LLVMFuzzer-${name} - PROPERTIES RUNTIME_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/lib/Fuzzer/test" - ) - add_dependencies(TestBinaries LLVMFuzzer-${name}) -endfunction() - -############################################################################### -# Basic tests -############################################################################### - -set(Tests - AbsNegAndConstantTest - AbsNegAndConstant64Test - AccumulateAllocationsTest - BogusInitializeTest - BufferOverflowOnInput - CallerCalleeTest - CounterTest - CustomCrossOverAndMutateTest - CustomCrossOverTest - CustomMutatorTest - CxxStringEqTest - DivTest - EmptyTest - EquivalenceATest - EquivalenceBTest - FourIndependentBranchesTest - FullCoverageSetTest - InitializeTest - Memcmp64BytesTest - MemcmpTest - LeakTest - LeakTimeoutTest - LoadTest - NullDerefTest - NullDerefOnEmptyTest - NthRunCrashTest - OneHugeAllocTest - OutOfMemoryTest - OutOfMemorySingleLargeMallocTest - RepeatedMemcmp - RepeatedBytesTest - SimpleCmpTest - SimpleDictionaryTest - SimpleHashTest - SimpleTest - SimpleThreadedTest - SingleByteInputTest - SingleMemcmpTest - SingleStrcmpTest - SingleStrncmpTest - SpamyTest - ShrinkControlFlowTest - ShrinkValueProfileTest - StrcmpTest - StrncmpOOBTest - StrncmpTest - StrstrTest - SwapCmpTest - SwitchTest - Switch2Test - ThreadedLeakTest - ThreadedTest - TimeoutTest - TimeoutEmptyTest - TraceMallocTest - TwoDifferentBugsTest - ) - -if(APPLE OR MSVC) - # LeakSanitizer is not supported on OSX and Windows right now - set(HAS_LSAN 0) - message(WARNING "LeakSanitizer is not supported." - " Building and running LibFuzzer LeakSanitizer tests is disabled." - ) -else() - set(HAS_LSAN 1) -endif() - -foreach(Test ${Tests}) - add_libfuzzer_test(${Test} SOURCES ${Test}.cpp) -endforeach() - -function(test_export_symbol target symbol) - if(MSVC) - set_target_properties(LLVMFuzzer-${target} PROPERTIES LINK_FLAGS - "-export:${symbol}") - endif() -endfunction() - -test_export_symbol(InitializeTest "LLVMFuzzerInitialize") -test_export_symbol(BogusInitializeTest "LLVMFuzzerInitialize") -test_export_symbol(CustomCrossOverTest "LLVMFuzzerCustomCrossOver") -test_export_symbol(CustomMutatorTest "LLVMFuzzerCustomMutator") - -############################################################################### -# Unit tests -############################################################################### - -add_executable(LLVMFuzzer-Unittest - FuzzerUnittest.cpp - ) - -add_executable(LLVMFuzzer-StandaloneInitializeTest - InitializeTest.cpp - ../standalone/StandaloneFuzzTargetMain.c - ) - -target_link_libraries(LLVMFuzzer-Unittest - gtest - gtest_main - LLVMFuzzerNoMain - ) - -target_include_directories(LLVMFuzzer-Unittest PRIVATE - "${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include" - ) - -add_dependencies(TestBinaries LLVMFuzzer-Unittest) -set_target_properties(LLVMFuzzer-Unittest - PROPERTIES RUNTIME_OUTPUT_DIRECTORY - "${CMAKE_CURRENT_BINARY_DIR}" -) - -add_dependencies(TestBinaries LLVMFuzzer-StandaloneInitializeTest) -set_target_properties(LLVMFuzzer-StandaloneInitializeTest - PROPERTIES RUNTIME_OUTPUT_DIRECTORY - "${CMAKE_CURRENT_BINARY_DIR}" -) - -############################################################################### -# Additional tests -############################################################################### - -include_directories(..) - -# add_subdirectory(uninstrumented) -add_subdirectory(no-coverage) -add_subdirectory(ubsan) - -add_library(LLVMFuzzer-DSO1 SHARED DSO1.cpp) -add_library(LLVMFuzzer-DSO2 SHARED DSO2.cpp) - -add_executable(LLVMFuzzer-DSOTest - DSOTestMain.cpp - DSOTestExtra.cpp) - -target_link_libraries(LLVMFuzzer-DSOTest - LLVMFuzzer-DSO1 - LLVMFuzzer-DSO2 - LLVMFuzzer - ) - -set_target_properties(LLVMFuzzer-DSOTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/lib/Fuzzer/test") - -if(MSVC) - set_output_directory(LLVMFuzzer-DSO1 - BINARY_DIR "${CMAKE_BINARY_DIR}/lib/Fuzzer/test" - LIBRARY_DIR "${CMAKE_BINARY_DIR}/lib/Fuzzer/test") - set_output_directory(LLVMFuzzer-DSO2 - BINARY_DIR "${CMAKE_BINARY_DIR}/lib/Fuzzer/test" - LIBRARY_DIR "${CMAKE_BINARY_DIR}/lib/Fuzzer/test") -else(MSVC) - set_output_directory(LLVMFuzzer-DSO1 - LIBRARY_DIR "${CMAKE_BINARY_DIR}/lib/Fuzzer/lib") - set_output_directory(LLVMFuzzer-DSO2 - LIBRARY_DIR "${CMAKE_BINARY_DIR}/lib/Fuzzer/lib") -endif() - -add_dependencies(TestBinaries LLVMFuzzer-DSOTest) - -############################################################################### -# Configure lit to run the tests -# -# Note this is done after declaring all tests so we can inform lit if any tests -# need to be disabled. -############################################################################### -set(LIBFUZZER_POSIX 1) -if (MSVC) - set(LIBFUZZER_POSIX 0) -endif() - -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg - ) - -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/unit/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/unit/lit.site.cfg - ) - -add_lit_testsuite(check-fuzzer "Running Fuzzer tests" - ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS TestBinaries - ) - -# Don't add dependencies on Windows. The linker step would fail on Windows, -# since cmake will use link.exe for linking and won't include compiler-rt libs. -if(NOT MSVC) - add_dependencies(check-fuzzer FileCheck sancov not) -endif() diff --git a/libFuzzer/Fuzzer/test/CallerCalleeTest.cpp b/libFuzzer/Fuzzer/test/CallerCalleeTest.cpp deleted file mode 100644 index 3ec025d..0000000 --- a/libFuzzer/Fuzzer/test/CallerCalleeTest.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. -// Try to find the target using the indirect caller-callee pairs. -#include -#include -#include -#include -#include - -typedef void (*F)(); -static F t[256]; - -void f34() { - std::cerr << "BINGO\n"; - exit(1); -} -void f23() { t[(unsigned)'d'] = f34;} -void f12() { t[(unsigned)'c'] = f23;} -void f01() { t[(unsigned)'b'] = f12;} -void f00() {} - -static F t0[256] = { - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, - f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, -}; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size < 4) return 0; - // Spoof the counters. - for (int i = 0; i < 200; i++) { - f23(); - f12(); - f01(); - } - memcpy(t, t0, sizeof(t)); - t[(unsigned)'a'] = f01; - t[Data[0]](); - t[Data[1]](); - t[Data[2]](); - t[Data[3]](); - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/CounterTest.cpp b/libFuzzer/Fuzzer/test/CounterTest.cpp deleted file mode 100644 index 4917934..0000000 --- a/libFuzzer/Fuzzer/test/CounterTest.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Test for a fuzzer: must find the case where a particular basic block is -// executed many times. -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - int Num = 0; - for (size_t i = 0; i < Size; i++) - if (Data[i] == 'A' + i) - Num++; - if (Num >= 4) { - std::cerr << "BINGO!\n"; - exit(1); - } - return 0; -} diff --git a/libFuzzer/Fuzzer/test/CustomCrossOverAndMutateTest.cpp b/libFuzzer/Fuzzer/test/CustomCrossOverAndMutateTest.cpp deleted file mode 100644 index 74fc939..0000000 --- a/libFuzzer/Fuzzer/test/CustomCrossOverAndMutateTest.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Test that libFuzzer does not crash when LLVMFuzzerMutate called from -// LLVMFuzzerCustomCrossOver. -#include -#include -#include -#include -#include -#include -#include - -#include "FuzzerInterface.h" - -static volatile int sink; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - std::string Str(reinterpret_cast(Data), Size); - if (Size && Data[0] == '0') - sink++; - return 0; -} - -extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1, - const uint8_t *Data2, size_t Size2, - uint8_t *Out, size_t MaxOutSize, - unsigned int Seed) { - std::vector Buffer(MaxOutSize * 10); - LLVMFuzzerMutate(Buffer.data(), Buffer.size(), Buffer.size()); - size_t Size = std::min(Size1, MaxOutSize); - memcpy(Out, Data1, Size); - return Size; -} diff --git a/libFuzzer/Fuzzer/test/CustomCrossOverTest.cpp b/libFuzzer/Fuzzer/test/CustomCrossOverTest.cpp deleted file mode 100644 index b624088..0000000 --- a/libFuzzer/Fuzzer/test/CustomCrossOverTest.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a cutom mutator. -#include -#include -#include -#include -#include -#include -#include - -#include "FuzzerInterface.h" - -static const char *Separator = "-_^_-"; -static const char *Target = "012-_^_-abc"; - -static volatile int sink; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - assert(Data); - std::string Str(reinterpret_cast(Data), Size); - - // Ensure that two different elements exist in the corpus. - if (Size && Data[0] == '0') sink++; - if (Size && Data[0] == 'a') sink--; - - if (Str.find(Target) != std::string::npos) { - std::cout << "BINGO; Found the target, exiting\n"; - exit(1); - } - return 0; -} - -extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1, - const uint8_t *Data2, size_t Size2, - uint8_t *Out, size_t MaxOutSize, - unsigned int Seed) { - static bool Printed; - static size_t SeparatorLen = strlen(Separator); - - if (!Printed) { - std::cerr << "In LLVMFuzzerCustomCrossover\n"; - Printed = true; - } - - std::mt19937 R(Seed); - - size_t Offset1 = 0; - size_t Len1 = R() % (Size1 - Offset1); - size_t Offset2 = 0; - size_t Len2 = R() % (Size2 - Offset2); - size_t Size = Len1 + Len2 + SeparatorLen; - - if (Size > MaxOutSize) - return 0; - - memcpy(Out, Data1 + Offset1, Len1); - memcpy(Out + Len1, Separator, SeparatorLen); - memcpy(Out + Len1 + SeparatorLen, Data2 + Offset2, Len2); - - return Len1 + Len2 + SeparatorLen; -} diff --git a/libFuzzer/Fuzzer/test/CustomMutatorTest.cpp b/libFuzzer/Fuzzer/test/CustomMutatorTest.cpp deleted file mode 100644 index 4f84519..0000000 --- a/libFuzzer/Fuzzer/test/CustomMutatorTest.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a cutom mutator. -#include -#include -#include -#include -#include - -#include "FuzzerInterface.h" - -static volatile int Sink; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - assert(Data); - if (Size > 0 && Data[0] == 'H') { - Sink = 1; - if (Size > 1 && Data[1] == 'i') { - Sink = 2; - if (Size > 2 && Data[2] == '!') { - std::cout << "BINGO; Found the target, exiting\n"; - exit(1); - } - } - } - return 0; -} - -extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, - size_t MaxSize, unsigned int Seed) { - static bool Printed; - if (!Printed) { - std::cerr << "In LLVMFuzzerCustomMutator\n"; - Printed = true; - } - return LLVMFuzzerMutate(Data, Size, MaxSize); -} diff --git a/libFuzzer/Fuzzer/test/CxxStringEqTest.cpp b/libFuzzer/Fuzzer/test/CxxStringEqTest.cpp deleted file mode 100644 index 9005ab8..0000000 --- a/libFuzzer/Fuzzer/test/CxxStringEqTest.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. Must find a specific string -// used in std::string operator ==. -#include -#include -#include -#include -#include - -static volatile int Sink; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - std::string Str((const char*)Data, Size); - bool Eq = Str == "FooBar"; - Sink = Str == "123456"; // Try to confuse the fuzzer - if (Eq) { - std::cout << "BINGO; Found the target, exiting\n"; - abort(); - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/DSO1.cpp b/libFuzzer/Fuzzer/test/DSO1.cpp deleted file mode 100644 index 72a5ec4..0000000 --- a/libFuzzer/Fuzzer/test/DSO1.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Source code for a simple DSO. -#ifdef _WIN32 -__declspec( dllexport ) -#endif -int DSO1(int a) { - if (a < 123456) - return 0; - return 1; -} - -void Uncovered1() { } diff --git a/libFuzzer/Fuzzer/test/DSO2.cpp b/libFuzzer/Fuzzer/test/DSO2.cpp deleted file mode 100644 index 2967055..0000000 --- a/libFuzzer/Fuzzer/test/DSO2.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Source code for a simple DSO. -#ifdef _WIN32 -__declspec( dllexport ) -#endif -int DSO2(int a) { - if (a < 3598235) - return 0; - return 1; -} - -void Uncovered2() {} diff --git a/libFuzzer/Fuzzer/test/DSOTestExtra.cpp b/libFuzzer/Fuzzer/test/DSOTestExtra.cpp deleted file mode 100644 index a2274d0..0000000 --- a/libFuzzer/Fuzzer/test/DSOTestExtra.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Source code for a simple DSO. - -int DSOTestExtra(int a) { - if (a < 452345) - return 0; - return 1; -} - diff --git a/libFuzzer/Fuzzer/test/DSOTestMain.cpp b/libFuzzer/Fuzzer/test/DSOTestMain.cpp deleted file mode 100644 index 3e225d8..0000000 --- a/libFuzzer/Fuzzer/test/DSOTestMain.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Source code for a simple DSO. - -#include -#include -#include -#include -extern int DSO1(int a); -extern int DSO2(int a); -extern int DSOTestExtra(int a); - -static volatile int *nil = 0; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - int x, y, z; - if (Size < sizeof(int) * 3) { - x = y = z = 0; - } else { - memcpy(&x, Data + 0 * sizeof(int), sizeof(int)); - memcpy(&y, Data + 1 * sizeof(int), sizeof(int)); - memcpy(&z, Data + 2 * sizeof(int), sizeof(int)); - } - int sum = DSO1(x) + DSO2(y) + (z ? DSOTestExtra(z) : 0); - if (sum == 3) { - fprintf(stderr, "BINGO %d %d %d\n", x, y, z); - *nil = 0; - } - return 0; -} diff --git a/libFuzzer/Fuzzer/test/DivTest.cpp b/libFuzzer/Fuzzer/test/DivTest.cpp deleted file mode 100644 index 63f6960..0000000 --- a/libFuzzer/Fuzzer/test/DivTest.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer: find the interesting argument for div. -#include -#include -#include -#include -#include - -static volatile int Sink; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size < 4) return 0; - int a; - memcpy(&a, Data, 4); - Sink = 12345678 / (987654 - a); - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/EmptyTest.cpp b/libFuzzer/Fuzzer/test/EmptyTest.cpp deleted file mode 100644 index 5e84330..0000000 --- a/libFuzzer/Fuzzer/test/EmptyTest.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -// A fuzzer with empty target function. - -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - return 0; -} diff --git a/libFuzzer/Fuzzer/test/EquivalenceATest.cpp b/libFuzzer/Fuzzer/test/EquivalenceATest.cpp deleted file mode 100644 index 7d1ebb0..0000000 --- a/libFuzzer/Fuzzer/test/EquivalenceATest.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -#include -#include -#include - -// Test for libFuzzer's "equivalence" fuzzing, part A. -extern "C" void LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size); -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - // fprintf(stderr, "A %zd\n", Size); - uint8_t Result[50]; - if (Size > 50) Size = 50; - for (size_t i = 0; i < Size; i++) - Result[Size - i - 1] = Data[i]; - LLVMFuzzerAnnounceOutput(Result, Size); - return 0; -} diff --git a/libFuzzer/Fuzzer/test/EquivalenceBTest.cpp b/libFuzzer/Fuzzer/test/EquivalenceBTest.cpp deleted file mode 100644 index b1de208..0000000 --- a/libFuzzer/Fuzzer/test/EquivalenceBTest.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -#include -#include -#include - -// Test for libFuzzer's "equivalence" fuzzing, part B. -extern "C" void LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size); -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - // fprintf(stderr, "B %zd\n", Size); - uint8_t Result[50]; - if (Size > 50) Size = 50; - for (size_t i = 0; i < Size; i++) - Result[Size - i - 1] = Data[i]; - - // Be a bit different from EquivalenceATest - if (Size > 10 && Data[5] == 'B' && Data[6] == 'C' && Data[7] == 'D') { - static int c; - if (!c) - fprintf(stderr, "ZZZZZZZ\n"); - c = 1; - Result[2]++; - } - - LLVMFuzzerAnnounceOutput(Result, Size); - return 0; -} diff --git a/libFuzzer/Fuzzer/test/FourIndependentBranchesTest.cpp b/libFuzzer/Fuzzer/test/FourIndependentBranchesTest.cpp deleted file mode 100644 index 62b3be7..0000000 --- a/libFuzzer/Fuzzer/test/FourIndependentBranchesTest.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find the string "FUZZ". -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - int bits = 0; - if (Size > 0 && Data[0] == 'F') bits |= 1; - if (Size > 1 && Data[1] == 'U') bits |= 2; - if (Size > 2 && Data[2] == 'Z') bits |= 4; - if (Size > 3 && Data[3] == 'Z') bits |= 8; - if (bits == 15) { - std::cerr << "BINGO!\n"; - exit(1); - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/FullCoverageSetTest.cpp b/libFuzzer/Fuzzer/test/FullCoverageSetTest.cpp deleted file mode 100644 index 415e0b4..0000000 --- a/libFuzzer/Fuzzer/test/FullCoverageSetTest.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find the string "FUZZER". -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - int bits = 0; - if (Size > 0 && Data[0] == 'F') bits |= 1; - if (Size > 1 && Data[1] == 'U') bits |= 2; - if (Size > 2 && Data[2] == 'Z') bits |= 4; - if (Size > 3 && Data[3] == 'Z') bits |= 8; - if (Size > 4 && Data[4] == 'E') bits |= 16; - if (Size > 5 && Data[5] == 'R') bits |= 32; - if (bits == 63) { - std::cerr << "BINGO!\n"; - exit(1); - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/FuzzerFnAdapterUnittest.cpp b/libFuzzer/Fuzzer/test/FuzzerFnAdapterUnittest.cpp deleted file mode 100644 index 11be180..0000000 --- a/libFuzzer/Fuzzer/test/FuzzerFnAdapterUnittest.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -#include "FuzzerFnAdapter.h" -#include "gtest/gtest-spi.h" -#include "gtest/gtest.h" - -namespace fuzzer { -namespace impl { - -template -bool Unpack(std::tuple *Tuple, std::initializer_list data) { - std::vector V(data); - return Unpack(V.data(), V.size(), Tuple); -} - -TEST(Unpack, Bool) { - std::tuple T; - EXPECT_TRUE(Unpack(&T, {1})); - EXPECT_TRUE(std::get<0>(T)); - - EXPECT_TRUE(Unpack(&T, {0})); - EXPECT_FALSE(std::get<0>(T)); - - EXPECT_FALSE(Unpack(&T, {})); -} - -TEST(Unpack, BoolBool) { - std::tuple T; - EXPECT_TRUE(Unpack(&T, {1, 0})); - EXPECT_TRUE(std::get<0>(T)); - EXPECT_FALSE(std::get<1>(T)); - - EXPECT_TRUE(Unpack(&T, {0, 1})); - EXPECT_FALSE(std::get<0>(T)); - EXPECT_TRUE(std::get<1>(T)); - - EXPECT_FALSE(Unpack(&T, {})); - EXPECT_FALSE(Unpack(&T, {10})); -} - -TEST(Unpack, BoolInt) { - std::tuple T; - EXPECT_TRUE(Unpack(&T, {1, 16, 2, 0, 0})); - EXPECT_TRUE(std::get<0>(T)); - EXPECT_EQ(528, std::get<1>(T)); - - EXPECT_FALSE(Unpack(&T, {1, 2})); -} - -TEST(Unpack, Vector) { - std::tuple> T; - const auto &V = std::get<0>(T); - - EXPECT_FALSE(Unpack(&T, {})); - - EXPECT_TRUE(Unpack(&T, {0})); - EXPECT_EQ(0ul, V.size()); - - EXPECT_TRUE(Unpack(&T, {0, 1, 2, 3})); - EXPECT_EQ(0ul, V.size()); - - EXPECT_TRUE(Unpack(&T, {2})); - EXPECT_EQ(0ul, V.size()); - - EXPECT_TRUE(Unpack(&T, {2, 3})); - EXPECT_EQ(1ul, V.size()); - EXPECT_EQ(3, V[0]); - - EXPECT_TRUE(Unpack(&T, {2, 9, 8})); - EXPECT_EQ(2ul, V.size()); - EXPECT_EQ(9, V[0]); - EXPECT_EQ(8, V[1]); -} - -TEST(Unpack, String) { - std::tuple T; - const auto &S = std::get<0>(T); - - EXPECT_TRUE(Unpack(&T, {2, 3})); - EXPECT_EQ(1ul, S.size()); - EXPECT_EQ(3, S[0]); -} - -template -bool UnpackAndApply(Fn F, std::initializer_list Data) { - std::vector V(Data); - return UnpackAndApply(F, V.data(), V.size()); -} - -static void fnBool(bool b) { EXPECT_TRUE(b); } - -TEST(Apply, Bool) { - EXPECT_FALSE(UnpackAndApply(fnBool, {})); - EXPECT_TRUE(UnpackAndApply(fnBool, {1})); - EXPECT_NONFATAL_FAILURE(UnpackAndApply(fnBool, {0}), - "Actual: false\nExpected: true"); -} - -static void fnInt(int i) { EXPECT_EQ(42, i); } - -TEST(Apply, Int) { - EXPECT_FALSE(UnpackAndApply(fnInt, {})); - EXPECT_TRUE(UnpackAndApply(fnInt, {42, 0, 0, 0})); - EXPECT_NONFATAL_FAILURE(UnpackAndApply(fnInt, {10, 0, 0, 0}), - "Actual: 10\nExpected: 42"); -} - -} // namespace impl -} // namespace fuzzer diff --git a/libFuzzer/Fuzzer/test/InitializeTest.cpp b/libFuzzer/Fuzzer/test/InitializeTest.cpp deleted file mode 100644 index 0d6a0fd..0000000 --- a/libFuzzer/Fuzzer/test/InitializeTest.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Make sure LLVMFuzzerInitialize is called. -#include -#include -#include -#include -#include -#include - -static char *argv0; - -extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { - assert(*argc > 0); - argv0 = **argv; - fprintf(stderr, "LLVMFuzzerInitialize: %s\n", argv0); - return 0; -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size == strlen(argv0) && - !strncmp(reinterpret_cast(Data), argv0, Size)) { - fprintf(stderr, "BINGO %s\n", argv0); - exit(1); - } - return 0; -} diff --git a/libFuzzer/Fuzzer/test/LargeTest.cpp b/libFuzzer/Fuzzer/test/LargeTest.cpp deleted file mode 100644 index 83ed619..0000000 --- a/libFuzzer/Fuzzer/test/LargeTest.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// A fuzz target with lots of edges. -#include -#include - -static inline void break_optimization(const void *arg) { - __asm__ __volatile__("" : : "r" (arg) : "memory"); -} - -#define A \ - do { \ - i++; \ - c++; \ - if (Data[(i + __LINE__) % Size] == (c % 256)) \ - break_optimization(Data); \ - else \ - break_optimization(0); \ - } while (0) - -// for (int i = 0, n = Data[(__LINE__ - 1) % Size] % 16; i < n; i++) - -#define B do{A; A; A; A; A; A; A; A; A; A; A; A; A; A; A; A; A; A; }while(0) -#define C do{B; B; B; B; B; B; B; B; B; B; B; B; B; B; B; B; B; B; }while(0) -#define D do{C; C; C; C; C; C; C; C; C; C; C; C; C; C; C; C; C; C; }while(0) -#define E do{D; D; D; D; D; D; D; D; D; D; D; D; D; D; D; D; D; D; }while(0) - -volatile int sink; -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (!Size) return 0; - int c = 0; - int i = 0; - D; - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/LeakTest.cpp b/libFuzzer/Fuzzer/test/LeakTest.cpp deleted file mode 100644 index 22e5164..0000000 --- a/libFuzzer/Fuzzer/test/LeakTest.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Test with a leak. -#include -#include - -static volatile void *Sink; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size > 0 && *Data == 'H') { - Sink = new int; - Sink = nullptr; - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/LeakTimeoutTest.cpp b/libFuzzer/Fuzzer/test/LeakTimeoutTest.cpp deleted file mode 100644 index 4f31b3e..0000000 --- a/libFuzzer/Fuzzer/test/LeakTimeoutTest.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Test with a leak. -#include -#include - -static volatile int *Sink; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (!Size) return 0; - Sink = new int; - Sink = new int; - while (Sink) *Sink = 0; // Infinite loop. - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/LoadTest.cpp b/libFuzzer/Fuzzer/test/LoadTest.cpp deleted file mode 100644 index eef16c7..0000000 --- a/libFuzzer/Fuzzer/test/LoadTest.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer: find interesting value of array index. -#include -#include -#include -#include -#include - -static volatile int Sink; -const int kArraySize = 1234567; -int array[kArraySize]; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size < 8) return 0; - uint64_t a = 0; - memcpy(&a, Data, 8); - Sink = array[a % (kArraySize + 1)]; - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/Memcmp64BytesTest.cpp b/libFuzzer/Fuzzer/test/Memcmp64BytesTest.cpp deleted file mode 100644 index e81526b..0000000 --- a/libFuzzer/Fuzzer/test/Memcmp64BytesTest.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find a particular string. -#include -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - const char kString64Bytes[] = - "123456789 123456789 123456789 123456789 123456789 123456789 1234"; - assert(sizeof(kString64Bytes) == 65); - if (Size >= 64 && memcmp(Data, kString64Bytes, 64) == 0) { - fprintf(stderr, "BINGO\n"); - exit(1); - } - return 0; -} diff --git a/libFuzzer/Fuzzer/test/MemcmpTest.cpp b/libFuzzer/Fuzzer/test/MemcmpTest.cpp deleted file mode 100644 index fdbf946..0000000 --- a/libFuzzer/Fuzzer/test/MemcmpTest.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find a particular string. -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - // TODO: check other sizes. - if (Size >= 8 && memcmp(Data, "01234567", 8) == 0) { - if (Size >= 12 && memcmp(Data + 8, "ABCD", 4) == 0) { - if (Size >= 14 && memcmp(Data + 12, "XY", 2) == 0) { - if (Size >= 17 && memcmp(Data + 14, "KLM", 3) == 0) { - if (Size >= 27 && memcmp(Data + 17, "ABCDE-GHIJ", 10) == 0){ - fprintf(stderr, "BINGO %zd\n", Size); - for (size_t i = 0; i < Size; i++) { - uint8_t C = Data[i]; - if (C >= 32 && C < 127) - fprintf(stderr, "%c", C); - } - fprintf(stderr, "\n"); - exit(1); - } - } - } - } - } - return 0; -} diff --git a/libFuzzer/Fuzzer/test/NotinstrumentedTest.cpp b/libFuzzer/Fuzzer/test/NotinstrumentedTest.cpp deleted file mode 100644 index ffe952c..0000000 --- a/libFuzzer/Fuzzer/test/NotinstrumentedTest.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// This test should not be instrumented. -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/NthRunCrashTest.cpp b/libFuzzer/Fuzzer/test/NthRunCrashTest.cpp deleted file mode 100644 index b43e69e..0000000 --- a/libFuzzer/Fuzzer/test/NthRunCrashTest.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Crash on the N-th execution. -#include -#include -#include - -static int Counter; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Counter++ == 1000) { - std::cout << "BINGO; Found the target, exiting\n"; - exit(1); - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/NullDerefOnEmptyTest.cpp b/libFuzzer/Fuzzer/test/NullDerefOnEmptyTest.cpp deleted file mode 100644 index 1537109..0000000 --- a/libFuzzer/Fuzzer/test/NullDerefOnEmptyTest.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find the empty string. -#include -#include -#include -#include - -static volatile int *Null = 0; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size == 0) { - std::cout << "Found the target, dereferencing NULL\n"; - *Null = 1; - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/NullDerefTest.cpp b/libFuzzer/Fuzzer/test/NullDerefTest.cpp deleted file mode 100644 index 3f03d24..0000000 --- a/libFuzzer/Fuzzer/test/NullDerefTest.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find the string "Hi!". -#include -#include -#include -#include - -static volatile int Sink; -static volatile int *Null = 0; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size > 0 && Data[0] == 'H') { - Sink = 1; - if (Size > 1 && Data[1] == 'i') { - Sink = 2; - if (Size > 2 && Data[2] == '!') { - std::cout << "Found the target, dereferencing NULL\n"; - *Null = 1; - } - } - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/OneHugeAllocTest.cpp b/libFuzzer/Fuzzer/test/OneHugeAllocTest.cpp deleted file mode 100644 index 8d3d1d6..0000000 --- a/libFuzzer/Fuzzer/test/OneHugeAllocTest.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Tests OOM handling when there is a single large allocation. -#include -#include -#include -#include -#include -#include - -static volatile char *SinkPtr; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size > 0 && Data[0] == 'H') { - if (Size > 1 && Data[1] == 'i') { - if (Size > 2 && Data[2] == '!') { - size_t kSize = (size_t)1 << 31; - char *p = new char[kSize]; - memset(p, 0, kSize); - SinkPtr = p; - delete [] p; - } - } - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp b/libFuzzer/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp deleted file mode 100644 index 316b768..0000000 --- a/libFuzzer/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Tests OOM handling. -#include -#include -#include -#include -#include -#include - -static volatile char *SinkPtr; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size > 0 && Data[0] == 'H') { - if (Size > 1 && Data[1] == 'i') { - if (Size > 2 && Data[2] == '!') { - size_t kSize = 0x20000000U; - char *p = new char[kSize]; - SinkPtr = p; - delete [] p; - } - } - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/OutOfMemoryTest.cpp b/libFuzzer/Fuzzer/test/OutOfMemoryTest.cpp deleted file mode 100644 index 078a39e..0000000 --- a/libFuzzer/Fuzzer/test/OutOfMemoryTest.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Tests OOM handling. -#include -#include -#include -#include -#include -#include -#include - -static volatile char *SinkPtr; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size > 0 && Data[0] == 'H') { - if (Size > 1 && Data[1] == 'i') { - if (Size > 2 && Data[2] == '!') { - while (true) { - size_t kSize = 1 << 28; - char *p = new char[kSize]; - memset(p, 0, kSize); - SinkPtr = p; - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - } - } - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/RepeatedBytesTest.cpp b/libFuzzer/Fuzzer/test/RepeatedBytesTest.cpp deleted file mode 100644 index 2fa6c78..0000000 --- a/libFuzzer/Fuzzer/test/RepeatedBytesTest.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find repeated bytes. -#include -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - assert(Data); - // Looking for AAAAAAAAAAAAAAAAAAAAAA or some such. - size_t CurA = 0, MaxA = 0; - for (size_t i = 0; i < Size; i++) { - // Make sure there are no conditionals in the loop so that - // coverage can't help the fuzzer. - int EQ = Data[i] == 'A'; - CurA = EQ * (CurA + 1); - int GT = CurA > MaxA; - MaxA = GT * CurA + (!GT) * MaxA; - } - if (MaxA >= 20) { - std::cout << "BINGO; Found the target (Max: " << MaxA << "), exiting\n"; - exit(0); - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/RepeatedMemcmp.cpp b/libFuzzer/Fuzzer/test/RepeatedMemcmp.cpp deleted file mode 100644 index 7377f65..0000000 --- a/libFuzzer/Fuzzer/test/RepeatedMemcmp.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - - -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - int Matches1 = 0; - for (size_t i = 0; i + 2 < Size; i += 3) - if (!memcmp(Data + i, "foo", 3)) - Matches1++; - int Matches2 = 0; - for (size_t i = 0; i + 2 < Size; i += 3) - if (!memcmp(Data + i, "bar", 3)) - Matches2++; - - if (Matches1 > 10 && Matches2 > 10) { - fprintf(stderr, "BINGO!\n"); - exit(1); - } - return 0; -} diff --git a/libFuzzer/Fuzzer/test/ShrinkControlFlowTest.cpp b/libFuzzer/Fuzzer/test/ShrinkControlFlowTest.cpp deleted file mode 100644 index 0fd7c5e..0000000 --- a/libFuzzer/Fuzzer/test/ShrinkControlFlowTest.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Test that we can find the minimal item in the corpus (3 bytes: "FUZ"). -#include -#include -#include -#include -#include - -static volatile int Sink; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - int8_t Ids[256]; - memset(Ids, -1, sizeof(Ids)); - for (size_t i = 0; i < Size; i++) - if (Ids[Data[i]] == -1) - Ids[Data[i]] = i; - int F = Ids[(unsigned char)'F']; - int U = Ids[(unsigned char)'U']; - int Z = Ids[(unsigned char)'Z']; - if (F >= 0 && U > F && Z > U) { - Sink++; - //fprintf(stderr, "IDS: %d %d %d\n", F, U, Z); - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/ShrinkValueProfileTest.cpp b/libFuzzer/Fuzzer/test/ShrinkValueProfileTest.cpp deleted file mode 100644 index 026b8ce..0000000 --- a/libFuzzer/Fuzzer/test/ShrinkValueProfileTest.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Test that we can find the minimal item in the corpus (3 bytes: "FUZ"). -#include -#include -#include -#include -#include - -static volatile uint32_t Sink; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size < sizeof(uint32_t)) return 0; - uint32_t X, Y; - size_t Offset = Size < 8 ? 0 : Size / 2; - memcpy(&X, Data + Offset, sizeof(uint32_t)); - memcpy(&Y, "FUZZ", sizeof(uint32_t)); - Sink = X == Y; - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/SignedIntOverflowTest.cpp b/libFuzzer/Fuzzer/test/SignedIntOverflowTest.cpp deleted file mode 100644 index 7df32ad..0000000 --- a/libFuzzer/Fuzzer/test/SignedIntOverflowTest.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Test for signed-integer-overflow. -#include -#include -#include -#include -#include -#include - -static volatile int Sink; -static int Large = INT_MAX; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - assert(Data); - if (Size > 0 && Data[0] == 'H') { - Sink = 1; - if (Size > 1 && Data[1] == 'i') { - Sink = 2; - if (Size > 2 && Data[2] == '!') { - Large++; // int overflow. - } - } - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/SimpleCmpTest.cpp b/libFuzzer/Fuzzer/test/SimpleCmpTest.cpp deleted file mode 100644 index 0220c30..0000000 --- a/libFuzzer/Fuzzer/test/SimpleCmpTest.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find several narrow ranges. -#include -#include -#include -#include - -extern int AllLines[]; - -bool PrintOnce(int Line) { - if (!AllLines[Line]) - fprintf(stderr, "Seen line %d\n", Line); - AllLines[Line] = 1; - return true; -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size != 22) return 0; - uint64_t x = 0; - int64_t y = 0; - int32_t z = 0; - uint16_t a = 0; - memcpy(&x, Data, 8); // 8 - memcpy(&y, Data + 8, 8); // 16 - memcpy(&z, Data + 16, sizeof(z)); // 20 - memcpy(&a, Data + 20, sizeof(a)); // 22 - - if (x > 1234567890 && PrintOnce(__LINE__) && - x < 1234567895 && PrintOnce(__LINE__) && - a == 0x4242 && PrintOnce(__LINE__) && - y >= 987654321 && PrintOnce(__LINE__) && - y <= 987654325 && PrintOnce(__LINE__) && - z < -10000 && PrintOnce(__LINE__) && - z >= -10005 && PrintOnce(__LINE__) && - z != -10003 && PrintOnce(__LINE__) && - true) { - fprintf(stderr, "BINGO; Found the target: size %zd (%zd, %zd, %d, %d), exiting.\n", - Size, x, y, z, a); - exit(1); - } - return 0; -} - -int AllLines[__LINE__ + 1]; // Must be the last line. diff --git a/libFuzzer/Fuzzer/test/SimpleDictionaryTest.cpp b/libFuzzer/Fuzzer/test/SimpleDictionaryTest.cpp deleted file mode 100644 index cd7292b..0000000 --- a/libFuzzer/Fuzzer/test/SimpleDictionaryTest.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. -// The fuzzer must find a string based on dictionary words: -// "Elvis" -// "Presley" -#include -#include -#include -#include -#include - -static volatile int Zero = 0; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - const char *Expected = "ElvisPresley"; - if (Size < strlen(Expected)) return 0; - size_t Match = 0; - for (size_t i = 0; Expected[i]; i++) - if (Expected[i] + Zero == Data[i]) - Match++; - if (Match == strlen(Expected)) { - std::cout << "BINGO; Found the target, exiting\n"; - exit(1); - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/SimpleFnAdapterTest.cpp b/libFuzzer/Fuzzer/test/SimpleFnAdapterTest.cpp deleted file mode 100644 index d30c98b..0000000 --- a/libFuzzer/Fuzzer/test/SimpleFnAdapterTest.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer Fn adapter. The fuzzer has to find two non-empty -// vectors with the same content. - -#include -#include - -#include "FuzzerFnAdapter.h" - -static void TestFn(std::vector V1, std::vector V2) { - if (V1.size() > 0 && V1 == V2) { - std::cout << "BINGO; Found the target, exiting\n"; - exit(0); - } -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - fuzzer::Adapt(TestFn, Data, Size); - return 0; -} - - diff --git a/libFuzzer/Fuzzer/test/SimpleHashTest.cpp b/libFuzzer/Fuzzer/test/SimpleHashTest.cpp deleted file mode 100644 index 00599de..0000000 --- a/libFuzzer/Fuzzer/test/SimpleHashTest.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// This test computes a checksum of the data (all but the last 4 bytes), -// and then compares the last 4 bytes with the computed value. -// A fuzzer with cmp traces is expected to defeat this check. -#include -#include -#include -#include - -// A modified jenkins_one_at_a_time_hash initialized by non-zero, -// so that simple_hash(0) != 0. See also -// https://en.wikipedia.org/wiki/Jenkins_hash_function -static uint32_t simple_hash(const uint8_t *Data, size_t Size) { - uint32_t Hash = 0x12039854; - for (uint32_t i = 0; i < Size; i++) { - Hash += Data[i]; - Hash += (Hash << 10); - Hash ^= (Hash >> 6); - } - Hash += (Hash << 3); - Hash ^= (Hash >> 11); - Hash += (Hash << 15); - return Hash; -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size < 14) - return 0; - - uint32_t Hash = simple_hash(&Data[0], Size - 4); - uint32_t Want = reinterpret_cast(&Data[Size - 4])[0]; - if (Hash != Want) - return 0; - fprintf(stderr, "BINGO; simple_hash defeated: %x == %x\n", (unsigned int)Hash, - (unsigned int)Want); - exit(1); - return 0; -} diff --git a/libFuzzer/Fuzzer/test/SimpleTest.cpp b/libFuzzer/Fuzzer/test/SimpleTest.cpp deleted file mode 100644 index e53ea16..0000000 --- a/libFuzzer/Fuzzer/test/SimpleTest.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find the string "Hi!". -#include -#include -#include -#include -#include - -static volatile int Sink; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - assert(Data); - if (Size > 0 && Data[0] == 'H') { - Sink = 1; - if (Size > 1 && Data[1] == 'i') { - Sink = 2; - if (Size > 2 && Data[2] == '!') { - std::cout << "BINGO; Found the target, exiting\n"; - exit(0); - } - } - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/SimpleThreadedTest.cpp b/libFuzzer/Fuzzer/test/SimpleThreadedTest.cpp deleted file mode 100644 index 5f02d3f..0000000 --- a/libFuzzer/Fuzzer/test/SimpleThreadedTest.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Threaded test for a fuzzer. The fuzzer should find "H" -#include -#include -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - auto C = [&] { - if (Size >= 2 && Data[0] == 'H') { - std::cout << "BINGO; Found the target, exiting\n"; - abort(); - } - }; - std::thread T[] = {std::thread(C), std::thread(C), std::thread(C), - std::thread(C), std::thread(C), std::thread(C)}; - for (auto &X : T) - X.join(); - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/SingleByteInputTest.cpp b/libFuzzer/Fuzzer/test/SingleByteInputTest.cpp deleted file mode 100644 index 4ce819d..0000000 --- a/libFuzzer/Fuzzer/test/SingleByteInputTest.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer, need just one byte to crash. -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size > 0 && Data[Size/2] == 42) { - fprintf(stderr, "BINGO\n"); - abort(); - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/SingleMemcmpTest.cpp b/libFuzzer/Fuzzer/test/SingleMemcmpTest.cpp deleted file mode 100644 index c73f68a..0000000 --- a/libFuzzer/Fuzzer/test/SingleMemcmpTest.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find a particular string. -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - char *S = (char*)Data; - if (Size >= 6 && !memcmp(S, "qwerty", 6)) { - fprintf(stderr, "BINGO\n"); - exit(1); - } - return 0; -} diff --git a/libFuzzer/Fuzzer/test/SingleStrcmpTest.cpp b/libFuzzer/Fuzzer/test/SingleStrcmpTest.cpp deleted file mode 100644 index 48f481d..0000000 --- a/libFuzzer/Fuzzer/test/SingleStrcmpTest.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find a particular string. -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size >= 7) { - char Copy[7]; - memcpy(Copy, Data, 6); - Copy[6] = 0; - if (!strcmp(Copy, "qwerty")) { - fprintf(stderr, "BINGO\n"); - exit(1); - } - } - return 0; -} diff --git a/libFuzzer/Fuzzer/test/SingleStrncmpTest.cpp b/libFuzzer/Fuzzer/test/SingleStrncmpTest.cpp deleted file mode 100644 index dbcc464..0000000 --- a/libFuzzer/Fuzzer/test/SingleStrncmpTest.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find a particular string. -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - char *S = (char*)Data; - if (Size >= 6 && !strncmp(S, "qwerty", 6)) { - fprintf(stderr, "BINGO\n"); - exit(1); - } - return 0; -} diff --git a/libFuzzer/Fuzzer/test/SpamyTest.cpp b/libFuzzer/Fuzzer/test/SpamyTest.cpp deleted file mode 100644 index d294d4d..0000000 --- a/libFuzzer/Fuzzer/test/SpamyTest.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// The test spams to stderr and stdout. -#include -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - assert(Data); - printf("PRINTF_STDOUT\n"); - fflush(stdout); - fprintf(stderr, "PRINTF_STDERR\n"); - std::cout << "STREAM_COUT\n"; - std::cout.flush(); - std::cerr << "STREAM_CERR\n"; - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/StrcmpTest.cpp b/libFuzzer/Fuzzer/test/StrcmpTest.cpp deleted file mode 100644 index cd91dda..0000000 --- a/libFuzzer/Fuzzer/test/StrcmpTest.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Break through a series of strcmp. -#include -#include -#include -#include -#include - -bool Eq(const uint8_t *Data, size_t Size, const char *Str) { - char Buff[1024]; - size_t Len = strlen(Str); - if (Size < Len) return false; - if (Len >= sizeof(Buff)) return false; - memcpy(Buff, (char*)Data, Len); - Buff[Len] = 0; - int res = strcmp(Buff, Str); - return res == 0; -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Eq(Data, Size, "ABC") && - Size >= 3 && Eq(Data + 3, Size - 3, "QWER") && - Size >= 7 && Eq(Data + 7, Size - 7, "ZXCVN") && - Size >= 14 && Data[13] == 42 - ) { - fprintf(stderr, "BINGO\n"); - exit(1); - } - return 0; -} diff --git a/libFuzzer/Fuzzer/test/StrncmpOOBTest.cpp b/libFuzzer/Fuzzer/test/StrncmpOOBTest.cpp deleted file mode 100644 index f70b003..0000000 --- a/libFuzzer/Fuzzer/test/StrncmpOOBTest.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Test that libFuzzer itself does not read out of bounds. -#include -#include -#include -#include -#include -#include - -static volatile int Sink; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size < 5) return 0; - const char *Ch = reinterpret_cast(Data); - if (Ch[Size - 3] == 'a') - Sink = strncmp(Ch + Size - 3, "abcdefg", 6); - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/StrncmpTest.cpp b/libFuzzer/Fuzzer/test/StrncmpTest.cpp deleted file mode 100644 index 5ffd011..0000000 --- a/libFuzzer/Fuzzer/test/StrncmpTest.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find a particular string. -#include -#include -#include -#include - -static volatile int sink; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - // TODO: check other sizes. - char *S = (char*)Data; - if (Size >= 8 && strncmp(S, "123", 8)) - sink = 1; - if (Size >= 8 && strncmp(S, "01234567", 8) == 0) { - if (Size >= 12 && strncmp(S + 8, "ABCD", 4) == 0) { - if (Size >= 14 && strncmp(S + 12, "XY", 2) == 0) { - if (Size >= 17 && strncmp(S + 14, "KLM", 3) == 0) { - fprintf(stderr, "BINGO\n"); - exit(1); - } - } - } - } - return 0; -} diff --git a/libFuzzer/Fuzzer/test/StrstrTest.cpp b/libFuzzer/Fuzzer/test/StrstrTest.cpp deleted file mode 100644 index f021e75..0000000 --- a/libFuzzer/Fuzzer/test/StrstrTest.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Test strstr and strcasestr hooks. -#include -#include -#include -#include -#include - -// Windows does not have strcasestr and memmem, so we are not testing them. -#ifdef _WIN32 -#define strcasestr strstr -#define memmem(a, b, c, d) true -#endif - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size < 4) return 0; - std::string s(reinterpret_cast(Data), Size); - if (strstr(s.c_str(), "FUZZ") && - strcasestr(s.c_str(), "aBcD") && - memmem(s.data(), s.size(), "kuku", 4) - ) { - fprintf(stderr, "BINGO\n"); - exit(1); - } - return 0; -} diff --git a/libFuzzer/Fuzzer/test/SwapCmpTest.cpp b/libFuzzer/Fuzzer/test/SwapCmpTest.cpp deleted file mode 100644 index f79db4c..0000000 --- a/libFuzzer/Fuzzer/test/SwapCmpTest.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// The fuzzer must find several constants with swapped bytes. -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size < 14) return 0; - uint64_t x = 0; - uint32_t y = 0; - uint16_t z = 0; - memcpy(&x, Data, sizeof(x)); - memcpy(&y, Data + Size / 2, sizeof(y)); - memcpy(&z, Data + Size - sizeof(z), sizeof(z)); - - x = __builtin_bswap64(x); - y = __builtin_bswap32(y); - z = __builtin_bswap16(z); - - if (x == 0x46555A5A5A5A5546ULL && - z == 0x4F4B && - y == 0x66757A7A && - true - ) { - if (Data[Size - 3] == 'z') { - fprintf(stderr, "BINGO; Found the target\n"); - exit(1); - } - } - return 0; -} diff --git a/libFuzzer/Fuzzer/test/Switch2Test.cpp b/libFuzzer/Fuzzer/test/Switch2Test.cpp deleted file mode 100644 index 3c6a300..0000000 --- a/libFuzzer/Fuzzer/test/Switch2Test.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find the interesting switch value. -#include -#include -#include -#include -#include - -int Switch(int a) { - switch(a) { - case 100001: return 1; - case 100002: return 2; - case 100003: return 4; - } - return 0; -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - const int N = 3; - if (Size < N * sizeof(int)) return 0; - int Res = 0; - for (int i = 0; i < N; i++) { - int X; - memcpy(&X, Data + i * sizeof(int), sizeof(int)); - Res += Switch(X); - } - if (Res == 5 || Res == 3 || Res == 6 || Res == 7) { - fprintf(stderr, "BINGO; Found the target, exiting; Res=%d\n", Res); - exit(1); - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/SwitchTest.cpp b/libFuzzer/Fuzzer/test/SwitchTest.cpp deleted file mode 100644 index 3dc051f..0000000 --- a/libFuzzer/Fuzzer/test/SwitchTest.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find the interesting switch value. -#include -#include -#include -#include -#include - -static volatile int Sink; - -template -bool Switch(const uint8_t *Data, size_t Size) { - T X; - if (Size < sizeof(X)) return false; - memcpy(&X, Data, sizeof(X)); - switch (X) { - case 1: Sink = __LINE__; break; - case 101: Sink = __LINE__; break; - case 1001: Sink = __LINE__; break; - case 10001: Sink = __LINE__; break; - case 100001: Sink = __LINE__; break; - case 1000001: Sink = __LINE__; break; - case 10000001: Sink = __LINE__; break; - case 100000001: return true; - } - return false; -} - -bool ShortSwitch(const uint8_t *Data, size_t Size) { - short X; - if (Size < sizeof(short)) return false; - memcpy(&X, Data, sizeof(short)); - switch(X) { - case 42: Sink = __LINE__; break; - case 402: Sink = __LINE__; break; - case 4002: Sink = __LINE__; break; - case 5002: Sink = __LINE__; break; - case 7002: Sink = __LINE__; break; - case 9002: Sink = __LINE__; break; - case 14002: Sink = __LINE__; break; - case 21402: return true; - } - return false; -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size >= 4 && Switch(Data, Size) && - Size >= 12 && Switch(Data + 4, Size - 4) && - Size >= 14 && ShortSwitch(Data + 12, 2) - ) { - fprintf(stderr, "BINGO; Found the target, exiting\n"); - exit(1); - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/ThreadedLeakTest.cpp b/libFuzzer/Fuzzer/test/ThreadedLeakTest.cpp deleted file mode 100644 index 7511071..0000000 --- a/libFuzzer/Fuzzer/test/ThreadedLeakTest.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// The fuzzer should find a leak in a non-main thread. -#include -#include -#include - -static volatile int *Sink; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size == 0) return 0; - if (Data[0] != 'F') return 0; - std::thread T([&] { Sink = new int; }); - T.join(); - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/ThreadedTest.cpp b/libFuzzer/Fuzzer/test/ThreadedTest.cpp deleted file mode 100644 index 09137a9..0000000 --- a/libFuzzer/Fuzzer/test/ThreadedTest.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Threaded test for a fuzzer. The fuzzer should not crash. -#include -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size < 8) return 0; - assert(Data); - auto C = [&] { - size_t Res = 0; - for (size_t i = 0; i < Size / 2; i++) - Res += memcmp(Data, Data + Size / 2, 4); - return Res; - }; - std::thread T[] = {std::thread(C), std::thread(C), std::thread(C), - std::thread(C), std::thread(C), std::thread(C)}; - for (auto &X : T) - X.join(); - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/TimeoutEmptyTest.cpp b/libFuzzer/Fuzzer/test/TimeoutEmptyTest.cpp deleted file mode 100644 index 8066f48..0000000 --- a/libFuzzer/Fuzzer/test/TimeoutEmptyTest.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find the empty string. -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - static volatile int Zero = 0; - if (!Size) - while(!Zero) - ; - return 0; -} diff --git a/libFuzzer/Fuzzer/test/TimeoutTest.cpp b/libFuzzer/Fuzzer/test/TimeoutTest.cpp deleted file mode 100644 index f810701..0000000 --- a/libFuzzer/Fuzzer/test/TimeoutTest.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. The fuzzer must find the string "Hi!". -#include -#include -#include -#include - -static volatile int Sink; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size > 0 && Data[0] == 'H') { - Sink = 1; - if (Size > 1 && Data[1] == 'i') { - Sink = 2; - if (Size > 2 && Data[2] == '!') { - Sink = 2; - while (Sink) - ; - } - } - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/TraceMallocTest.cpp b/libFuzzer/Fuzzer/test/TraceMallocTest.cpp deleted file mode 100644 index 43e6950..0000000 --- a/libFuzzer/Fuzzer/test/TraceMallocTest.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Tests -trace_malloc -#include -#include -#include -#include -#include - -int *Ptr; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (!Size) return 0; - if (*Data == 1) { - delete Ptr; - Ptr = nullptr; - } else if (*Data == 2) { - delete Ptr; - Ptr = new int; - } else if (*Data == 3) { - if (!Ptr) - Ptr = new int; - } - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/TwoDifferentBugsTest.cpp b/libFuzzer/Fuzzer/test/TwoDifferentBugsTest.cpp deleted file mode 100644 index 42c0d19..0000000 --- a/libFuzzer/Fuzzer/test/TwoDifferentBugsTest.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// Simple test for a fuzzer. This test may trigger two different bugs. -#include -#include -#include -#include - -static volatile int *Null = 0; - -void Foo() { Null[1] = 0; } -void Bar() { Null[2] = 0; } - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size < 10 && Data[0] == 'H') - Foo(); - if (Size >= 10 && Data[0] == 'H') - Bar(); - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/UninstrumentedTest.cpp b/libFuzzer/Fuzzer/test/UninstrumentedTest.cpp deleted file mode 100644 index ffe952c..0000000 --- a/libFuzzer/Fuzzer/test/UninstrumentedTest.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. - -// This test should not be instrumented. -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - return 0; -} - diff --git a/libFuzzer/Fuzzer/test/afl-driver-extra-stats.test b/libFuzzer/Fuzzer/test/afl-driver-extra-stats.test deleted file mode 100644 index 1b0818e..0000000 --- a/libFuzzer/Fuzzer/test/afl-driver-extra-stats.test +++ /dev/null @@ -1,30 +0,0 @@ -REQUIRES: posix - -; Test that not specifying an extra stats file isn't broken. -RUN: unset AFL_DRIVER_EXTRA_STATS_FILENAME -RUN: AFLDriverTest - -; Test that specifying an invalid extra stats file causes a crash. -RUN: ASAN_OPTIONS= AFL_DRIVER_EXTRA_STATS_FILENAME=%T not --crash AFLDriverTest - -; Test that specifying a corrupted stats file causes a crash. -echo "peak_rss_mb :0" > %t -ASAN_OPTIONS= AFL_DRIVER_EXTRA_STATS_FILENAME=%t not --crash AFLDriverTest - -; Test that specifying a valid nonexistent stats file works. -RUN: rm -f %t -RUN: AFL_DRIVER_EXTRA_STATS_FILENAME=%t AFLDriverTest -RUN: [[ $(grep "peak_rss_mb\|slowest_unit_time_sec" %t | wc -l) -eq 2 ]] - -; Test that specifying a valid preexisting stats file works. -RUN: printf "peak_rss_mb : 0\nslowest_unit_time_sec: 0\n" > %t -RUN: AFL_DRIVER_EXTRA_STATS_FILENAME=%t AFLDriverTest -; Check that both lines were printed. -RUN: [[ $(grep "peak_rss_mb\|slowest_unit_time_sec" %t | wc -l) -eq 2 ]] - -; Test that peak_rss_mb and slowest_unit_time_in_secs are only updated when necessary. -; Check that both lines have 9999 since there's no way we have exceeded that -; amount of time or virtual memory. -RUN: printf "peak_rss_mb : 9999\nslowest_unit_time_sec: 9999\n" > %t -RUN: AFL_DRIVER_EXTRA_STATS_FILENAME=%t AFLDriverTest -RUN: [[ $(grep "9999" %t | wc -l) -eq 2 ]] diff --git a/libFuzzer/Fuzzer/test/afl-driver-stderr.test b/libFuzzer/Fuzzer/test/afl-driver-stderr.test deleted file mode 100644 index e835acd..0000000 --- a/libFuzzer/Fuzzer/test/afl-driver-stderr.test +++ /dev/null @@ -1,12 +0,0 @@ -REQUIRES: posix - -; Test that not specifying a stderr file isn't broken. -RUN: unset AFL_DRIVER_STDERR_DUPLICATE_FILENAME -RUN: AFLDriverTest - -; Test that specifying an invalid file causes a crash. -RUN: ASAN_OPTIONS= AFL_DRIVER_STDERR_DUPLICATE_FILENAME="%T" not --crash AFLDriverTest - -; Test that a file is created when specified as the duplicate stderr. -RUN: AFL_DRIVER_STDERR_DUPLICATE_FILENAME=%t AFLDriverTest -RUN: stat %t diff --git a/libFuzzer/Fuzzer/test/caller-callee.test b/libFuzzer/Fuzzer/test/caller-callee.test deleted file mode 100644 index 76a951c..0000000 --- a/libFuzzer/Fuzzer/test/caller-callee.test +++ /dev/null @@ -1,2 +0,0 @@ -CHECK: BINGO -RUN: not LLVMFuzzer-CallerCalleeTest -use_value_profile=1 -cross_over=0 -max_len=6 -seed=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/libFuzzer/Fuzzer/test/coverage.test b/libFuzzer/Fuzzer/test/coverage.test deleted file mode 100644 index ff3fdff..0000000 --- a/libFuzzer/Fuzzer/test/coverage.test +++ /dev/null @@ -1,21 +0,0 @@ -XFAIL: darwin - -CHECK: COVERAGE: -CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:13 -CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:14 -CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:16 -CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:19 -CHECK: COVERED_DIRS: {{.*}}lib{{[/\\]}}Fuzzer{{[/\\]}}test -RUN: not LLVMFuzzer-NullDerefTest -print_coverage=1 2>&1 | FileCheck %s - -RUN: LLVMFuzzer-DSOTest -print_coverage=1 -runs=0 2>&1 | FileCheck %s --check-prefix=DSO -DSO: COVERAGE: -DSO-DAG: COVERED:{{.*}}DSO1{{.*}}DSO1.cpp -DSO-DAG: COVERED:{{.*}}DSO2{{.*}}DSO2.cpp -DSO-DAG: COVERED:{{.*}}LLVMFuzzerTestOneInput{{.*}}DSOTestMain -DSO-DAG: UNCOVERED_LINE:{{.*}}DSO1{{.*}}DSO1.cpp -DSO-DAG: UNCOVERED_LINE:{{.*}}DSO2{{.*}}DSO2.cpp -DSO-DAG: UNCOVERED_FUNC: in Uncovered1 -DSO-DAG: UNCOVERED_FUNC: in Uncovered2 -DSO-DAG: UNCOVERED_LINE: in LLVMFuzzerTestOneInput -DSO-DAG: UNCOVERED_FILE:{{.*}}DSOTestExtra.cpp diff --git a/libFuzzer/Fuzzer/test/cxxstring.test b/libFuzzer/Fuzzer/test/cxxstring.test deleted file mode 100644 index c60d7ae..0000000 --- a/libFuzzer/Fuzzer/test/cxxstring.test +++ /dev/null @@ -1,2 +0,0 @@ -RUN: not LLVMFuzzer-CxxStringEqTest -seed=1 -runs=1000000 2>&1 | FileCheck %s -CHECK: BINGO diff --git a/libFuzzer/Fuzzer/test/dict1.txt b/libFuzzer/Fuzzer/test/dict1.txt deleted file mode 100644 index 520d0cc..0000000 --- a/libFuzzer/Fuzzer/test/dict1.txt +++ /dev/null @@ -1,4 +0,0 @@ -# Dictionary for SimpleDictionaryTest - -a="Elvis" -b="Presley" diff --git a/libFuzzer/Fuzzer/test/dump_coverage.test b/libFuzzer/Fuzzer/test/dump_coverage.test deleted file mode 100644 index 8acc830..0000000 --- a/libFuzzer/Fuzzer/test/dump_coverage.test +++ /dev/null @@ -1,14 +0,0 @@ -RUN: rm -rf %t_workdir && mkdir -p %t_workdir -RUN: env ASAN_OPTIONS=coverage_dir='"%t_workdir"' not LLVMFuzzer-NullDerefTest -dump_coverage=1 2>&1 | FileCheck %s -RUN: sancov -covered-functions LLVMFuzzer-NullDerefTest* %t_workdir/*.sancov | FileCheck %s --check-prefix=SANCOV -RUN: env ASAN_OPTIONS=coverage_dir='"%t_workdir"' LLVMFuzzer-DSOTest -dump_coverage=1 -runs=0 2>&1 | FileCheck %s --check-prefix=DSO -RUN: env ASAN_OPTIONS=coverage_dir='"%t_workdir"' not LLVMFuzzer-NullDerefTest -dump_coverage=0 2>&1 | FileCheck %s --check-prefix=NOCOV - -CHECK: SanitizerCoverage: {{.*}}LLVMFuzzer-NullDerefTest.{{.*}}.sancov {{.*}} PCs written -SANCOV: LLVMFuzzerTestOneInput - -DSO: SanitizerCoverage: {{.*}}LLVMFuzzer-DSOTest.{{.*}}.sancov {{.*}} PCs written -DSO-DAG: SanitizerCoverage: {{.*}}LLVMFuzzer-DSO1.{{.*}}.sancov {{.*}} PCs written -DSO-DAG: SanitizerCoverage: {{.*}}LLVMFuzzer-DSO2.{{.*}}.sancov {{.*}} PCs written - -NOCOV-NOT: SanitizerCoverage: {{.*}} PCs written diff --git a/libFuzzer/Fuzzer/test/equivalence.test b/libFuzzer/Fuzzer/test/equivalence.test deleted file mode 100644 index c9b5f69..0000000 --- a/libFuzzer/Fuzzer/test/equivalence.test +++ /dev/null @@ -1,18 +0,0 @@ -REQUIRES: posix - -RUN: LLVMFuzzer-EquivalenceATest -run_equivalence_server=EQUIV_TEST & export APID=$! -RUN: sleep 3 -RUN: not LLVMFuzzer-EquivalenceBTest -use_equivalence_server=EQUIV_TEST -max_len=4096 2>&1 | FileCheck %s -CHECK: ERROR: libFuzzer: equivalence-mismatch. Sizes: {{.*}}; offset 2 -CHECK: SUMMARY: libFuzzer: equivalence-mismatch -RUN: kill -9 $APID - - -# Run EquivalenceATest against itself with a small timeout -# to stress the signal handling and ensure that shmem doesn't mind -# the signals. - -RUN: LLVMFuzzer-EquivalenceATest -timeout=1 -run_equivalence_server=EQUIV_TEST & export APID=$! -RUN: sleep 3 -RUN: LLVMFuzzer-EquivalenceATest -timeout=1 -use_equivalence_server=EQUIV_TEST -runs=500000 2>&1 -RUN: kill -9 $APID diff --git a/libFuzzer/Fuzzer/test/fuzzer-customcrossover.test b/libFuzzer/Fuzzer/test/fuzzer-customcrossover.test deleted file mode 100644 index 28d39ce..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-customcrossover.test +++ /dev/null @@ -1,10 +0,0 @@ -RUN: rm -rf %t/CustomCrossover -RUN: mkdir -p %t/CustomCrossover -RUN: echo "0123456789" > %t/CustomCrossover/digits -RUN: echo "abcdefghij" > %t/CustomCrossover/chars -RUN: not LLVMFuzzer-CustomCrossOverTest -seed=1 -use_memcmp=0 -runs=100000 %t/CustomCrossover 2>&1 | FileCheck %s --check-prefix=LLVMFuzzerCustomCrossover -RUN: rm -rf %t/CustomCrossover - -LLVMFuzzerCustomCrossover: In LLVMFuzzerCustomCrossover -LLVMFuzzerCustomCrossover: BINGO - diff --git a/libFuzzer/Fuzzer/test/fuzzer-customcrossoverandmutate.test b/libFuzzer/Fuzzer/test/fuzzer-customcrossoverandmutate.test deleted file mode 100644 index 218019d..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-customcrossoverandmutate.test +++ /dev/null @@ -1 +0,0 @@ -RUN: LLVMFuzzer-CustomCrossOverAndMutateTest -seed=1 -use_memcmp=0 -runs=100000 diff --git a/libFuzzer/Fuzzer/test/fuzzer-custommutator.test b/libFuzzer/Fuzzer/test/fuzzer-custommutator.test deleted file mode 100644 index fcd740b..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-custommutator.test +++ /dev/null @@ -1,4 +0,0 @@ -RUN: not LLVMFuzzer-CustomMutatorTest 2>&1 | FileCheck %s --check-prefix=LLVMFuzzerCustomMutator -LLVMFuzzerCustomMutator: In LLVMFuzzerCustomMutator -LLVMFuzzerCustomMutator: BINGO - diff --git a/libFuzzer/Fuzzer/test/fuzzer-dict.test b/libFuzzer/Fuzzer/test/fuzzer-dict.test deleted file mode 100644 index dec002f..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-dict.test +++ /dev/null @@ -1,6 +0,0 @@ -CHECK: BINGO -Done1000000: Done 1000000 runs in - -RUN: not LLVMFuzzer-SimpleDictionaryTest -dict=%S/dict1.txt -seed=1 -runs=1000003 2>&1 | FileCheck %s -RUN: LLVMFuzzer-SimpleDictionaryTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 - diff --git a/libFuzzer/Fuzzer/test/fuzzer-dirs.test b/libFuzzer/Fuzzer/test/fuzzer-dirs.test deleted file mode 100644 index 3de64f2..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-dirs.test +++ /dev/null @@ -1,15 +0,0 @@ -RUN: rm -rf %t/SUB1 -RUN: mkdir -p %t/SUB1/SUB2/SUB3 -RUN: echo a > %t/SUB1/a -RUN: echo b > %t/SUB1/SUB2/b -RUN: echo c > %t/SUB1/SUB2/SUB3/c -RUN: LLVMFuzzer-SimpleTest %t/SUB1 -runs=0 2>&1 | FileCheck %s --check-prefix=SUBDIRS -SUBDIRS: READ units: 3 -RUN: echo -n zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz > %t/SUB1/long -RUN: LLVMFuzzer-SimpleTest %t/SUB1 -runs=0 2>&1 | FileCheck %s --check-prefix=LONG -LONG: INFO: -max_len is not provided, using 93 -RUN: rm -rf %t/SUB1 - -RUN: not LLVMFuzzer-SimpleTest NONEXISTENT_DIR 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR -NONEXISTENT_DIR: No such directory: NONEXISTENT_DIR; exiting - diff --git a/libFuzzer/Fuzzer/test/fuzzer-fdmask.test b/libFuzzer/Fuzzer/test/fuzzer-fdmask.test deleted file mode 100644 index abbc4bd..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-fdmask.test +++ /dev/null @@ -1,30 +0,0 @@ -RUN: LLVMFuzzer-SpamyTest -runs=1 2>&1 | FileCheck %s --check-prefix=FD_MASK_0 -RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=0 2>&1 | FileCheck %s --check-prefix=FD_MASK_0 -RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=1 2>&1 | FileCheck %s --check-prefix=FD_MASK_1 -RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=2 2>&1 | FileCheck %s --check-prefix=FD_MASK_2 -RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=3 2>&1 | FileCheck %s --check-prefix=FD_MASK_3 - -FD_MASK_0: PRINTF_STDOUT -FD_MASK_0: PRINTF_STDERR -FD_MASK_0: STREAM_COUT -FD_MASK_0: STREAM_CERR -FD_MASK_0: INITED - -FD_MASK_1-NOT: PRINTF_STDOUT -FD_MASK_1: PRINTF_STDERR -FD_MASK_1-NOT: STREAM_COUT -FD_MASK_1: STREAM_CERR -FD_MASK_1: INITED - -FD_MASK_2: PRINTF_STDOUT -FD_MASK_2-NOT: PRINTF_STDERR -FD_MASK_2: STREAM_COUT -FD_MASK_2-NOTE: STREAM_CERR -FD_MASK_2: INITED - -FD_MASK_3-NOT: PRINTF_STDOUT -FD_MASK_3-NOT: PRINTF_STDERR -FD_MASK_3-NOT: STREAM_COUT -FD_MASK_3-NOT: STREAM_CERR -FD_MASK_3: INITED - diff --git a/libFuzzer/Fuzzer/test/fuzzer-finalstats.test b/libFuzzer/Fuzzer/test/fuzzer-finalstats.test deleted file mode 100644 index 1cbcd10..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-finalstats.test +++ /dev/null @@ -1,11 +0,0 @@ -RUN: LLVMFuzzer-SimpleTest -seed=1 -runs=77 -print_final_stats=1 2>&1 | FileCheck %s --check-prefix=FINAL_STATS -FINAL_STATS: stat::number_of_executed_units: 77 -FINAL_STATS: stat::average_exec_per_sec: 0 -FINAL_STATS: stat::new_units_added: -FINAL_STATS: stat::slowest_unit_time_sec: 0 -FINAL_STATS: stat::peak_rss_mb: - -RUN: LLVMFuzzer-SimpleTest %S/dict1.txt -runs=33 -print_final_stats=1 2>&1 | FileCheck %s --check-prefix=FINAL_STATS1 -FINAL_STATS1: stat::number_of_executed_units: 33 -FINAL_STATS1: stat::peak_rss_mb: - diff --git a/libFuzzer/Fuzzer/test/fuzzer-flags.test b/libFuzzer/Fuzzer/test/fuzzer-flags.test deleted file mode 100644 index 76ea277..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-flags.test +++ /dev/null @@ -1,10 +0,0 @@ -RUN: LLVMFuzzer-SimpleTest -foo_bar=1 2>&1 | FileCheck %s --check-prefix=FOO_BAR -FOO_BAR: WARNING: unrecognized flag '-foo_bar=1'; use -help=1 to list all flags -FOO_BAR: BINGO - -RUN: LLVMFuzzer-SimpleTest -runs=10 --max_len=100 2>&1 | FileCheck %s --check-prefix=DASH_DASH -DASH_DASH: WARNING: did you mean '-max_len=100' (single dash)? -DASH_DASH: INFO: A corpus is not provided, starting from an empty corpus - -RUN: LLVMFuzzer-SimpleTest -help=1 2>&1 | FileCheck %s --check-prefix=NO_INTERNAL -NO_INTERNAL-NOT: internal flag diff --git a/libFuzzer/Fuzzer/test/fuzzer-fn-adapter.test b/libFuzzer/Fuzzer/test/fuzzer-fn-adapter.test deleted file mode 100644 index 0ea96f3..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-fn-adapter.test +++ /dev/null @@ -1,3 +0,0 @@ -RUN: LLVMFuzzer-SimpleFnAdapterTest 2>&1 | FileCheck %s - -CHECK: BINGO diff --git a/libFuzzer/Fuzzer/test/fuzzer-jobs.test b/libFuzzer/Fuzzer/test/fuzzer-jobs.test deleted file mode 100644 index 5bf8cfa..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-jobs.test +++ /dev/null @@ -1,29 +0,0 @@ -RUN: rm -rf %tmp -RUN: mkdir %tmp && cd %tmp -# Create a shared corpus directory -RUN: rm -rf FuzzerJobsTestCORPUS -RUN: mkdir FuzzerJobsTestCORPUS -RUN: rm -f fuzz-{0,1}.log -# Start fuzzer and in parallel check that the output files -# that should be created exist. -RUN: LLVMFuzzer-EmptyTest -max_total_time=4 -jobs=2 -workers=2 FuzzerJobsTestCORPUS > %t-fuzzer-jobs-test.log 2>&1 & export FUZZER_PID=$! -# Wait a short while to give time for the child processes -# to start fuzzing -RUN: sleep 2 -# If the instances are running in parallel they should have created their log -# files by now. -RUN: ls fuzz-0.log -RUN: ls fuzz-1.log -# Wait for libfuzzer to finish. -# This probably isn't portable but we need a way to block until -# the fuzzer is done otherwise we might remove the files while -# they are being used. -RUN: while kill -0 ${FUZZER_PID}; do : ; done -RUN: rm -f fuzz-{0,1}.log -RUN: rm -rf FuzzerJobsTestCORPUS -RUN: FileCheck -input-file=%t-fuzzer-jobs-test.log %s -RUN: rm %t-fuzzer-jobs-test.log -RUN: cd ../ - -CHECK-DAG: Job 0 exited with exit code 0 -CHECK-DAG: Job 1 exited with exit code 0 diff --git a/libFuzzer/Fuzzer/test/fuzzer-leak.test b/libFuzzer/Fuzzer/test/fuzzer-leak.test deleted file mode 100644 index 9cf5c74..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-leak.test +++ /dev/null @@ -1,35 +0,0 @@ -REQUIRES: lsan -RUN: not LLVMFuzzer-LeakTest -runs=100000 -detect_leaks=1 2>&1 | FileCheck %s --check-prefix=LEAK_DURING -LEAK_DURING: ERROR: LeakSanitizer: detected memory leaks -LEAK_DURING: Direct leak of 4 byte(s) in 1 object(s) allocated from: -LEAK_DURING: INFO: to ignore leaks on libFuzzer side use -detect_leaks=0 -LEAK_DURING: Test unit written to ./leak- -LEAK_DURING-NOT: DONE -LEAK_DURING-NOT: Done - -RUN: not LLVMFuzzer-LeakTest -runs=0 -detect_leaks=1 %S 2>&1 | FileCheck %s --check-prefix=LEAK_IN_CORPUS -LEAK_IN_CORPUS: ERROR: LeakSanitizer: detected memory leaks -LEAK_IN_CORPUS: INFO: a leak has been found in the initial corpus. - -RUN: not LLVMFuzzer-LeakTest -runs=100000000 %S/hi.txt 2>&1 | FileCheck %s --check-prefix=MULTI_RUN_LEAK -MULTI_RUN_LEAK-NOT: pulse -MULTI_RUN_LEAK: LeakSanitizer: detected memory leaks - -RUN: not LLVMFuzzer-LeakTest -runs=100000 -detect_leaks=0 2>&1 | FileCheck %s --check-prefix=LEAK_AFTER -RUN: not LLVMFuzzer-LeakTest -runs=100000 2>&1 | FileCheck %s --check-prefix=LEAK_DURING -RUN: not LLVMFuzzer-ThreadedLeakTest -runs=100000 -detect_leaks=0 2>&1 | FileCheck %s --check-prefix=LEAK_AFTER -RUN: not LLVMFuzzer-ThreadedLeakTest -runs=100000 2>&1 | FileCheck %s --check-prefix=LEAK_DURING -LEAK_AFTER: Done 100000 runs in -LEAK_AFTER: ERROR: LeakSanitizer: detected memory leaks - -RUN: not LLVMFuzzer-LeakTest -runs=100000 -max_len=1 2>&1 | FileCheck %s --check-prefix=MAX_LEN_1 -MAX_LEN_1: Test unit written to ./leak-7cf184f4c67ad58283ecb19349720b0cae756829 - -RUN: not LLVMFuzzer-LeakTimeoutTest -timeout=1 2>&1 | FileCheck %s --check-prefix=LEAK_TIMEOUT -LEAK_TIMEOUT: ERROR: libFuzzer: timeout after -LEAK_TIMEOUT-NOT: LeakSanitizer - -RUN: LLVMFuzzer-AccumulateAllocationsTest -detect_leaks=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=ACCUMULATE_ALLOCS -ACCUMULATE_ALLOCS: INFO: libFuzzer disabled leak detection after every mutation - -RUN: LLVMFuzzer-LeakTest -error_exitcode=0 diff --git a/libFuzzer/Fuzzer/test/fuzzer-oom-with-profile.test b/libFuzzer/Fuzzer/test/fuzzer-oom-with-profile.test deleted file mode 100644 index 2b2b0b9..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-oom-with-profile.test +++ /dev/null @@ -1,6 +0,0 @@ -REQUIRES: linux -RUN: not LLVMFuzzer-OutOfMemoryTest -rss_limit_mb=300 2>&1 | FileCheck %s -CHECK: ERROR: libFuzzer: out-of-memory (used: {{.*}}; limit: 300Mb) -CHECK: Live Heap Allocations -CHECK: Test unit written to ./oom- -SUMMARY: libFuzzer: out-of-memory diff --git a/libFuzzer/Fuzzer/test/fuzzer-oom.test b/libFuzzer/Fuzzer/test/fuzzer-oom.test deleted file mode 100644 index 60da701..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-oom.test +++ /dev/null @@ -1,13 +0,0 @@ -RUN: not LLVMFuzzer-OutOfMemoryTest -rss_limit_mb=300 2>&1 | FileCheck %s -XFAIL: darwin - -CHECK: ERROR: libFuzzer: out-of-memory (used: {{.*}}; limit: 300Mb) -CHECK: Test unit written to ./oom- -SUMMARY: libFuzzer: out-of-memory - -RUN: not LLVMFuzzer-OutOfMemorySingleLargeMallocTest -rss_limit_mb=300 2>&1 | FileCheck %s --check-prefix=SINGLE_LARGE_MALLOC -SINGLE_LARGE_MALLOC: libFuzzer: out-of-memory (malloc(53{{.*}})) -SINGLE_LARGE_MALLOC: in LLVMFuzzerTestOneInput - -# Check that -rss_limit_mb=0 means no limit. -RUN: LLVMFuzzer-AccumulateAllocationsTest -runs=1000 -rss_limit_mb=0 diff --git a/libFuzzer/Fuzzer/test/fuzzer-printcovpcs.test b/libFuzzer/Fuzzer/test/fuzzer-printcovpcs.test deleted file mode 100644 index e4c6f0e..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-printcovpcs.test +++ /dev/null @@ -1,8 +0,0 @@ -RUN: LLVMFuzzer-SimpleTest -print_pcs=1 -seed=1 2>&1 | FileCheck %s --check-prefix=PCS -PCS-NOT: NEW_PC -PCS:INITED -PCS:NEW_PC: {{0x[a-f0-9]+}} -PCS:NEW_PC: {{0x[a-f0-9]+}} -PCS:NEW -PCS:BINGO - diff --git a/libFuzzer/Fuzzer/test/fuzzer-runs.test b/libFuzzer/Fuzzer/test/fuzzer-runs.test deleted file mode 100644 index 056c447..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-runs.test +++ /dev/null @@ -1,8 +0,0 @@ -RUN: mkdir -p %t -RUN: echo abcd > %t/NthRunCrashTest.in -RUN: LLVMFuzzer-NthRunCrashTest %t/NthRunCrashTest.in -RUN: LLVMFuzzer-NthRunCrashTest %t/NthRunCrashTest.in -runs=10 -RUN: not LLVMFuzzer-NthRunCrashTest %t/NthRunCrashTest.in -runs=10000 2>&1 | FileCheck %s -RUN: rm %t/NthRunCrashTest.in -CHECK: BINGO - diff --git a/libFuzzer/Fuzzer/test/fuzzer-seed.test b/libFuzzer/Fuzzer/test/fuzzer-seed.test deleted file mode 100644 index f1bdf9e..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-seed.test +++ /dev/null @@ -1,3 +0,0 @@ -RUN: LLVMFuzzer-SimpleCmpTest -seed=-1 -runs=0 2>&1 | FileCheck %s --check-prefix=CHECK_SEED_MINUS_ONE -CHECK_SEED_MINUS_ONE: Seed: 4294967295 - diff --git a/libFuzzer/Fuzzer/test/fuzzer-segv.test b/libFuzzer/Fuzzer/test/fuzzer-segv.test deleted file mode 100644 index b9a6a5c..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-segv.test +++ /dev/null @@ -1,5 +0,0 @@ -RUN: env ASAN_OPTIONS=handle_segv=0 not LLVMFuzzer-NullDerefTest 2>&1 | FileCheck %s --check-prefix=LIBFUZZER_OWN_SEGV_HANDLER -LIBFUZZER_OWN_SEGV_HANDLER: == ERROR: libFuzzer: deadly signal -LIBFUZZER_OWN_SEGV_HANDLER: SUMMARY: libFuzzer: deadly signal -LIBFUZZER_OWN_SEGV_HANDLER: Test unit written to ./crash- - diff --git a/libFuzzer/Fuzzer/test/fuzzer-singleinputs.test b/libFuzzer/Fuzzer/test/fuzzer-singleinputs.test deleted file mode 100644 index 500e5da..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-singleinputs.test +++ /dev/null @@ -1,16 +0,0 @@ -RUN: not LLVMFuzzer-NullDerefTest %S/hi.txt 2>&1 | FileCheck %s --check-prefix=SingleInput -SingleInput-NOT: Test unit written to ./crash- - -RUN: rm -rf %tmp/SINGLE_INPUTS -RUN: mkdir -p %tmp/SINGLE_INPUTS -RUN: echo aaa > %tmp/SINGLE_INPUTS/aaa -RUN: echo bbb > %tmp/SINGLE_INPUTS/bbb -RUN: LLVMFuzzer-SimpleTest %tmp/SINGLE_INPUTS/aaa %tmp/SINGLE_INPUTS/bbb 2>&1 | FileCheck %s --check-prefix=SINGLE_INPUTS -RUN: LLVMFuzzer-SimpleTest -max_len=2 %tmp/SINGLE_INPUTS/aaa %tmp/SINGLE_INPUTS/bbb 2>&1 | FileCheck %s --check-prefix=SINGLE_INPUTS -RUN: rm -rf %tmp/SINGLE_INPUTS -SINGLE_INPUTS: LLVMFuzzer-SimpleTest{{.*}}: Running 2 inputs 1 time(s) each. -SINGLE_INPUTS: aaa in -SINGLE_INPUTS: bbb in -SINGLE_INPUTS: NOTE: fuzzing was not performed, you have only -SINGLE_INPUTS: executed the target code on a fixed set of inputs. - diff --git a/libFuzzer/Fuzzer/test/fuzzer-threaded.test b/libFuzzer/Fuzzer/test/fuzzer-threaded.test deleted file mode 100644 index c58a334..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-threaded.test +++ /dev/null @@ -1,7 +0,0 @@ -CHECK: Done 1000 runs in - -RUN: LLVMFuzzer-ThreadedTest -use_traces=1 -runs=1000 2>&1 | FileCheck %s -RUN: LLVMFuzzer-ThreadedTest -use_traces=1 -runs=1000 2>&1 | FileCheck %s -RUN: LLVMFuzzer-ThreadedTest -use_traces=1 -runs=1000 2>&1 | FileCheck %s -RUN: LLVMFuzzer-ThreadedTest -use_traces=1 -runs=1000 2>&1 | FileCheck %s - diff --git a/libFuzzer/Fuzzer/test/fuzzer-timeout.test b/libFuzzer/Fuzzer/test/fuzzer-timeout.test deleted file mode 100644 index beb0867..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-timeout.test +++ /dev/null @@ -1,19 +0,0 @@ -RUN: not LLVMFuzzer-TimeoutTest -timeout=1 2>&1 | FileCheck %s --check-prefix=TimeoutTest -TimeoutTest: ALARM: working on the last Unit for -TimeoutTest: Test unit written to ./timeout- -TimeoutTest: == ERROR: libFuzzer: timeout after -TimeoutTest: #0 -TimeoutTest: #1 -TimeoutTest: #2 -TimeoutTest: SUMMARY: libFuzzer: timeout - -RUN: not LLVMFuzzer-TimeoutTest -timeout=1 %S/hi.txt 2>&1 | FileCheck %s --check-prefix=SingleInputTimeoutTest -SingleInputTimeoutTest: ALARM: working on the last Unit for {{[1-3]}} seconds -SingleInputTimeoutTest-NOT: Test unit written to ./timeout- - -RUN: LLVMFuzzer-TimeoutTest -timeout=1 -timeout_exitcode=0 - -RUN: not LLVMFuzzer-TimeoutEmptyTest -timeout=1 2>&1 | FileCheck %s --check-prefix=TimeoutEmptyTest -TimeoutEmptyTest: ALARM: working on the last Unit for -TimeoutEmptyTest: == ERROR: libFuzzer: timeout after -TimeoutEmptyTest: SUMMARY: libFuzzer: timeout diff --git a/libFuzzer/Fuzzer/test/fuzzer-traces-hooks.test b/libFuzzer/Fuzzer/test/fuzzer-traces-hooks.test deleted file mode 100644 index f93a8b7..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-traces-hooks.test +++ /dev/null @@ -1,17 +0,0 @@ -// FIXME: Support for sanitizer hooks for memcmp and strcmp needs to -// be implemented in the sanitizer runtime for this test -UNSUPPORTED: windows -CHECK: BINGO - -RUN: not LLVMFuzzer-MemcmpTest -seed=1 -runs=2000000 2>&1 | FileCheck %s -RUN: not LLVMFuzzer-StrncmpTest -seed=1 -runs=2000000 2>&1 | FileCheck %s -RUN: not LLVMFuzzer-StrcmpTest -seed=1 -runs=2000000 2>&1 | FileCheck %s -RUN: not LLVMFuzzer-StrstrTest -seed=1 -runs=2000000 2>&1 | FileCheck %s - -RUN: not LLVMFuzzer-Memcmp64BytesTest -seed=1 -runs=1000000 2>&1 | FileCheck %s - -RUN: LLVMFuzzer-RepeatedMemcmp -seed=11 -runs=100000 2>&1 | FileCheck %s --check-prefix=RECOMMENDED_DICT -RECOMMENDED_DICT:###### Recommended dictionary. ###### -RECOMMENDED_DICT-DAG: "foo" -RECOMMENDED_DICT-DAG: "bar" -RECOMMENDED_DICT:###### End of recommended dictionary. ###### diff --git a/libFuzzer/Fuzzer/test/fuzzer-ubsan.test b/libFuzzer/Fuzzer/test/fuzzer-ubsan.test deleted file mode 100644 index 0e8ad6c..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer-ubsan.test +++ /dev/null @@ -1,4 +0,0 @@ -RUN: not LLVMFuzzer-SignedIntOverflowTest-Ubsan 2>&1 | FileCheck %s -CHECK: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int' -CHECK: Test unit written to ./crash- - diff --git a/libFuzzer/Fuzzer/test/fuzzer.test b/libFuzzer/Fuzzer/test/fuzzer.test deleted file mode 100644 index ff46d32..0000000 --- a/libFuzzer/Fuzzer/test/fuzzer.test +++ /dev/null @@ -1,60 +0,0 @@ -CHECK: BINGO -Done1000000: Done 1000000 runs in - -RUN: LLVMFuzzer-SimpleTest 2>&1 | FileCheck %s - -# only_ascii mode. Will perform some minimal self-validation. -RUN: LLVMFuzzer-SimpleTest -only_ascii=1 2>&1 - -RUN: LLVMFuzzer-SimpleCmpTest -max_total_time=1 -use_cmp=0 2>&1 | FileCheck %s --check-prefix=MaxTotalTime -MaxTotalTime: Done {{.*}} runs in {{.}} second(s) - -RUN: not LLVMFuzzer-NullDerefTest 2>&1 | FileCheck %s --check-prefix=NullDerefTest -RUN: not LLVMFuzzer-NullDerefTest -close_fd_mask=3 2>&1 | FileCheck %s --check-prefix=NullDerefTest -NullDerefTest: ERROR: AddressSanitizer: {{SEGV|access-violation}} on unknown address -NullDerefTest: Test unit written to ./crash- -RUN: not LLVMFuzzer-NullDerefTest -artifact_prefix=ZZZ 2>&1 | FileCheck %s --check-prefix=NullDerefTestPrefix -NullDerefTestPrefix: Test unit written to ZZZcrash- -RUN: not LLVMFuzzer-NullDerefTest -artifact_prefix=ZZZ -exact_artifact_path=FOOBAR 2>&1 | FileCheck %s --check-prefix=NullDerefTestExactPath -NullDerefTestExactPath: Test unit written to FOOBAR - -RUN: not LLVMFuzzer-NullDerefOnEmptyTest -print_final_stats=1 2>&1 | FileCheck %s --check-prefix=NULL_DEREF_ON_EMPTY -NULL_DEREF_ON_EMPTY: stat::number_of_executed_units: - -#not LLVMFuzzer-FullCoverageSetTest -timeout=15 -seed=1 -mutate_depth=2 -use_full_coverage_set=1 2>&1 | FileCheck %s - -RUN: not LLVMFuzzer-CounterTest -max_len=6 -seed=1 -timeout=15 2>&1 | FileCheck %s --check-prefix=COUNTERS - -COUNTERS: INITED {{.*}} {{bits:|ft:}} -COUNTERS: NEW {{.*}} {{bits:|ft:}} {{[1-9]*}} -COUNTERS: NEW {{.*}} {{bits:|ft:}} {{[1-9]*}} -COUNTERS: BINGO - -# Don't run UninstrumentedTest for now since we build libFuzzer itself with asan. -DISABLED: not LLVMFuzzer-UninstrumentedTest-Uninstrumented 2>&1 | FileCheck %s --check-prefix=UNINSTRUMENTED -UNINSTRUMENTED: ERROR: __sanitizer_set_death_callback is not defined. Exiting. - -RUN: not LLVMFuzzer-NotinstrumentedTest-NoCoverage 2>&1 | FileCheck %s --check-prefix=NO_COVERAGE -NO_COVERAGE: ERROR: no interesting inputs were found. Is the code instrumented for coverage? Exiting - -RUN: not LLVMFuzzer-BufferOverflowOnInput 2>&1 | FileCheck %s --check-prefix=OOB -OOB: AddressSanitizer: heap-buffer-overflow -OOB: is located 0 bytes to the right of 3-byte region - -RUN: not LLVMFuzzer-InitializeTest -use_value_profile=1 2>&1 | FileCheck %s - -RUN: not LLVMFuzzer-DSOTest 2>&1 | FileCheck %s --check-prefix=DSO -DSO: INFO: Loaded 3 modules -DSO: BINGO - -RUN: LLVMFuzzer-SimpleTest -exit_on_src_pos=SimpleTest.cpp:17 2>&1 | FileCheck %s --check-prefix=EXIT_ON_SRC_POS -RUN: LLVMFuzzer-ShrinkControlFlowTest -exit_on_src_pos=ShrinkControlFlowTest.cpp:23 2>&1 | FileCheck %s --check-prefix=EXIT_ON_SRC_POS -EXIT_ON_SRC_POS: INFO: found line matching '{{.*}}', exiting. - -RUN: env ASAN_OPTIONS=strict_string_checks=1 not LLVMFuzzer-StrncmpOOBTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=STRNCMP -STRNCMP: AddressSanitizer: heap-buffer-overflow -STRNCMP-NOT: __sanitizer_weak_hook_strncmp -STRNCMP: in LLVMFuzzerTestOneInput - -RUN: not LLVMFuzzer-BogusInitializeTest 2>&1 | FileCheck %s --check-prefix=BOGUS_INITIALIZE -BOGUS_INITIALIZE: argv[0] has been modified in LLVMFuzzerInitialize diff --git a/libFuzzer/Fuzzer/test/hi.txt b/libFuzzer/Fuzzer/test/hi.txt deleted file mode 100644 index 2f9031f..0000000 --- a/libFuzzer/Fuzzer/test/hi.txt +++ /dev/null @@ -1 +0,0 @@ -Hi! \ No newline at end of file diff --git a/libFuzzer/Fuzzer/test/lit.cfg b/libFuzzer/Fuzzer/test/lit.cfg deleted file mode 100644 index 85c95b4..0000000 --- a/libFuzzer/Fuzzer/test/lit.cfg +++ /dev/null @@ -1,55 +0,0 @@ -import lit.formats -import sys - -config.name = "LLVMFuzzer" -config.test_format = lit.formats.ShTest(True) -config.suffixes = ['.test'] -config.test_source_root = os.path.dirname(__file__) - -# Choose between lit's internal shell pipeline runner and a real shell. If -# LIT_USE_INTERNAL_SHELL is in the environment, we use that as an override. -use_lit_shell = os.environ.get("LIT_USE_INTERNAL_SHELL") -if use_lit_shell: - # 0 is external, "" is default, and everything else is internal. - execute_external = (use_lit_shell == "0") -else: - # Otherwise we default to internal on Windows and external elsewhere, as - # bash on Windows is usually very slow. - execute_external = (not sys.platform in ['win32']) - -# testFormat: The test format to use to interpret tests. -# -# For now we require '&&' between commands, until they get globally killed and -# the test runner updated. -config.test_format = lit.formats.ShTest(execute_external) - -# Tweak PATH to include llvm tools dir and current exec dir. -llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) -if (not llvm_tools_dir) or (not os.path.exists(llvm_tools_dir)): - lit_config.fatal("Invalid llvm_tools_dir config attribute: %r" % llvm_tools_dir) -path = os.path.pathsep.join((llvm_tools_dir, config.test_exec_root, - config.environment['PATH'])) -config.environment['PATH'] = path - -if config.has_lsan: - lit_config.note('lsan feature available') - config.available_features.add('lsan') -else: - lit_config.note('lsan feature unavailable') - -if sys.platform.startswith('win') or sys.platform.startswith('cygwin'): - config.available_features.add('windows') - -if sys.platform.startswith('darwin'): - config.available_features.add('darwin') - -if config.is_posix: - config.available_features.add('posix') - -if sys.platform.startswith('linux'): - # Note the value of ``sys.platform`` is not consistent - # between python 2 and 3, hence the use of ``.startswith()``. - lit_config.note('linux feature available') - config.available_features.add('linux') -else: - lit_config.note('linux feature unavailable') diff --git a/libFuzzer/Fuzzer/test/lit.site.cfg.in b/libFuzzer/Fuzzer/test/lit.site.cfg.in deleted file mode 100644 index 069f2b7..0000000 --- a/libFuzzer/Fuzzer/test/lit.site.cfg.in +++ /dev/null @@ -1,5 +0,0 @@ -config.test_exec_root = "@CMAKE_CURRENT_BINARY_DIR@" -config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" -config.has_lsan = True if @HAS_LSAN@ == 1 else False -config.is_posix = @LIBFUZZER_POSIX@ -lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") diff --git a/libFuzzer/Fuzzer/test/merge-posix.test b/libFuzzer/Fuzzer/test/merge-posix.test deleted file mode 100644 index 47b90b9..0000000 --- a/libFuzzer/Fuzzer/test/merge-posix.test +++ /dev/null @@ -1,23 +0,0 @@ -REQUIRES: posix - -RUN: rm -rf %tmp/T1 %tmp/T2 -RUN: mkdir -p %tmp/T1 %tmp/T2 - -RUN: echo F..... > %tmp/T1/1 -RUN: echo .U.... > %tmp/T1/2 -RUN: echo ..Z... > %tmp/T1/3 - -RUN: echo .....F > %tmp/T2/1 -RUN: echo ....U. > %tmp/T2/2 -RUN: echo ...Z.. > %tmp/T2/3 -RUN: echo ...Z.. > %tmp/T2/4 -RUN: echo ....E. > %tmp/T2/5 -RUN: echo .....R > %tmp/T2/6 - -# Check that we can report an error if file size exceeded -RUN: (ulimit -f 1; not LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=SIGXFSZ) -SIGXFSZ: ERROR: libFuzzer: file size exceeded - -# Check that we honor TMPDIR -RUN: TMPDIR=DIR_DOES_NOT_EXIST not LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=TMPDIR -TMPDIR: MERGE-OUTER: failed to write to the control file: DIR_DOES_NOT_EXIST/libFuzzerTemp diff --git a/libFuzzer/Fuzzer/test/merge.test b/libFuzzer/Fuzzer/test/merge.test deleted file mode 100644 index e59da8c..0000000 --- a/libFuzzer/Fuzzer/test/merge.test +++ /dev/null @@ -1,53 +0,0 @@ -CHECK: BINGO - -RUN: rm -rf %tmp/T0 %tmp/T1 %tmp/T2 -RUN: mkdir -p %tmp/T0 %tmp/T1 %tmp/T2 -RUN: echo F..... > %tmp/T0/1 -RUN: echo .U.... > %tmp/T0/2 -RUN: echo ..Z... > %tmp/T0/3 - -# T1 has 3 elements, T2 is empty. -RUN: cp %tmp/T0/* %tmp/T1/ -RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=CHECK1 -CHECK1: MERGE-OUTER: 3 files, 3 in the initial corpus -CHECK1: MERGE-OUTER: 0 new files with 0 new features added - -RUN: echo ...Z.. > %tmp/T2/1 -RUN: echo ....E. > %tmp/T2/2 -RUN: echo .....R > %tmp/T2/3 -RUN: echo F..... > %tmp/T2/a -RUN: echo .U.... > %tmp/T2/b -RUN: echo ..Z... > %tmp/T2/c - -# T1 has 3 elements, T2 has 6 elements, only 3 are new. -RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=CHECK2 -CHECK2: MERGE-OUTER: 9 files, 3 in the initial corpus -CHECK2: MERGE-OUTER: 3 new files with 3 new features added - -# Now, T1 has 6 units and T2 has no new interesting units. -RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=CHECK3 -CHECK3: MERGE-OUTER: 12 files, 6 in the initial corpus -CHECK3: MERGE-OUTER: 0 new files with 0 new features added - -# Check that we respect max_len during the merge and don't crash. -RUN: rm %tmp/T1/* -RUN: cp %tmp/T0/* %tmp/T1/ -RUN: echo looooooooong > %tmp/T2/looooooooong -RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 -max_len=6 2>&1 | FileCheck %s --check-prefix=MAX_LEN -MAX_LEN: MERGE-OUTER: 3 new files - -# Check that merge tolerates failures. -RUN: rm %tmp/T1/* -RUN: cp %tmp/T0/* %tmp/T1/ -RUN: echo 'FUZZER' > %tmp/T2/FUZZER -RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=MERGE_WITH_CRASH -MERGE_WITH_CRASH: MERGE-OUTER: succesfull in 2 attempt(s) -MERGE_WITH_CRASH: MERGE-OUTER: 3 new files - -# Check that we actually limit the size with max_len -RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 -max_len=5 2>&1 | FileCheck %s --check-prefix=MERGE_LEN5 -MERGE_LEN5: MERGE-OUTER: succesfull in 1 attempt(s) - -RUN: rm -rf %tmp/T1/* %tmp/T2/* -RUN: not LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=EMPTY -EMPTY: MERGE-OUTER: zero succesfull attempts, exiting diff --git a/libFuzzer/Fuzzer/test/minimize_crash.test b/libFuzzer/Fuzzer/test/minimize_crash.test deleted file mode 100644 index 7d60ea6..0000000 --- a/libFuzzer/Fuzzer/test/minimize_crash.test +++ /dev/null @@ -1,12 +0,0 @@ -RUN: echo 'Hi!rv349f34t3gg' > not_minimal_crash -RUN: LLVMFuzzer-NullDerefTest -minimize_crash=1 not_minimal_crash -max_total_time=2 2>&1 | FileCheck %s -CHECK: CRASH_MIN: failed to minimize beyond ./minimized-from-{{.*}} (3 bytes), exiting -RUN: LLVMFuzzer-NullDerefTest -minimize_crash=1 not_minimal_crash -max_total_time=2 -exact_artifact_path=exact_minimized_path 2>&1 | FileCheck %s --check-prefix=CHECK_EXACT -CHECK_EXACT: CRASH_MIN: failed to minimize beyond exact_minimized_path (3 bytes), exiting -RUN: rm not_minimal_crash minimized-from-* exact_minimized_path - -RUN: echo -n 'abcd*xyz' > not_minimal_crash -RUN: LLVMFuzzer-SingleByteInputTest -minimize_crash=1 not_minimal_crash -artifact_prefix=./ZZZ- -exact_artifact_path=exact_minimized_path 2>&1 | FileCheck %s --check-prefix=MIN1 -MIN1: Test unit written to ./ZZZ-minimized-from- -MIN1: INFO: The input is small enough, exiting -MIN1: CRASH_MIN: failed to minimize beyond exact_minimized_path (1 bytes), exiting diff --git a/libFuzzer/Fuzzer/test/no-coverage/CMakeLists.txt b/libFuzzer/Fuzzer/test/no-coverage/CMakeLists.txt deleted file mode 100644 index 52e7240..0000000 --- a/libFuzzer/Fuzzer/test/no-coverage/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -# These tests are not instrumented with coverage, -# but have coverage rt in the binary. - -set(CMAKE_CXX_FLAGS - "${LIBFUZZER_FLAGS_BASE} -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters,trace-pc-guard") - -set(NoCoverageTests - NotinstrumentedTest - ) - -foreach(Test ${NoCoverageTests}) - add_libfuzzer_test(${Test}-NoCoverage SOURCES ../${Test}.cpp) -endforeach() - - -############################################################################### -# AFL Driver test -############################################################################### -if(NOT MSVC) - add_executable(AFLDriverTest - ../AFLDriverTest.cpp ../../afl/afl_driver.cpp) - - set_target_properties(AFLDriverTest - PROPERTIES RUNTIME_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/lib/Fuzzer/test" - ) - - add_dependencies(TestBinaries AFLDriverTest) -endif() diff --git a/libFuzzer/Fuzzer/test/repeated-bytes.test b/libFuzzer/Fuzzer/test/repeated-bytes.test deleted file mode 100644 index 7139408..0000000 --- a/libFuzzer/Fuzzer/test/repeated-bytes.test +++ /dev/null @@ -1,2 +0,0 @@ -CHECK: BINGO -RUN: LLVMFuzzer-RepeatedBytesTest -seed=1 -runs=1000000 2>&1 | FileCheck %s diff --git a/libFuzzer/Fuzzer/test/shrink.test b/libFuzzer/Fuzzer/test/shrink.test deleted file mode 100644 index edb86cb..0000000 --- a/libFuzzer/Fuzzer/test/shrink.test +++ /dev/null @@ -1,7 +0,0 @@ -RUN: LLVMFuzzer-ShrinkControlFlowTest -seed=1 -exit_on_item=0eb8e4ed029b774d80f2b66408203801cb982a60 -runs=1000000 -shrink=1 2>&1 | FileCheck %s --check-prefix=SHRINK1 -RUN: LLVMFuzzer-ShrinkControlFlowTest -seed=1 -exit_on_item=0eb8e4ed029b774d80f2b66408203801cb982a60 -runs=1000000 -shrink=0 2>&1 | FileCheck %s --check-prefix=SHRINK0 -RUN: LLVMFuzzer-ShrinkValueProfileTest -seed=1 -exit_on_item=aea2e3923af219a8956f626558ef32f30a914ebc -runs=100000 -shrink=1 -use_value_profile=1 2>&1 | FileCheck %s --check-prefix=SHRINK1_VP - -SHRINK0: Done 1000000 runs in -SHRINK1: INFO: found item with checksum '0eb8e4ed029b774d80f2b66408203801cb982a60', exiting. -SHRINK1_VP: INFO: found item with checksum 'aea2e3923af219a8956f626558ef32f30a914ebc', exiting diff --git a/libFuzzer/Fuzzer/test/simple-cmp.test b/libFuzzer/Fuzzer/test/simple-cmp.test deleted file mode 100644 index 145a036..0000000 --- a/libFuzzer/Fuzzer/test/simple-cmp.test +++ /dev/null @@ -1,2 +0,0 @@ -CHECK: BINGO -RUN: not LLVMFuzzer-SimpleCmpTest -seed=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/libFuzzer/Fuzzer/test/standalone.test b/libFuzzer/Fuzzer/test/standalone.test deleted file mode 100644 index 3097b3f..0000000 --- a/libFuzzer/Fuzzer/test/standalone.test +++ /dev/null @@ -1,4 +0,0 @@ -RUN: LLVMFuzzer-StandaloneInitializeTest %S/hi.txt %S/dict1.txt 2>&1 | FileCheck %s -CHECK: StandaloneFuzzTargetMain: running 2 inputs -CHECK: Done: {{.*}}hi.txt: (3 bytes) -CHECK: Done: {{.*}}dict1.txt: (61 bytes) diff --git a/libFuzzer/Fuzzer/test/swap-cmp.test b/libFuzzer/Fuzzer/test/swap-cmp.test deleted file mode 100644 index 908b798..0000000 --- a/libFuzzer/Fuzzer/test/swap-cmp.test +++ /dev/null @@ -1,2 +0,0 @@ -CHECK: BINGO -RUN: not LLVMFuzzer-SwapCmpTest -seed=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/libFuzzer/Fuzzer/test/trace-bb/CMakeLists.txt b/libFuzzer/Fuzzer/test/trace-bb/CMakeLists.txt deleted file mode 100644 index fd168c4..0000000 --- a/libFuzzer/Fuzzer/test/trace-bb/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# These tests are not instrumented with coverage. - -set(CMAKE_CXX_FLAGS - "${LIBFUZZER_FLAGS_BASE} -fsanitize-coverage=edge,trace-bb") - -set(TraceBBTests - SimpleTest - ) - -foreach(Test ${TraceBBTests}) - add_libfuzzer_test(${Test}-TraceBB SOURCES ../${Test}.cpp) -endforeach() - -# Propagate value into parent directory -set(TestBinaries ${TestBinaries} PARENT_SCOPE) diff --git a/libFuzzer/Fuzzer/test/trace-malloc-2.test b/libFuzzer/Fuzzer/test/trace-malloc-2.test deleted file mode 100644 index 7719b65..0000000 --- a/libFuzzer/Fuzzer/test/trace-malloc-2.test +++ /dev/null @@ -1,8 +0,0 @@ -// FIXME: This test infinite loops on darwin because it crashes -// printing a stack trace repeatedly -UNSUPPORTED: darwin - -RUN: LLVMFuzzer-TraceMallocTest -seed=1 -trace_malloc=2 -runs=1000 2>&1 | FileCheck %s --check-prefix=TRACE2 -TRACE2-DAG: FREE[0] -TRACE2-DAG: MALLOC[0] -TRACE2-DAG: in LLVMFuzzerTestOneInput diff --git a/libFuzzer/Fuzzer/test/trace-malloc.test b/libFuzzer/Fuzzer/test/trace-malloc.test deleted file mode 100644 index 25694cc..0000000 --- a/libFuzzer/Fuzzer/test/trace-malloc.test +++ /dev/null @@ -1,5 +0,0 @@ -RUN: LLVMFuzzer-TraceMallocTest -seed=1 -trace_malloc=1 -runs=10000 2>&1 | FileCheck %s -CHECK-DAG: MallocFreeTracer: STOP 0 0 (same) -CHECK-DAG: MallocFreeTracer: STOP 0 1 (DIFFERENT) -CHECK-DAG: MallocFreeTracer: STOP 1 0 (DIFFERENT) -CHECK-DAG: MallocFreeTracer: STOP 1 1 (same) diff --git a/libFuzzer/Fuzzer/test/trace-pc/CMakeLists.txt b/libFuzzer/Fuzzer/test/trace-pc/CMakeLists.txt deleted file mode 100644 index d5caa18..0000000 --- a/libFuzzer/Fuzzer/test/trace-pc/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -# These tests are not instrumented with coverage. - -set(CMAKE_CXX_FLAGS - "${LIBFUZZER_FLAGS_BASE} -fno-sanitize-coverage=8bit-counters -fsanitize-coverage=trace-pc-guard") - -set(TracePCTests - SimpleTest - CounterTest - CallerCalleeTest - NullDerefTest - ShrinkControlFlowTest - ShrinkValueProfileTest - SwitchTest - Switch2Test - FullCoverageSetTest - ) - -foreach(Test ${TracePCTests}) - add_libfuzzer_test(${Test}-TracePC SOURCES ../${Test}.cpp) -endforeach() - -# Propagate value into parent directory -set(TestBinaries ${TestBinaries} PARENT_SCOPE) - -add_library(LLVMFuzzer-DSO1 SHARED ../DSO1.cpp) -add_library(LLVMFuzzer-DSO2 SHARED ../DSO2.cpp) - -add_executable(LLVMFuzzer-DSOTest - ../DSOTestMain.cpp - ../DSOTestExtra.cpp) - -target_link_libraries(LLVMFuzzer-DSOTest - LLVMFuzzer-DSO1 - LLVMFuzzer-DSO2 - LLVMFuzzer - ) - -set_target_properties(LLVMFuzzer-DSOTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/lib/Fuzzer/test") -set_target_properties(LLVMFuzzer-DSO1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/lib/Fuzzer/lib") -set_target_properties(LLVMFuzzer-DSO2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/lib/Fuzzer/lib") - -set(TestBinaries ${TestBinaries} LLVMFuzzer-DSOTest PARENT_SCOPE) diff --git a/libFuzzer/Fuzzer/test/ubsan/CMakeLists.txt b/libFuzzer/Fuzzer/test/ubsan/CMakeLists.txt deleted file mode 100644 index 55e0a11..0000000 --- a/libFuzzer/Fuzzer/test/ubsan/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# These tests are instrumented with ubsan in non-recovery mode. - -set(CMAKE_CXX_FLAGS - "${LIBFUZZER_FLAGS_BASE} -fsanitize=undefined -fno-sanitize-recover=all") - -set(UbsanTests - SignedIntOverflowTest - ) - -foreach(Test ${UbsanTests}) - add_libfuzzer_test(${Test}-Ubsan SOURCES ../${Test}.cpp) -endforeach() diff --git a/libFuzzer/Fuzzer/test/ulimit.test b/libFuzzer/Fuzzer/test/ulimit.test deleted file mode 100644 index c2faca1..0000000 --- a/libFuzzer/Fuzzer/test/ulimit.test +++ /dev/null @@ -1,4 +0,0 @@ -REQUIRES: posix - -RUN: ulimit -s 1000 -RUN: LLVMFuzzer-SimpleTest diff --git a/libFuzzer/Fuzzer/test/uninstrumented/CMakeLists.txt b/libFuzzer/Fuzzer/test/uninstrumented/CMakeLists.txt deleted file mode 100644 index f4ab59e..0000000 --- a/libFuzzer/Fuzzer/test/uninstrumented/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# These tests are not instrumented with coverage and don't -# have coverage rt in the binary. - -set(CMAKE_CXX_FLAGS - "${LIBFUZZER_FLAGS_BASE} -fno-sanitize=all -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters,trace-pc-guard") - -set(UninstrumentedTests - UninstrumentedTest - ) - -foreach(Test ${UninstrumentedTests}) - add_libfuzzer_test(${Test}-Uninstrumented SOURCES ../${Test}.cpp) -endforeach() diff --git a/libFuzzer/Fuzzer/test/unit/lit.cfg b/libFuzzer/Fuzzer/test/unit/lit.cfg deleted file mode 100644 index 0cc3193..0000000 --- a/libFuzzer/Fuzzer/test/unit/lit.cfg +++ /dev/null @@ -1,7 +0,0 @@ -import lit.formats - -config.name = "LLVMFuzzer-Unittest" -print config.test_exec_root -config.test_format = lit.formats.GoogleTest(".", "Unittest") -config.suffixes = [] -config.test_source_root = config.test_exec_root diff --git a/libFuzzer/Fuzzer/test/unit/lit.site.cfg.in b/libFuzzer/Fuzzer/test/unit/lit.site.cfg.in deleted file mode 100644 index 114daf4..0000000 --- a/libFuzzer/Fuzzer/test/unit/lit.site.cfg.in +++ /dev/null @@ -1,2 +0,0 @@ -config.test_exec_root = "@CMAKE_CURRENT_BINARY_DIR@" -lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/unit/lit.cfg") diff --git a/libFuzzer/Fuzzer/test/value-profile-cmp.test b/libFuzzer/Fuzzer/test/value-profile-cmp.test deleted file mode 100644 index 48edba4..0000000 --- a/libFuzzer/Fuzzer/test/value-profile-cmp.test +++ /dev/null @@ -1,2 +0,0 @@ -CHECK: BINGO -RUN: not LLVMFuzzer-SimpleCmpTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/libFuzzer/Fuzzer/test/value-profile-cmp2.test b/libFuzzer/Fuzzer/test/value-profile-cmp2.test deleted file mode 100644 index 43d6240..0000000 --- a/libFuzzer/Fuzzer/test/value-profile-cmp2.test +++ /dev/null @@ -1,2 +0,0 @@ -CHECK: BINGO -RUN: not LLVMFuzzer-SimpleHashTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/libFuzzer/Fuzzer/test/value-profile-cmp3.test b/libFuzzer/Fuzzer/test/value-profile-cmp3.test deleted file mode 100644 index 8a96276..0000000 --- a/libFuzzer/Fuzzer/test/value-profile-cmp3.test +++ /dev/null @@ -1,2 +0,0 @@ -CHECK: BINGO -RUN: not LLVMFuzzer-AbsNegAndConstantTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/libFuzzer/Fuzzer/test/value-profile-cmp4.test b/libFuzzer/Fuzzer/test/value-profile-cmp4.test deleted file mode 100644 index 1e7131e..0000000 --- a/libFuzzer/Fuzzer/test/value-profile-cmp4.test +++ /dev/null @@ -1,2 +0,0 @@ -CHECK: BINGO -RUN: not LLVMFuzzer-AbsNegAndConstant64Test -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/libFuzzer/Fuzzer/test/value-profile-div.test b/libFuzzer/Fuzzer/test/value-profile-div.test deleted file mode 100644 index b966a89..0000000 --- a/libFuzzer/Fuzzer/test/value-profile-div.test +++ /dev/null @@ -1,3 +0,0 @@ -CHECK: AddressSanitizer: {{FPE|int-divide-by-zero}} -RUN: not LLVMFuzzer-DivTest -seed=1 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s - diff --git a/libFuzzer/Fuzzer/test/value-profile-load.test b/libFuzzer/Fuzzer/test/value-profile-load.test deleted file mode 100644 index 14d3109..0000000 --- a/libFuzzer/Fuzzer/test/value-profile-load.test +++ /dev/null @@ -1,3 +0,0 @@ -CHECK: AddressSanitizer: global-buffer-overflow -RUN: not LLVMFuzzer-LoadTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s - diff --git a/libFuzzer/Fuzzer/test/value-profile-mem.test b/libFuzzer/Fuzzer/test/value-profile-mem.test deleted file mode 100644 index 880b269..0000000 --- a/libFuzzer/Fuzzer/test/value-profile-mem.test +++ /dev/null @@ -1,2 +0,0 @@ -CHECK: BINGO -RUN: not LLVMFuzzer-SingleMemcmpTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/libFuzzer/Fuzzer/test/value-profile-set.test b/libFuzzer/Fuzzer/test/value-profile-set.test deleted file mode 100644 index 9d06c36..0000000 --- a/libFuzzer/Fuzzer/test/value-profile-set.test +++ /dev/null @@ -1,3 +0,0 @@ -CHECK: BINGO -RUN: not LLVMFuzzer-FourIndependentBranchesTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s - diff --git a/libFuzzer/Fuzzer/test/value-profile-strcmp.test b/libFuzzer/Fuzzer/test/value-profile-strcmp.test deleted file mode 100644 index 1e7ef9b..0000000 --- a/libFuzzer/Fuzzer/test/value-profile-strcmp.test +++ /dev/null @@ -1,2 +0,0 @@ -CHECK: BINGO -RUN: not LLVMFuzzer-SingleStrcmpTest -seed=1 -use_cmp=0 -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/libFuzzer/Fuzzer/test/value-profile-strncmp.test b/libFuzzer/Fuzzer/test/value-profile-strncmp.test deleted file mode 100644 index 6509731..0000000 --- a/libFuzzer/Fuzzer/test/value-profile-strncmp.test +++ /dev/null @@ -1,2 +0,0 @@ -CHECK: BINGO -RUN: not LLVMFuzzer-SingleStrncmpTest -seed=1 -use_cmp=0 -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/libFuzzer/Fuzzer/test/value-profile-switch.test b/libFuzzer/Fuzzer/test/value-profile-switch.test deleted file mode 100644 index 1947f56..0000000 --- a/libFuzzer/Fuzzer/test/value-profile-switch.test +++ /dev/null @@ -1,3 +0,0 @@ -CHECK: BINGO -RUN: not LLVMFuzzer-SwitchTest -use_cmp=0 -use_value_profile=1 -runs=100000000 -seed=1 2>&1 | FileCheck %s -RUN: not LLVMFuzzer-Switch2Test -use_cmp=0 -use_value_profile=1 -runs=100000000 -seed=1 2>&1 | FileCheck %s diff --git a/libFuzzer/Fuzzer/tests/CMakeLists.txt b/libFuzzer/Fuzzer/tests/CMakeLists.txt new file mode 100644 index 0000000..cfb039c --- /dev/null +++ b/libFuzzer/Fuzzer/tests/CMakeLists.txt @@ -0,0 +1,87 @@ +include(CompilerRTCompile) + +set(LIBFUZZER_UNITTEST_CFLAGS + ${COMPILER_RT_UNITTEST_CFLAGS} + ${COMPILER_RT_GTEST_CFLAGS} + -I${COMPILER_RT_SOURCE_DIR}/lib/fuzzer + -fno-rtti + -O2) + +if (APPLE) + set(FUZZER_SUPPORTED_OS osx) +endif() + +add_custom_target(FuzzerUnitTests) +set_target_properties(FuzzerUnitTests PROPERTIES FOLDER "Compiler-RT Tests") + +add_custom_target(FuzzedDataProviderUnitTests) +set_target_properties(FuzzedDataProviderUnitTests PROPERTIES FOLDER "Compiler-RT Tests") + +set(LIBFUZZER_UNITTEST_LINK_FLAGS ${COMPILER_RT_UNITTEST_LINK_FLAGS}) +list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS --driver-mode=g++) + +if(WIN32) + list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -Wl,-defaultlib:libcmt,-defaultlib:oldnames) +else() + list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lpthread) +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND + COMPILER_RT_LIBCXX_PATH AND + COMPILER_RT_LIBCXXABI_PATH) + list(APPEND LIBFUZZER_UNITTEST_CFLAGS -nostdinc++) +endif() + +if ("-fvisibility=hidden" IN_LIST LIBFUZZER_CFLAGS) + # Match visibility settings. + list(APPEND LIBFUZZER_UNITTEST_CFLAGS "-fvisibility=hidden") +endif() + +if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST FUZZER_SUPPORTED_ARCH) + # libFuzzer unit tests are only run on the host machine. + set(arch ${COMPILER_RT_DEFAULT_TARGET_ARCH}) + + set(LIBFUZZER_TEST_RUNTIME RTFuzzerTest.${arch}) + if(APPLE) + set(LIBFUZZER_TEST_RUNTIME_OBJECTS + $) + else() + set(LIBFUZZER_TEST_RUNTIME_OBJECTS + $) + endif() + add_library(${LIBFUZZER_TEST_RUNTIME} STATIC + ${LIBFUZZER_TEST_RUNTIME_OBJECTS}) + set_target_properties(${LIBFUZZER_TEST_RUNTIME} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + FOLDER "Compiler-RT Runtime tests") + + if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND + COMPILER_RT_LIBCXX_PATH AND + COMPILER_RT_LIBCXXABI_PATH) + file(GLOB libfuzzer_headers ../*.h) + set(LIBFUZZER_TEST_RUNTIME_DEPS libcxx_fuzzer_${arch}-build ${libfuzzer_headers}) + set(LIBFUZZER_TEST_RUNTIME_CFLAGS -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1) + set(LIBFUZZER_TEST_RUNTIME_LINK_FLAGS ${LIBCXX_${arch}_PREFIX}/lib/libc++.a) + endif() + + set(FuzzerTestObjects) + generate_compiler_rt_tests(FuzzerTestObjects + FuzzerUnitTests "Fuzzer-${arch}-Test" ${arch} + SOURCES FuzzerUnittest.cpp ${COMPILER_RT_GTEST_SOURCE} + RUNTIME ${LIBFUZZER_TEST_RUNTIME} + DEPS gtest ${LIBFUZZER_TEST_RUNTIME_DEPS} + CFLAGS ${LIBFUZZER_UNITTEST_CFLAGS} ${LIBFUZZER_TEST_RUNTIME_CFLAGS} + LINK_FLAGS ${LIBFUZZER_UNITTEST_LINK_FLAGS} ${LIBFUZZER_TEST_RUNTIME_LINK_FLAGS}) + set_target_properties(FuzzerUnitTests PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + set(FuzzedDataProviderTestObjects) + generate_compiler_rt_tests(FuzzedDataProviderTestObjects + FuzzedDataProviderUnitTests "FuzzerUtils-${arch}-Test" ${arch} + SOURCES FuzzedDataProviderUnittest.cpp ${COMPILER_RT_GTEST_SOURCE} + DEPS gtest ${LIBFUZZER_TEST_RUNTIME_DEPS} ${COMPILER_RT_SOURCE_DIR}/include/fuzzer/FuzzedDataProvider.h + CFLAGS ${LIBFUZZER_UNITTEST_CFLAGS} ${LIBFUZZER_TEST_RUNTIME_CFLAGS} + LINK_FLAGS ${LIBFUZZER_UNITTEST_LINK_FLAGS} ${LIBFUZZER_TEST_RUNTIME_LINK_FLAGS}) + set_target_properties(FuzzedDataProviderUnitTests PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) +endif() diff --git a/libFuzzer/Fuzzer/tests/FuzzedDataProviderUnittest.cpp b/libFuzzer/Fuzzer/tests/FuzzedDataProviderUnittest.cpp new file mode 100644 index 0000000..2222834 --- /dev/null +++ b/libFuzzer/Fuzzer/tests/FuzzedDataProviderUnittest.cpp @@ -0,0 +1,405 @@ +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "gtest/gtest.h" +#include +#include + +#include + +// The test is intentionally extensive, as behavior of |FuzzedDataProvider| must +// not be broken, given than many fuzz targets depend on it. Changing the +// behavior might invalidate existing corpora and make the fuzz targets using +// |FuzzedDataProvider| to lose code coverage accumulated over time. + +/* A random 1KB buffer generated by: +$ python -c "import os; print ',\n'.join([', '.join(['0x%02X' % ord(i) for i \ + in list(os.urandom(8))]) for _ in xrange(128)])" +*/ +const uint8_t Data[] = { + 0x8A, 0x19, 0x0D, 0x44, 0x37, 0x0D, 0x38, 0x5E, 0x9B, 0xAA, 0xF3, 0xDA, + 0xAA, 0x88, 0xF2, 0x9B, 0x6C, 0xBA, 0xBE, 0xB1, 0xF2, 0xCF, 0x13, 0xB8, + 0xAC, 0x1A, 0x7F, 0x1C, 0xC9, 0x90, 0xD0, 0xD9, 0x5C, 0x42, 0xB3, 0xFD, + 0xE3, 0x05, 0xA4, 0x03, 0x37, 0x49, 0x50, 0x4B, 0xBC, 0x39, 0xA2, 0x09, + 0x6C, 0x2F, 0xAF, 0xD1, 0xB5, 0x47, 0xBF, 0x92, 0xBD, 0x79, 0xE5, 0xC5, + 0x6E, 0x51, 0xA4, 0xED, 0xE9, 0xBD, 0x40, 0x4A, 0xFC, 0x25, 0x7A, 0x27, + 0xC8, 0x92, 0xF7, 0x30, 0xDE, 0x40, 0x66, 0x66, 0xE8, 0x5F, 0x65, 0x39, + 0x7E, 0x9E, 0x80, 0x2B, 0x01, 0x71, 0x2A, 0xFF, 0xD3, 0x0A, 0xAC, 0x6E, + 0x49, 0x32, 0x79, 0x10, 0x6A, 0x6F, 0x97, 0x96, 0x70, 0x7E, 0x50, 0x65, + 0xC9, 0x1D, 0xBD, 0x4E, 0x17, 0x04, 0x1E, 0xBA, 0x26, 0xAC, 0x1F, 0xE3, + 0x37, 0x1C, 0x15, 0x43, 0x60, 0x41, 0x2A, 0x7C, 0xCA, 0x70, 0xCE, 0xAB, + 0x20, 0x24, 0xF8, 0xD9, 0x1F, 0x14, 0x7C, 0x5C, 0xDD, 0x6F, 0xB3, 0xD7, + 0x8B, 0x63, 0x10, 0xB7, 0xDA, 0x99, 0xAF, 0x99, 0x01, 0x21, 0xE6, 0xE1, + 0x86, 0x27, 0xBE, 0x8D, 0xDF, 0x1E, 0xEA, 0x80, 0x0B, 0x8A, 0x60, 0xC3, + 0x3A, 0x85, 0x33, 0x53, 0x59, 0xE1, 0xB5, 0xF1, 0x62, 0xA6, 0x7B, 0x24, + 0x94, 0xE3, 0x8C, 0x10, 0x93, 0xF8, 0x6E, 0xC2, 0x00, 0x91, 0x90, 0x0B, + 0x5D, 0x52, 0x4F, 0x21, 0xE3, 0x40, 0x3A, 0x6E, 0xB6, 0x32, 0x15, 0xDB, + 0x5D, 0x01, 0x86, 0x63, 0x83, 0x24, 0xC5, 0xDE, 0xAB, 0x31, 0x84, 0xAA, + 0xE5, 0x64, 0x02, 0x8D, 0x23, 0x82, 0x86, 0x14, 0x16, 0x18, 0x9F, 0x3D, + 0x31, 0xBE, 0x3B, 0xF0, 0x6C, 0x26, 0x42, 0x9A, 0x67, 0xFE, 0x28, 0xEC, + 0x28, 0xDB, 0x01, 0xB4, 0x52, 0x41, 0x81, 0x7C, 0x54, 0xD3, 0xC8, 0x00, + 0x01, 0x66, 0xB0, 0x2C, 0x3F, 0xBC, 0xAF, 0xAC, 0x87, 0xCD, 0x83, 0xCF, + 0x23, 0xFC, 0xC8, 0x97, 0x8C, 0x71, 0x32, 0x8B, 0xBF, 0x70, 0xC0, 0x48, + 0x31, 0x92, 0x18, 0xFE, 0xE5, 0x33, 0x48, 0x82, 0x98, 0x1E, 0x30, 0xCC, + 0xAD, 0x5D, 0x97, 0xC4, 0xB4, 0x39, 0x7C, 0xCD, 0x39, 0x44, 0xF1, 0xA9, + 0xD0, 0xF4, 0x27, 0xB7, 0x78, 0x85, 0x9E, 0x72, 0xFC, 0xCC, 0xEE, 0x98, + 0x25, 0x3B, 0x69, 0x6B, 0x0C, 0x11, 0xEA, 0x22, 0xB6, 0xD0, 0xCD, 0xBF, + 0x6D, 0xBE, 0x12, 0xDE, 0xFE, 0x78, 0x2E, 0x54, 0xCB, 0xBA, 0xD7, 0x2E, + 0x54, 0x25, 0x14, 0x84, 0xFE, 0x1A, 0x10, 0xCE, 0xCC, 0x20, 0xE6, 0xE2, + 0x7F, 0xE0, 0x5F, 0xDB, 0xA7, 0xF3, 0xE2, 0x4C, 0x52, 0x82, 0xFC, 0x0B, + 0xA0, 0xBD, 0x34, 0x21, 0xF7, 0xEB, 0x1C, 0x5B, 0x67, 0xD0, 0xAF, 0x22, + 0x15, 0xA1, 0xFF, 0xC2, 0x68, 0x25, 0x5B, 0xB2, 0x13, 0x3F, 0xFF, 0x98, + 0x53, 0x25, 0xC5, 0x58, 0x39, 0xD0, 0x43, 0x86, 0x6C, 0x5B, 0x57, 0x8E, + 0x83, 0xBA, 0xB9, 0x09, 0x09, 0x14, 0x0C, 0x9E, 0x99, 0x83, 0x88, 0x53, + 0x79, 0xFD, 0xF7, 0x49, 0xE9, 0x2C, 0xCE, 0xE6, 0x7B, 0xF5, 0xC2, 0x27, + 0x5E, 0x56, 0xB5, 0xB4, 0x46, 0x90, 0x91, 0x7F, 0x99, 0x88, 0xA7, 0x23, + 0xC1, 0x80, 0xB8, 0x2D, 0xCD, 0xF7, 0x6F, 0x9A, 0xEC, 0xBD, 0x16, 0x9F, + 0x7D, 0x87, 0x1E, 0x15, 0x51, 0xC4, 0x96, 0xE2, 0xBF, 0x61, 0x66, 0xB5, + 0xFD, 0x01, 0x67, 0xD6, 0xFF, 0xD2, 0x14, 0x20, 0x98, 0x8E, 0xEF, 0xF3, + 0x22, 0xDB, 0x7E, 0xCE, 0x70, 0x2D, 0x4C, 0x06, 0x5A, 0xA0, 0x4F, 0xC8, + 0xB0, 0x4D, 0xA6, 0x52, 0xB2, 0xD6, 0x2F, 0xD8, 0x57, 0xE5, 0xEF, 0xF9, + 0xEE, 0x52, 0x0F, 0xEC, 0xC4, 0x90, 0x33, 0xAD, 0x25, 0xDA, 0xCD, 0x12, + 0x44, 0x5F, 0x32, 0xF6, 0x6F, 0xEF, 0x85, 0xB8, 0xDC, 0x3C, 0x01, 0x48, + 0x28, 0x5D, 0x2D, 0x9C, 0x9B, 0xC0, 0x49, 0x36, 0x1E, 0x6A, 0x0A, 0x0C, + 0xB0, 0x6E, 0x81, 0x89, 0xCB, 0x0A, 0x89, 0xCF, 0x73, 0xC6, 0x63, 0x3D, + 0x8E, 0x13, 0x57, 0x91, 0x4E, 0xA3, 0x93, 0x8C, 0x61, 0x67, 0xFD, 0x13, + 0xE0, 0x14, 0x72, 0xB3, 0xE4, 0x23, 0x45, 0x08, 0x4E, 0x4E, 0xF5, 0xA7, + 0xA8, 0xEE, 0x30, 0xFD, 0x81, 0x80, 0x1F, 0xF3, 0x4F, 0xD7, 0xE7, 0xF2, + 0x16, 0xC0, 0xD6, 0x15, 0x6A, 0x0F, 0x89, 0x15, 0xA9, 0xCF, 0x35, 0x50, + 0x6B, 0x49, 0x3E, 0x12, 0x4A, 0x72, 0xE4, 0x59, 0x9D, 0xD7, 0xDB, 0xD2, + 0xD1, 0x61, 0x7D, 0x52, 0x4A, 0x36, 0xF6, 0xBA, 0x0E, 0xFA, 0x88, 0x6F, + 0x3C, 0x82, 0x16, 0xF0, 0xD5, 0xED, 0x4D, 0x78, 0xEF, 0x38, 0x17, 0x90, + 0xEA, 0x28, 0x32, 0xA9, 0x79, 0x40, 0xFF, 0xAA, 0xE6, 0xF5, 0xC7, 0x96, + 0x56, 0x65, 0x61, 0x83, 0x3D, 0xBD, 0xD7, 0xED, 0xD6, 0xB6, 0xC0, 0xED, + 0x34, 0xAA, 0x60, 0xA9, 0xE8, 0x82, 0x78, 0xEA, 0x69, 0xF6, 0x47, 0xAF, + 0x39, 0xAB, 0x11, 0xDB, 0xE9, 0xFB, 0x68, 0x0C, 0xFE, 0xDF, 0x97, 0x9F, + 0x3A, 0xF4, 0xF3, 0x32, 0x27, 0x30, 0x57, 0x0E, 0xF7, 0xB2, 0xEE, 0xFB, + 0x1E, 0x98, 0xA8, 0xA3, 0x25, 0x45, 0xE4, 0x6D, 0x2D, 0xAE, 0xFE, 0xDA, + 0xB3, 0x32, 0x9B, 0x5D, 0xF5, 0x32, 0x74, 0xEA, 0xE5, 0x02, 0x30, 0x53, + 0x95, 0x13, 0x7A, 0x23, 0x1F, 0x10, 0x30, 0xEA, 0x78, 0xE4, 0x36, 0x1D, + 0x92, 0x96, 0xB9, 0x91, 0x2D, 0xFA, 0x43, 0xAB, 0xE6, 0xEF, 0x14, 0x14, + 0xC9, 0xBC, 0x46, 0xC6, 0x05, 0x7C, 0xC6, 0x11, 0x23, 0xCF, 0x3D, 0xC8, + 0xBE, 0xEC, 0xA3, 0x58, 0x31, 0x55, 0x65, 0x14, 0xA7, 0x94, 0x93, 0xDD, + 0x2D, 0x76, 0xC9, 0x66, 0x06, 0xBD, 0xF5, 0xE7, 0x30, 0x65, 0x42, 0x52, + 0xA2, 0x50, 0x9B, 0xE6, 0x40, 0xA2, 0x4B, 0xEC, 0xA6, 0xB7, 0x39, 0xAA, + 0xD7, 0x61, 0x2C, 0xBF, 0x37, 0x5A, 0xDA, 0xB3, 0x5D, 0x2F, 0x5D, 0x11, + 0x82, 0x97, 0x32, 0x8A, 0xC1, 0xA1, 0x13, 0x20, 0x17, 0xBD, 0xA2, 0x91, + 0x94, 0x2A, 0x4E, 0xBE, 0x3E, 0x77, 0x63, 0x67, 0x5C, 0x0A, 0xE1, 0x22, + 0x0A, 0x4F, 0x63, 0xE2, 0x84, 0xE9, 0x9F, 0x14, 0x86, 0xE2, 0x4B, 0x20, + 0x9F, 0x50, 0xB3, 0x56, 0xED, 0xDE, 0x39, 0xD8, 0x75, 0x64, 0x45, 0x54, + 0xE5, 0x34, 0x57, 0x8C, 0x3B, 0xF2, 0x0E, 0x94, 0x1B, 0x10, 0xA2, 0xA2, + 0x38, 0x76, 0x21, 0x8E, 0x2A, 0x57, 0x64, 0x58, 0x0A, 0x27, 0x6D, 0x4C, + 0xD0, 0xB5, 0xC1, 0xFC, 0x75, 0xD0, 0x01, 0x86, 0x66, 0xA8, 0xF1, 0x98, + 0x58, 0xFB, 0xFC, 0x64, 0xD2, 0x31, 0x77, 0xAD, 0x0E, 0x46, 0x87, 0xCC, + 0x9B, 0x86, 0x90, 0xFF, 0xB6, 0x64, 0x35, 0xA5, 0x5D, 0x9E, 0x44, 0x51, + 0x87, 0x9E, 0x1E, 0xEE, 0xF3, 0x3B, 0x5C, 0xDD, 0x94, 0x03, 0xAA, 0x18, + 0x2C, 0xB7, 0xC4, 0x37, 0xD5, 0x53, 0x28, 0x60, 0xEF, 0x77, 0xEF, 0x3B, + 0x9E, 0xD2, 0xCE, 0xE9, 0x53, 0x2D, 0xF5, 0x19, 0x7E, 0xBB, 0xB5, 0x46, + 0xE2, 0xF7, 0xD6, 0x4D, 0x6D, 0x5B, 0x81, 0x56, 0x6B, 0x12, 0x55, 0x63, + 0xC3, 0xAB, 0x08, 0xBB, 0x2E, 0xD5, 0x11, 0xBC, 0x18, 0xCB, 0x8B, 0x12, + 0x2E, 0x3E, 0x75, 0x32, 0x98, 0x8A, 0xDE, 0x3C, 0xEA, 0x33, 0x46, 0xE7, + 0x7A, 0xA5, 0x12, 0x09, 0x26, 0x7E, 0x7E, 0x03, 0x4F, 0xFD, 0xC0, 0xFD, + 0xEA, 0x4F, 0x83, 0x85, 0x39, 0x62, 0xFB, 0xA2, 0x33, 0xD9, 0x2D, 0xB1, + 0x30, 0x6F, 0x88, 0xAB, 0x61, 0xCB, 0x32, 0xEB, 0x30, 0xF9, 0x51, 0xF6, + 0x1F, 0x3A, 0x11, 0x4D, 0xFD, 0x54, 0xD6, 0x3D, 0x43, 0x73, 0x39, 0x16, + 0xCF, 0x3D, 0x29, 0x4A}; + +TEST(FuzzedDataProvider, ConsumeBytes) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + EXPECT_EQ(std::vector(1, 0x8A), + DataProv.ConsumeBytes(1)); + EXPECT_EQ(std::vector( + {0x19, 0x0D, 0x44, 0x37, 0x0D, 0x38, 0x5E, 0x9B, 0xAA, 0xF3}), + DataProv.ConsumeBytes(10)); + + std::vector UChars = DataProv.ConsumeBytes(24); + EXPECT_EQ(std::vector({0xDA, 0xAA, 0x88, 0xF2, 0x9B, 0x6C, + 0xBA, 0xBE, 0xB1, 0xF2, 0xCF, 0x13, + 0xB8, 0xAC, 0x1A, 0x7F, 0x1C, 0xC9, + 0x90, 0xD0, 0xD9, 0x5C, 0x42, 0xB3}), + UChars); + + EXPECT_EQ(std::vector(Data + 1 + 10 + 24, Data + sizeof(Data)), + DataProv.ConsumeBytes(31337)); +} + +TEST(FuzzedDataProvider, ConsumeBytesWithTerminator) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + EXPECT_EQ(std::vector({0x8A, 0x00}), + DataProv.ConsumeBytesWithTerminator(1)); + EXPECT_EQ(std::vector({0x19, 0x0D, 0x44, 0x37, 0x0D, 0x38, 0x5E, + 0x9B, 0xAA, 0xF3, 111}), + DataProv.ConsumeBytesWithTerminator(10, 111)); + + std::vector UChars = + DataProv.ConsumeBytesWithTerminator(24); + EXPECT_EQ(std::vector( + {0xDA, 0xAA, 0x88, 0xF2, 0x9B, 0x6C, 0xBA, 0xBE, 0xB1, + 0xF2, 0xCF, 0x13, 0xB8, 0xAC, 0x1A, 0x7F, 0x1C, 0xC9, + 0x90, 0xD0, 0xD9, 0x5C, 0x42, 0xB3, 0x00}), + UChars); + + std::vector Expected(Data + 1 + 10 + 24, Data + sizeof(Data)); + Expected.push_back(65); + EXPECT_EQ(Expected, + DataProv.ConsumeBytesWithTerminator(31337, 65)); +} + +TEST(FuzzedDataProvider, ConsumeBytesAsString) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + EXPECT_EQ(std::string("\x8A\x19\x0D\x44\x37\x0D\x38\x5E\x9B\xAA\xF3\xDA"), + DataProv.ConsumeBytesAsString(12)); + EXPECT_EQ(std::string(Data + 12, Data + sizeof(Data)), + DataProv.ConsumeBytesAsString(31337)); +} + +TEST(FuzzedDataProvider, ConsumeIntegralInRange) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + EXPECT_EQ(int32_t(21), DataProv.ConsumeIntegralInRange(10, 30)); + EXPECT_EQ(int32_t(1337), + DataProv.ConsumeIntegralInRange(1337, 1337)); + EXPECT_EQ(int8_t(-59), DataProv.ConsumeIntegralInRange(-100, 100)); + EXPECT_EQ(uint16_t(15823), + DataProv.ConsumeIntegralInRange(0, 65535)); + EXPECT_EQ((signed char)(-101), + DataProv.ConsumeIntegralInRange(-123, 123)); + EXPECT_EQ(int64_t(-53253077544), DataProv.ConsumeIntegralInRange( + -99999999999, 99999999999)); + + // Exhaust the buffer. + auto String = DataProv.ConsumeBytesAsString(31337); + EXPECT_EQ(size_t(1014), String.length()); + EXPECT_EQ(uint64_t(123456789), + DataProv.ConsumeIntegralInRange(123456789, 987654321)); +} + +TEST(FuzzedDataProvider, ConsumeRandomLengthString) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + EXPECT_EQ( + std::string( + "\x8A\x19\x0D\x44\x37\x0D\x38\x5E\x9B\xAA\xF3\xDA\xAA\x88\xF2\x9B\x6C" + "\xBA\xBE\xB1\xF2\xCF\x13\xB8\xAC\x1A\x7F\x1C\xC9\x90\xD0\xD9"), + DataProv.ConsumeRandomLengthString(1337)); + EXPECT_EQ(std::string( + "\xB3\xFD\xE3\x05\xA4\x03\x37\x49\x50\x4B\xBC\x39\xA2\x09\x6C" + "\x2F\xAF\xD1\xB5\x47\xBF\x92\xBD\x79\xE5\xC5\x6E\x51\xA4\xED" + "\xE9\xBD\x40\x4A\xFC\x25\x7A\x27\xC8\x92\xF7\x30\xDE\x40\x66" + "\x66\xE8\x5F\x65\x39\x7E\x9E\x80\x2B\x01\x71\x2A\xFF\xD3\x0A" + "\xAC\x6E\x49\x32\x79\x10\x6A\x6F\x97\x96\x70\x7E\x50\x65\xC9" + "\x1D\xBD\x4E\x17\x04\x1E\xBA\x26\xAC\x1F\xE3\x37\x1C\x15\x43" + "\x60\x41\x2A\x7C\xCA\x70\xCE\xAB\x20\x24\xF8\xD9\x1F\x14\x7C"), + DataProv.ConsumeRandomLengthString(31337)); + EXPECT_EQ(std::string(Data + 141, Data + 141 + 5), + DataProv.ConsumeRandomLengthString(5)); + EXPECT_EQ(std::string(Data + 141 + 5, Data + 141 + 5 + 2), + DataProv.ConsumeRandomLengthString(2)); + + // Exhaust the buffer. + auto String = DataProv.ConsumeBytesAsString(31337); + EXPECT_EQ(size_t(876), String.length()); + EXPECT_EQ(std::string(), DataProv.ConsumeRandomLengthString(1)); +} + +TEST(FuzzedDataProvider, ConsumeRemainingBytes) { + { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + EXPECT_EQ(std::vector(Data, Data + sizeof(Data)), + DataProv.ConsumeRemainingBytes()); + EXPECT_EQ(std::vector(), + DataProv.ConsumeRemainingBytes()); + } + + { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + EXPECT_EQ(std::vector(Data, Data + 123), + DataProv.ConsumeBytes(123)); + EXPECT_EQ(std::vector(Data + 123, Data + sizeof(Data)), + DataProv.ConsumeRemainingBytes()); + } +} + +TEST(FuzzedDataProvider, ConsumeRemainingBytesAsString) { + { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + EXPECT_EQ(std::string(Data, Data + sizeof(Data)), + DataProv.ConsumeRemainingBytesAsString()); + EXPECT_EQ(std::string(""), DataProv.ConsumeRemainingBytesAsString()); + } + + { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + EXPECT_EQ(std::vector(Data, Data + 123), + DataProv.ConsumeBytes(123)); + EXPECT_EQ(std::string(Data + 123, Data + sizeof(Data)), + DataProv.ConsumeRemainingBytesAsString()); + } +} + +TEST(FuzzedDataProvider, ConsumeIntegral) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + EXPECT_EQ(int32_t(-903266865), DataProv.ConsumeIntegral()); + EXPECT_EQ(uint32_t(372863811), DataProv.ConsumeIntegral()); + EXPECT_EQ(uint8_t(61), DataProv.ConsumeIntegral()); + EXPECT_EQ(int16_t(22100), DataProv.ConsumeIntegral()); + EXPECT_EQ(uint64_t(18252263806144500217u), + DataProv.ConsumeIntegral()); + + // Exhaust the buffer. + auto String = DataProv.ConsumeBytesAsString(31337); + EXPECT_EQ(size_t(1005), String.length()); + EXPECT_EQ(std::numeric_limits::min(), + DataProv.ConsumeIntegral()); +} + +TEST(FuzzedDataProvider, ConsumeBool) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + EXPECT_EQ(false, DataProv.ConsumeBool()); + EXPECT_EQ(true, DataProv.ConsumeBool()); + EXPECT_EQ(true, DataProv.ConsumeBool()); + EXPECT_EQ(true, DataProv.ConsumeBool()); + EXPECT_EQ(false, DataProv.ConsumeBool()); + EXPECT_EQ(true, DataProv.ConsumeBool()); + EXPECT_EQ(true, DataProv.ConsumeBool()); + EXPECT_EQ(true, DataProv.ConsumeBool()); + EXPECT_EQ(true, DataProv.ConsumeBool()); + EXPECT_EQ(false, DataProv.ConsumeBool()); + + // Exhaust the buffer. + auto String = DataProv.ConsumeBytesAsString(31337); + EXPECT_EQ(size_t(1014), String.length()); + EXPECT_EQ(false, DataProv.ConsumeBool()); +} + +TEST(FuzzedDataProvider, PickValueInArray) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + const int Array[] = {1, 2, 3, 4, 5}; + EXPECT_EQ(5, DataProv.PickValueInArray(Array)); + EXPECT_EQ(2, DataProv.PickValueInArray(Array)); + EXPECT_EQ(2, DataProv.PickValueInArray(Array)); + EXPECT_EQ(3, DataProv.PickValueInArray(Array)); + EXPECT_EQ(3, DataProv.PickValueInArray(Array)); + EXPECT_EQ(3, DataProv.PickValueInArray(Array)); + EXPECT_EQ(1, DataProv.PickValueInArray(Array)); + EXPECT_EQ(3, DataProv.PickValueInArray(Array)); + EXPECT_EQ(2, DataProv.PickValueInArray(Array)); + + EXPECT_EQ(uint8_t(0x9D), DataProv.PickValueInArray(Data)); + EXPECT_EQ(uint8_t(0xBA), DataProv.PickValueInArray(Data)); + EXPECT_EQ(uint8_t(0x69), DataProv.PickValueInArray(Data)); + EXPECT_EQ(uint8_t(0xD6), DataProv.PickValueInArray(Data)); + + EXPECT_EQ(uint32_t(777), DataProv.PickValueInArray({1337, 777})); + EXPECT_EQ(uint32_t(777), DataProv.PickValueInArray({1337, 777})); + EXPECT_EQ(uint64_t(1337), DataProv.PickValueInArray({1337, 777})); + EXPECT_EQ(size_t(777), DataProv.PickValueInArray({1337, 777})); + EXPECT_EQ(int16_t(1337), DataProv.PickValueInArray({1337, 777})); + EXPECT_EQ(int32_t(777), DataProv.PickValueInArray({1337, 777})); + EXPECT_EQ(int64_t(777), DataProv.PickValueInArray({1337, 777})); + + // Exhaust the buffer. + auto String = DataProv.ConsumeBytesAsString(31337); + EXPECT_EQ(size_t(1000), String.length()); + EXPECT_EQ(uint8_t(0x8A), DataProv.PickValueInArray(Data)); +} + +TEST(FuzzedDataProvider, ConsumeEnum) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + enum class Enum { + Zero, + One, + Two, + Three, + Four, + Five, + Six, + Seven, + kMaxValue = Seven + }; + EXPECT_EQ(Enum::Two, DataProv.ConsumeEnum()); + EXPECT_EQ(Enum::One, DataProv.ConsumeEnum()); + EXPECT_EQ(Enum::Five, DataProv.ConsumeEnum()); + EXPECT_EQ(Enum::Seven, DataProv.ConsumeEnum()); + EXPECT_EQ(Enum::Six, DataProv.ConsumeEnum()); + EXPECT_EQ(Enum::One, DataProv.ConsumeEnum()); + EXPECT_EQ(Enum::Three, DataProv.ConsumeEnum()); + EXPECT_EQ(Enum::Three, DataProv.ConsumeEnum()); + EXPECT_EQ(Enum::Five, DataProv.ConsumeEnum()); + EXPECT_EQ(Enum::Six, DataProv.ConsumeEnum()); + + // Exhaust the buffer. + auto String = DataProv.ConsumeBytesAsString(31337); + EXPECT_EQ(size_t(1014), String.length()); + EXPECT_EQ(Enum::Zero, DataProv.ConsumeEnum()); +} + +TEST(FuzzedDataProvider, remaining_bytes) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + EXPECT_EQ(size_t(1024), DataProv.remaining_bytes()); + EXPECT_EQ(false, DataProv.ConsumeBool()); + EXPECT_EQ(size_t(1024 - 1), DataProv.remaining_bytes()); + EXPECT_EQ(std::vector(Data, Data + 8), + DataProv.ConsumeBytes(8)); + EXPECT_EQ(size_t(1024 - 1 - 8), DataProv.remaining_bytes()); + + // Exhaust the buffer. + EXPECT_EQ(std::vector(Data + 8, Data + sizeof(Data) - 1), + DataProv.ConsumeRemainingBytes()); + EXPECT_EQ(size_t(0), DataProv.remaining_bytes()); +} + +TEST(FuzzedDataProvider, ConsumeProbability) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + ASSERT_FLOAT_EQ(float(0.28969181), DataProv.ConsumeProbability()); + ASSERT_DOUBLE_EQ(double(0.086814121166605432), + DataProv.ConsumeProbability()); + ASSERT_FLOAT_EQ(float(0.30104411), DataProv.ConsumeProbability()); + ASSERT_DOUBLE_EQ(double(0.96218831486039413), + DataProv.ConsumeProbability()); + ASSERT_FLOAT_EQ(float(0.67005056), DataProv.ConsumeProbability()); + ASSERT_DOUBLE_EQ(double(0.69210584173832279), + DataProv.ConsumeProbability()); + + // Exhaust the buffer. + EXPECT_EQ(std::vector(Data, Data + sizeof(Data) - 36), + DataProv.ConsumeRemainingBytes()); + ASSERT_FLOAT_EQ(float(0.0), DataProv.ConsumeProbability()); +} + +TEST(FuzzedDataProvider, ConsumeFloatingPoint) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + ASSERT_FLOAT_EQ(float(-2.8546307e+38), + DataProv.ConsumeFloatingPoint()); + ASSERT_DOUBLE_EQ(double(8.0940194040236032e+307), + DataProv.ConsumeFloatingPoint()); + ASSERT_FLOAT_EQ(float(271.49084), + DataProv.ConsumeFloatingPointInRange(123.0, 777.0)); + ASSERT_DOUBLE_EQ(double(30.859126145478349), + DataProv.ConsumeFloatingPointInRange(13.37, 31.337)); + ASSERT_FLOAT_EQ( + float(-903.47729), + DataProv.ConsumeFloatingPointInRange(-999.9999, -777.77)); + ASSERT_DOUBLE_EQ( + double(24.561393182922771), + DataProv.ConsumeFloatingPointInRange(-13.37, 31.337)); + ASSERT_FLOAT_EQ(float(1.0), + DataProv.ConsumeFloatingPointInRange(1.0, 1.0)); + ASSERT_DOUBLE_EQ(double(-1.0), + DataProv.ConsumeFloatingPointInRange(-1.0, -1.0)); + + // Exhaust the buffer. + EXPECT_EQ((std::vector(Data, Data + sizeof(Data) - 50)).size(), + DataProv.ConsumeRemainingBytes().size()); + ASSERT_FLOAT_EQ(float(0.0), DataProv.ConsumeProbability()); + ASSERT_NEAR(std::numeric_limits::lowest(), + DataProv.ConsumeFloatingPoint(), 1e-10); + ASSERT_FLOAT_EQ(float(123.0), + DataProv.ConsumeFloatingPointInRange(123.0, 777.0)); + ASSERT_DOUBLE_EQ(double(-13.37), DataProv.ConsumeFloatingPointInRange( + -13.37, 31.337)); +} + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/libFuzzer/Fuzzer/test/FuzzerUnittest.cpp b/libFuzzer/Fuzzer/tests/FuzzerUnittest.cpp similarity index 63% rename from libFuzzer/Fuzzer/test/FuzzerUnittest.cpp rename to libFuzzer/Fuzzer/tests/FuzzerUnittest.cpp index 4992ef5..7fc4b9a 100644 --- a/libFuzzer/Fuzzer/test/FuzzerUnittest.cpp +++ b/libFuzzer/Fuzzer/tests/FuzzerUnittest.cpp @@ -1,19 +1,25 @@ -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // Avoid ODR violations (LibFuzzer is built without ASan and this test is built // with ASan) involving C++ standard library types when using libcxx. #define _LIBCPP_HAS_NO_ASAN +// Do not attempt to use LLVM ostream etc from gtest. +#define GTEST_NO_LLVM_SUPPORT 1 + #include "FuzzerCorpus.h" -#include "FuzzerInternal.h" #include "FuzzerDictionary.h" +#include "FuzzerInternal.h" #include "FuzzerMerge.h" #include "FuzzerMutate.h" #include "FuzzerRandom.h" +#include "FuzzerTracePC.h" #include "gtest/gtest.h" #include #include +#include using namespace fuzzer; @@ -23,11 +29,26 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { abort(); } +TEST(Fuzzer, Basename) { + EXPECT_EQ(Basename("foo/bar"), "bar"); + EXPECT_EQ(Basename("bar"), "bar"); + EXPECT_EQ(Basename("/bar"), "bar"); + EXPECT_EQ(Basename("foo/x"), "x"); + EXPECT_EQ(Basename("foo/"), ""); +#if LIBFUZZER_WINDOWS + EXPECT_EQ(Basename("foo\\bar"), "bar"); + EXPECT_EQ(Basename("foo\\bar/baz"), "baz"); + EXPECT_EQ(Basename("\\bar"), "bar"); + EXPECT_EQ(Basename("foo\\x"), "x"); + EXPECT_EQ(Basename("foo\\"), ""); +#endif +} + TEST(Fuzzer, CrossOver) { std::unique_ptr t(new ExternalFunctions()); fuzzer::EF = t.get(); Random Rand(0); - MutationDispatcher MD(Rand, {}); + std::unique_ptr MD(new MutationDispatcher(Rand, {})); Unit A({0, 1, 2}), B({5, 6, 7}); Unit C; Unit Expected[] = { @@ -67,11 +88,11 @@ TEST(Fuzzer, CrossOver) { { 0, 5, 6, 7, 1, 2 } }; for (size_t Len = 1; Len < 8; Len++) { - std::set FoundUnits, ExpectedUnitsWitThisLength; + Set FoundUnits, ExpectedUnitsWitThisLength; for (int Iter = 0; Iter < 3000; Iter++) { C.resize(Len); - size_t NewSize = MD.CrossOver(A.data(), A.size(), B.data(), B.size(), - C.data(), C.size()); + size_t NewSize = MD->CrossOver(A.data(), A.size(), B.data(), B.size(), + C.data(), C.size()); C.resize(NewSize); FoundUnits.insert(C); } @@ -115,11 +136,11 @@ void TestEraseBytes(Mutator M, int NumIter) { Random Rand(0); - MutationDispatcher MD(Rand, {}); + std::unique_ptr MD(new MutationDispatcher(Rand, {})); int FoundMask = 0; for (int i = 0; i < NumIter; i++) { uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; - size_t NewSize = (MD.*M)(T, sizeof(T), sizeof(T)); + size_t NewSize = (*MD.*M)(T, sizeof(T), sizeof(T)); if (NewSize == 7 && !memcmp(REM0, T, 7)) FoundMask |= 1 << 0; if (NewSize == 7 && !memcmp(REM1, T, 7)) FoundMask |= 1 << 1; if (NewSize == 7 && !memcmp(REM2, T, 7)) FoundMask |= 1 << 2; @@ -151,7 +172,7 @@ void TestInsertByte(Mutator M, int NumIter) { std::unique_ptr t(new ExternalFunctions()); fuzzer::EF = t.get(); Random Rand(0); - MutationDispatcher MD(Rand, {}); + std::unique_ptr MD(new MutationDispatcher(Rand, {})); int FoundMask = 0; uint8_t INS0[8] = {0xF1, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; uint8_t INS1[8] = {0x00, 0xF2, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; @@ -163,7 +184,7 @@ void TestInsertByte(Mutator M, int NumIter) { uint8_t INS7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF8}; for (int i = 0; i < NumIter; i++) { uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; - size_t NewSize = (MD.*M)(T, 7, 8); + size_t NewSize = (*MD.*M)(T, 7, 8); if (NewSize == 8 && !memcmp(INS0, T, 8)) FoundMask |= 1 << 0; if (NewSize == 8 && !memcmp(INS1, T, 8)) FoundMask |= 1 << 1; if (NewSize == 8 && !memcmp(INS2, T, 8)) FoundMask |= 1 << 2; @@ -187,7 +208,7 @@ void TestInsertRepeatedBytes(Mutator M, int NumIter) { std::unique_ptr t(new ExternalFunctions()); fuzzer::EF = t.get(); Random Rand(0); - MutationDispatcher MD(Rand, {}); + std::unique_ptr MD(new MutationDispatcher(Rand, {})); int FoundMask = 0; uint8_t INS0[7] = {0x00, 0x11, 0x22, 0x33, 'a', 'a', 'a'}; uint8_t INS1[7] = {0x00, 0x11, 0x22, 'a', 'a', 'a', 0x33}; @@ -203,7 +224,7 @@ void TestInsertRepeatedBytes(Mutator M, int NumIter) { for (int i = 0; i < NumIter; i++) { uint8_t T[8] = {0x00, 0x11, 0x22, 0x33}; - size_t NewSize = (MD.*M)(T, 4, 8); + size_t NewSize = (*MD.*M)(T, 4, 8); if (NewSize == 7 && !memcmp(INS0, T, 7)) FoundMask |= 1 << 0; if (NewSize == 7 && !memcmp(INS1, T, 7)) FoundMask |= 1 << 1; if (NewSize == 7 && !memcmp(INS2, T, 7)) FoundMask |= 1 << 2; @@ -231,7 +252,7 @@ void TestChangeByte(Mutator M, int NumIter) { std::unique_ptr t(new ExternalFunctions()); fuzzer::EF = t.get(); Random Rand(0); - MutationDispatcher MD(Rand, {}); + std::unique_ptr MD(new MutationDispatcher(Rand, {})); int FoundMask = 0; uint8_t CH0[8] = {0xF0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; uint8_t CH1[8] = {0x00, 0xF1, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; @@ -243,7 +264,7 @@ void TestChangeByte(Mutator M, int NumIter) { uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7}; for (int i = 0; i < NumIter; i++) { uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; - size_t NewSize = (MD.*M)(T, 8, 9); + size_t NewSize = (*MD.*M)(T, 8, 9); if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0; if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1; if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2; @@ -267,7 +288,7 @@ void TestChangeBit(Mutator M, int NumIter) { std::unique_ptr t(new ExternalFunctions()); fuzzer::EF = t.get(); Random Rand(0); - MutationDispatcher MD(Rand, {}); + std::unique_ptr MD(new MutationDispatcher(Rand, {})); int FoundMask = 0; uint8_t CH0[8] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; uint8_t CH1[8] = {0x00, 0x13, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; @@ -279,7 +300,7 @@ void TestChangeBit(Mutator M, int NumIter) { uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7}; for (int i = 0; i < NumIter; i++) { uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; - size_t NewSize = (MD.*M)(T, 8, 9); + size_t NewSize = (*MD.*M)(T, 8, 9); if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0; if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1; if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2; @@ -303,7 +324,7 @@ void TestShuffleBytes(Mutator M, int NumIter) { std::unique_ptr t(new ExternalFunctions()); fuzzer::EF = t.get(); Random Rand(0); - MutationDispatcher MD(Rand, {}); + std::unique_ptr MD(new MutationDispatcher(Rand, {})); int FoundMask = 0; uint8_t CH0[7] = {0x00, 0x22, 0x11, 0x33, 0x44, 0x55, 0x66}; uint8_t CH1[7] = {0x11, 0x00, 0x33, 0x22, 0x44, 0x55, 0x66}; @@ -312,7 +333,7 @@ void TestShuffleBytes(Mutator M, int NumIter) { uint8_t CH4[7] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x44, 0x66}; for (int i = 0; i < NumIter; i++) { uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; - size_t NewSize = (MD.*M)(T, 7, 7); + size_t NewSize = (*MD.*M)(T, 7, 7); if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0; if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1; if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2; @@ -323,7 +344,7 @@ void TestShuffleBytes(Mutator M, int NumIter) { } TEST(FuzzerMutate, ShuffleBytes1) { - TestShuffleBytes(&MutationDispatcher::Mutate_ShuffleBytes, 1 << 16); + TestShuffleBytes(&MutationDispatcher::Mutate_ShuffleBytes, 1 << 17); } TEST(FuzzerMutate, ShuffleBytes2) { TestShuffleBytes(&MutationDispatcher::Mutate, 1 << 20); @@ -333,7 +354,7 @@ void TestCopyPart(Mutator M, int NumIter) { std::unique_ptr t(new ExternalFunctions()); fuzzer::EF = t.get(); Random Rand(0); - MutationDispatcher MD(Rand, {}); + std::unique_ptr MD(new MutationDispatcher(Rand, {})); int FoundMask = 0; uint8_t CH0[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11}; uint8_t CH1[7] = {0x55, 0x66, 0x22, 0x33, 0x44, 0x55, 0x66}; @@ -343,7 +364,7 @@ void TestCopyPart(Mutator M, int NumIter) { for (int i = 0; i < NumIter; i++) { uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; - size_t NewSize = (MD.*M)(T, 7, 7); + size_t NewSize = (*MD.*M)(T, 7, 7); if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0; if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1; if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2; @@ -359,7 +380,7 @@ void TestCopyPart(Mutator M, int NumIter) { for (int i = 0; i < NumIter; i++) { uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; - size_t NewSize = (MD.*M)(T, 5, 8); + size_t NewSize = (*MD.*M)(T, 5, 8); if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5; if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6; if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7; @@ -376,16 +397,31 @@ TEST(FuzzerMutate, CopyPart1) { TEST(FuzzerMutate, CopyPart2) { TestCopyPart(&MutationDispatcher::Mutate, 1 << 13); } +TEST(FuzzerMutate, CopyPartNoInsertAtMaxSize) { + // This (non exhaustively) tests if `Mutate_CopyPart` tries to perform an + // insert on an input of size `MaxSize`. Performing an insert in this case + // will lead to the mutation failing. + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + std::unique_ptr MD(new MutationDispatcher(Rand, {})); + uint8_t Data[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11, 0x22}; + size_t MaxSize = sizeof(Data); + for (int count = 0; count < (1 << 18); ++count) { + size_t NewSize = MD->Mutate_CopyPart(Data, MaxSize, MaxSize); + ASSERT_EQ(NewSize, MaxSize); + } +} void TestAddWordFromDictionary(Mutator M, int NumIter) { std::unique_ptr t(new ExternalFunctions()); fuzzer::EF = t.get(); Random Rand(0); - MutationDispatcher MD(Rand, {}); + std::unique_ptr MD(new MutationDispatcher(Rand, {})); uint8_t Word1[4] = {0xAA, 0xBB, 0xCC, 0xDD}; uint8_t Word2[3] = {0xFF, 0xEE, 0xEF}; - MD.AddWordToManualDictionary(Word(Word1, sizeof(Word1))); - MD.AddWordToManualDictionary(Word(Word2, sizeof(Word2))); + MD->AddWordToManualDictionary(Word(Word1, sizeof(Word1))); + MD->AddWordToManualDictionary(Word(Word2, sizeof(Word2))); int FoundMask = 0; uint8_t CH0[7] = {0x00, 0x11, 0x22, 0xAA, 0xBB, 0xCC, 0xDD}; uint8_t CH1[7] = {0x00, 0x11, 0xAA, 0xBB, 0xCC, 0xDD, 0x22}; @@ -397,7 +433,7 @@ void TestAddWordFromDictionary(Mutator M, int NumIter) { uint8_t CH7[6] = {0xFF, 0xEE, 0xEF, 0x00, 0x11, 0x22}; for (int i = 0; i < NumIter; i++) { uint8_t T[7] = {0x00, 0x11, 0x22}; - size_t NewSize = (MD.*M)(T, 3, 7); + size_t NewSize = (*MD.*M)(T, 3, 7); if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0; if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1; if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2; @@ -419,40 +455,11 @@ TEST(FuzzerMutate, AddWordFromDictionary2) { TestAddWordFromDictionary(&MutationDispatcher::Mutate, 1 << 15); } -void TestAddWordFromDictionaryWithHint(Mutator M, int NumIter) { - std::unique_ptr t(new ExternalFunctions()); - fuzzer::EF = t.get(); - Random Rand(0); - MutationDispatcher MD(Rand, {}); - uint8_t W[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xFF, 0xEE, 0xEF}; - size_t PosHint = 7777; - MD.AddWordToAutoDictionary({Word(W, sizeof(W)), PosHint}); - int FoundMask = 0; - for (int i = 0; i < NumIter; i++) { - uint8_t T[10000]; - memset(T, 0, sizeof(T)); - size_t NewSize = (MD.*M)(T, 9000, 10000); - if (NewSize >= PosHint + sizeof(W) && - !memcmp(W, T + PosHint, sizeof(W))) - FoundMask = 1; - } - EXPECT_EQ(FoundMask, 1); -} - -TEST(FuzzerMutate, AddWordFromDictionaryWithHint1) { - TestAddWordFromDictionaryWithHint( - &MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary, 1 << 5); -} - -TEST(FuzzerMutate, AddWordFromDictionaryWithHint2) { - TestAddWordFromDictionaryWithHint(&MutationDispatcher::Mutate, 1 << 10); -} - void TestChangeASCIIInteger(Mutator M, int NumIter) { std::unique_ptr t(new ExternalFunctions()); fuzzer::EF = t.get(); Random Rand(0); - MutationDispatcher MD(Rand, {}); + std::unique_ptr MD(new MutationDispatcher(Rand, {})); uint8_t CH0[8] = {'1', '2', '3', '4', '5', '6', '7', '7'}; uint8_t CH1[8] = {'1', '2', '3', '4', '5', '6', '7', '9'}; @@ -461,7 +468,7 @@ void TestChangeASCIIInteger(Mutator M, int NumIter) { int FoundMask = 0; for (int i = 0; i < NumIter; i++) { uint8_t T[8] = {'1', '2', '3', '4', '5', '6', '7', '8'}; - size_t NewSize = (MD.*M)(T, 8, 8); + size_t NewSize = (*MD.*M)(T, 8, 8); /**/ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0; else if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1; else if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2; @@ -484,7 +491,7 @@ void TestChangeBinaryInteger(Mutator M, int NumIter) { std::unique_ptr t(new ExternalFunctions()); fuzzer::EF = t.get(); Random Rand(0); - MutationDispatcher MD(Rand, {}); + std::unique_ptr MD(new MutationDispatcher(Rand, {})); uint8_t CH0[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x79}; uint8_t CH1[8] = {0x00, 0x11, 0x22, 0x31, 0x44, 0x55, 0x66, 0x77}; @@ -498,7 +505,7 @@ void TestChangeBinaryInteger(Mutator M, int NumIter) { int FoundMask = 0; for (int i = 0; i < NumIter; i++) { uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; - size_t NewSize = (MD.*M)(T, 8, 8); + size_t NewSize = (*MD.*M)(T, 8, 8); /**/ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0; else if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1; else if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2; @@ -550,7 +557,7 @@ TEST(FuzzerDictionary, ParseOneDictionaryEntry) { } TEST(FuzzerDictionary, ParseDictionaryFile) { - std::vector Units; + Vector Units; EXPECT_FALSE(ParseDictionaryFile("zzz\n", &Units)); EXPECT_FALSE(ParseDictionaryFile("", &Units)); EXPECT_TRUE(ParseDictionaryFile("\n", &Units)); @@ -562,11 +569,11 @@ TEST(FuzzerDictionary, ParseDictionaryFile) { EXPECT_TRUE(ParseDictionaryFile(" #zzzz\n", &Units)); EXPECT_EQ(Units.size(), 0U); EXPECT_TRUE(ParseDictionaryFile(" #zzzz\naaa=\"aa\"", &Units)); - EXPECT_EQ(Units, std::vector({Unit({'a', 'a'})})); + EXPECT_EQ(Units, Vector({Unit({'a', 'a'})})); EXPECT_TRUE( ParseDictionaryFile(" #zzzz\naaa=\"aa\"\n\nabc=\"abc\"", &Units)); EXPECT_EQ(Units, - std::vector({Unit({'a', 'a'}), Unit({'a', 'b', 'c'})})); + Vector({Unit({'a', 'a'}), Unit({'a', 'b', 'c'})})); } TEST(FuzzerUtil, Base64) { @@ -583,16 +590,18 @@ TEST(FuzzerUtil, Base64) { } TEST(Corpus, Distribution) { + DataFlowTrace DFT; Random Rand(0); - InputCorpus C(""); + std::unique_ptr C(new InputCorpus("")); size_t N = 10; size_t TriesPerUnit = 1<<16; for (size_t i = 0; i < N; i++) - C.AddToCorpus(Unit{ static_cast(i) }, 0); + C->AddToCorpus(Unit{static_cast(i)}, 1, false, false, {}, DFT, + nullptr); - std::vector Hist(N); + Vector Hist(N); for (size_t i = 0; i < N * TriesPerUnit; i++) { - Hist[C.ChooseUnitIdxToMutate(Rand)]++; + Hist[C->ChooseUnitIdxToMutate(Rand)]++; } for (size_t i = 0; i < N; i++) { // A weak sanity check that every unit gets invoked. @@ -610,7 +619,7 @@ TEST(Merge, Bad) { "2\n2\nA\n", "2\n2\nA\nB\nC\n", "0\n0\n", - "1\n1\nA\nDONE 0", + "1\n1\nA\nFT 0", "1\n1\nA\nSTARTED 1", }; Merger M; @@ -620,23 +629,24 @@ TEST(Merge, Bad) { } } -void EQ(const std::vector &A, const std::vector &B) { +void EQ(const Vector &A, const Vector &B) { EXPECT_EQ(A, B); } -void EQ(const std::vector &A, const std::vector &B) { - std::set a(A.begin(), A.end()); - std::set b(B.begin(), B.end()); +void EQ(const Vector &A, const Vector &B) { + Set a(A.begin(), A.end()); + Set b(B.begin(), B.end()); EXPECT_EQ(a, b); } static void Merge(const std::string &Input, - const std::vector Result, + const Vector Result, size_t NumNewFeatures) { Merger M; - std::vector NewFiles; + Vector NewFiles; + Set NewFeatures, NewCov; EXPECT_TRUE(M.Parse(Input, true)); - EXPECT_EQ(NumNewFeatures, M.Merge(&NewFiles)); + EXPECT_EQ(NumNewFeatures, M.Merge({}, &NewFeatures, {}, &NewCov, &NewFiles)); EQ(NewFiles, Result); } @@ -660,9 +670,9 @@ TEST(Merge, Good) { EXPECT_TRUE(M.Parse("3\n1\nAA\nBB\nC\n" "STARTED 0 1000\n" - "DONE 0 1 2 3\n" + "FT 0 1 2 3\n" "STARTED 1 1001\n" - "DONE 1 4 5 6 \n" + "FT 1 4 5 6 \n" "STARTED 2 1002\n" "", true)); EXPECT_EQ(M.Files.size(), 3U); @@ -679,12 +689,13 @@ TEST(Merge, Good) { EQ(M.Files[1].Features, {4, 5, 6}); - std::vector NewFiles; + Vector NewFiles; + Set NewFeatures, NewCov; EXPECT_TRUE(M.Parse("3\n2\nAA\nBB\nC\n" - "STARTED 0 1000\nDONE 0 1 2 3\n" - "STARTED 1 1001\nDONE 1 4 5 6 \n" - "STARTED 2 1002\nDONE 2 6 1 3 \n" + "STARTED 0 1000\nFT 0 1 2 3\n" + "STARTED 1 1001\nFT 1 4 5 6 \n" + "STARTED 2 1002\nFT 2 6 1 3 \n" "", true)); EXPECT_EQ(M.Files.size(), 3U); EXPECT_EQ(M.NumFilesInFirstCorpus, 2U); @@ -693,46 +704,353 @@ TEST(Merge, Good) { EQ(M.Files[0].Features, {1, 2, 3}); EQ(M.Files[1].Features, {4, 5, 6}); EQ(M.Files[2].Features, {1, 3, 6}); - EXPECT_EQ(0U, M.Merge(&NewFiles)); + EXPECT_EQ(0U, M.Merge({}, &NewFeatures, {}, &NewCov, &NewFiles)); EQ(NewFiles, {}); EXPECT_TRUE(M.Parse("3\n1\nA\nB\nC\n" - "STARTED 0 1000\nDONE 0 1 2 3\n" - "STARTED 1 1001\nDONE 1 4 5 6 \n" - "STARTED 2 1002\nDONE 2 6 1 3\n" + "STARTED 0 1000\nFT 0 1 2 3\n" + "STARTED 1 1001\nFT 1 4 5 6 \n" + "STARTED 2 1002\nFT 2 6 1 3\n" "", true)); EQ(M.Files[0].Features, {1, 2, 3}); EQ(M.Files[1].Features, {4, 5, 6}); EQ(M.Files[2].Features, {1, 3, 6}); - EXPECT_EQ(3U, M.Merge(&NewFiles)); + EXPECT_EQ(3U, M.Merge({}, &NewFeatures, {}, &NewCov, &NewFiles)); + EQ(NewFiles, {"B"}); + + // Same as the above, but with InitialFeatures. + EXPECT_TRUE(M.Parse("2\n0\nB\nC\n" + "STARTED 0 1001\nFT 0 4 5 6 \n" + "STARTED 1 1002\nFT 1 6 1 3\n" + "", true)); + EQ(M.Files[0].Features, {4, 5, 6}); + EQ(M.Files[1].Features, {1, 3, 6}); + Set InitialFeatures; + InitialFeatures.insert(1); + InitialFeatures.insert(2); + InitialFeatures.insert(3); + EXPECT_EQ(3U, M.Merge(InitialFeatures, &NewFeatures, {}, &NewCov, &NewFiles)); EQ(NewFiles, {"B"}); } TEST(Merge, Merge) { Merge("3\n1\nA\nB\nC\n" - "STARTED 0 1000\nDONE 0 1 2 3\n" - "STARTED 1 1001\nDONE 1 4 5 6 \n" - "STARTED 2 1002\nDONE 2 6 1 3 \n", + "STARTED 0 1000\nFT 0 1 2 3\n" + "STARTED 1 1001\nFT 1 4 5 6 \n" + "STARTED 2 1002\nFT 2 6 1 3 \n", {"B"}, 3); Merge("3\n0\nA\nB\nC\n" - "STARTED 0 2000\nDONE 0 1 2 3\n" - "STARTED 1 1001\nDONE 1 4 5 6 \n" - "STARTED 2 1002\nDONE 2 6 1 3 \n", + "STARTED 0 2000\nFT 0 1 2 3\n" + "STARTED 1 1001\nFT 1 4 5 6 \n" + "STARTED 2 1002\nFT 2 6 1 3 \n", {"A", "B", "C"}, 6); Merge("4\n0\nA\nB\nC\nD\n" - "STARTED 0 2000\nDONE 0 1 2 3\n" - "STARTED 1 1101\nDONE 1 4 5 6 \n" - "STARTED 2 1102\nDONE 2 6 1 3 100 \n" - "STARTED 3 1000\nDONE 3 1 \n", + "STARTED 0 2000\nFT 0 1 2 3\n" + "STARTED 1 1101\nFT 1 4 5 6 \n" + "STARTED 2 1102\nFT 2 6 1 3 100 \n" + "STARTED 3 1000\nFT 3 1 \n", {"A", "B", "C", "D"}, 7); Merge("4\n1\nA\nB\nC\nD\n" - "STARTED 0 2000\nDONE 0 4 5 6 7 8\n" - "STARTED 1 1100\nDONE 1 1 2 3 \n" - "STARTED 2 1100\nDONE 2 2 3 \n" - "STARTED 3 1000\nDONE 3 1 \n", + "STARTED 0 2000\nFT 0 4 5 6 7 8\n" + "STARTED 1 1100\nFT 1 1 2 3 \n" + "STARTED 2 1100\nFT 2 2 3 \n" + "STARTED 3 1000\nFT 3 1 \n", {"B", "D"}, 3); } + +TEST(DFT, BlockCoverage) { + BlockCoverage Cov; + // Assuming C0 has 5 instrumented blocks, + // C1: 7 blocks, C2: 4, C3: 9, C4 never covered, C5: 15, + + // Add C0 + EXPECT_TRUE(Cov.AppendCoverage("C0 5\n")); + EXPECT_EQ(Cov.GetCounter(0, 0), 1U); + EXPECT_EQ(Cov.GetCounter(0, 1), 0U); // not seen this BB yet. + EXPECT_EQ(Cov.GetCounter(0, 5), 0U); // BB ID out of bounds. + EXPECT_EQ(Cov.GetCounter(1, 0), 0U); // not seen this function yet. + + EXPECT_EQ(Cov.GetNumberOfBlocks(0), 5U); + EXPECT_EQ(Cov.GetNumberOfCoveredBlocks(0), 1U); + EXPECT_EQ(Cov.GetNumberOfBlocks(1), 0U); + + // Various errors. + EXPECT_FALSE(Cov.AppendCoverage("C0\n")); // No total number. + EXPECT_FALSE(Cov.AppendCoverage("C0 7\n")); // No total number. + EXPECT_FALSE(Cov.AppendCoverage("CZ\n")); // Wrong function number. + EXPECT_FALSE(Cov.AppendCoverage("C1 7 7")); // BB ID is too big. + EXPECT_FALSE(Cov.AppendCoverage("C1 100 7")); // BB ID is too big. + + // Add C0 more times. + EXPECT_TRUE(Cov.AppendCoverage("C0 5\n")); + EXPECT_EQ(Cov.GetCounter(0, 0), 2U); + EXPECT_TRUE(Cov.AppendCoverage("C0 1 2 5\n")); + EXPECT_EQ(Cov.GetCounter(0, 0), 3U); + EXPECT_EQ(Cov.GetCounter(0, 1), 1U); + EXPECT_EQ(Cov.GetCounter(0, 2), 1U); + EXPECT_EQ(Cov.GetCounter(0, 3), 0U); + EXPECT_EQ(Cov.GetCounter(0, 4), 0U); + EXPECT_EQ(Cov.GetNumberOfCoveredBlocks(0), 3U); + EXPECT_TRUE(Cov.AppendCoverage("C0 1 3 4 5\n")); + EXPECT_EQ(Cov.GetCounter(0, 0), 4U); + EXPECT_EQ(Cov.GetCounter(0, 1), 2U); + EXPECT_EQ(Cov.GetCounter(0, 2), 1U); + EXPECT_EQ(Cov.GetCounter(0, 3), 1U); + EXPECT_EQ(Cov.GetCounter(0, 4), 1U); + EXPECT_EQ(Cov.GetNumberOfCoveredBlocks(0), 5U); + + EXPECT_TRUE(Cov.AppendCoverage("C1 7\nC2 4\nC3 9\nC5 15\nC0 5\n")); + EXPECT_EQ(Cov.GetCounter(0, 0), 5U); + EXPECT_EQ(Cov.GetCounter(1, 0), 1U); + EXPECT_EQ(Cov.GetCounter(2, 0), 1U); + EXPECT_EQ(Cov.GetCounter(3, 0), 1U); + EXPECT_EQ(Cov.GetCounter(4, 0), 0U); + EXPECT_EQ(Cov.GetCounter(5, 0), 1U); + + EXPECT_TRUE(Cov.AppendCoverage("C3 4 5 9\nC5 11 12 15")); + EXPECT_EQ(Cov.GetCounter(0, 0), 5U); + EXPECT_EQ(Cov.GetCounter(1, 0), 1U); + EXPECT_EQ(Cov.GetCounter(2, 0), 1U); + EXPECT_EQ(Cov.GetCounter(3, 0), 2U); + EXPECT_EQ(Cov.GetCounter(3, 4), 1U); + EXPECT_EQ(Cov.GetCounter(3, 5), 1U); + EXPECT_EQ(Cov.GetCounter(3, 6), 0U); + EXPECT_EQ(Cov.GetCounter(4, 0), 0U); + EXPECT_EQ(Cov.GetCounter(5, 0), 2U); + EXPECT_EQ(Cov.GetCounter(5, 10), 0U); + EXPECT_EQ(Cov.GetCounter(5, 11), 1U); + EXPECT_EQ(Cov.GetCounter(5, 12), 1U); +} + +TEST(DFT, FunctionWeights) { + BlockCoverage Cov; + // unused function gets zero weight. + EXPECT_TRUE(Cov.AppendCoverage("C0 5\n")); + auto Weights = Cov.FunctionWeights(2); + EXPECT_GT(Weights[0], 0.); + EXPECT_EQ(Weights[1], 0.); + + // Less frequently used function gets less weight. + Cov.clear(); + EXPECT_TRUE(Cov.AppendCoverage("C0 5\nC1 5\nC1 5\n")); + Weights = Cov.FunctionWeights(2); + EXPECT_GT(Weights[0], Weights[1]); + + // A function with more uncovered blocks gets more weight. + Cov.clear(); + EXPECT_TRUE(Cov.AppendCoverage("C0 1 2 3 5\nC1 2 4\n")); + Weights = Cov.FunctionWeights(2); + EXPECT_GT(Weights[1], Weights[0]); + + // A function with DFT gets more weight than the function w/o DFT. + Cov.clear(); + EXPECT_TRUE(Cov.AppendCoverage("F1 111\nC0 3\nC1 1 2 3\n")); + Weights = Cov.FunctionWeights(2); + EXPECT_GT(Weights[1], Weights[0]); +} + + +TEST(Fuzzer, ForEachNonZeroByte) { + const size_t N = 64; + alignas(64) uint8_t Ar[N + 8] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 0, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 7, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 8, + 9, 9, 9, 9, 9, 9, 9, 9, + }; + typedef Vector > Vec; + Vec Res, Expected; + auto CB = [&](size_t FirstFeature, size_t Idx, uint8_t V) { + Res.push_back({FirstFeature + Idx, V}); + }; + ForEachNonZeroByte(Ar, Ar + N, 100, CB); + Expected = {{108, 1}, {109, 2}, {118, 3}, {120, 4}, + {135, 5}, {137, 6}, {146, 7}, {163, 8}}; + EXPECT_EQ(Res, Expected); + + Res.clear(); + ForEachNonZeroByte(Ar + 9, Ar + N, 109, CB); + Expected = { {109, 2}, {118, 3}, {120, 4}, + {135, 5}, {137, 6}, {146, 7}, {163, 8}}; + EXPECT_EQ(Res, Expected); + + Res.clear(); + ForEachNonZeroByte(Ar + 9, Ar + N - 9, 109, CB); + Expected = { {109, 2}, {118, 3}, {120, 4}, + {135, 5}, {137, 6}, {146, 7}}; + EXPECT_EQ(Res, Expected); +} + +// FuzzerCommand unit tests. The arguments in the two helper methods below must +// match. +static void makeCommandArgs(Vector *ArgsToAdd) { + assert(ArgsToAdd); + ArgsToAdd->clear(); + ArgsToAdd->push_back("foo"); + ArgsToAdd->push_back("-bar=baz"); + ArgsToAdd->push_back("qux"); + ArgsToAdd->push_back(Command::ignoreRemainingArgs()); + ArgsToAdd->push_back("quux"); + ArgsToAdd->push_back("-grault=garply"); +} + +static std::string makeCmdLine(const char *separator, const char *suffix) { + std::string CmdLine("foo -bar=baz qux "); + if (strlen(separator) != 0) { + CmdLine += separator; + CmdLine += " "; + } + CmdLine += Command::ignoreRemainingArgs(); + CmdLine += " quux -grault=garply"; + if (strlen(suffix) != 0) { + CmdLine += " "; + CmdLine += suffix; + } + return CmdLine; +} + +TEST(FuzzerCommand, Create) { + std::string CmdLine; + + // Default constructor + Command DefaultCmd; + + CmdLine = DefaultCmd.toString(); + EXPECT_EQ(CmdLine, ""); + + // Explicit constructor + Vector ArgsToAdd; + makeCommandArgs(&ArgsToAdd); + Command InitializedCmd(ArgsToAdd); + + CmdLine = InitializedCmd.toString(); + EXPECT_EQ(CmdLine, makeCmdLine("", "")); + + // Compare each argument + auto InitializedArgs = InitializedCmd.getArguments(); + auto i = ArgsToAdd.begin(); + auto j = InitializedArgs.begin(); + while (i != ArgsToAdd.end() && j != InitializedArgs.end()) { + EXPECT_EQ(*i++, *j++); + } + EXPECT_EQ(i, ArgsToAdd.end()); + EXPECT_EQ(j, InitializedArgs.end()); + + // Copy constructor + Command CopiedCmd(InitializedCmd); + + CmdLine = CopiedCmd.toString(); + EXPECT_EQ(CmdLine, makeCmdLine("", "")); + + // Assignment operator + Command AssignedCmd; + AssignedCmd = CopiedCmd; + + CmdLine = AssignedCmd.toString(); + EXPECT_EQ(CmdLine, makeCmdLine("", "")); +} + +TEST(FuzzerCommand, ModifyArguments) { + Vector ArgsToAdd; + makeCommandArgs(&ArgsToAdd); + Command Cmd; + std::string CmdLine; + + Cmd.addArguments(ArgsToAdd); + CmdLine = Cmd.toString(); + EXPECT_EQ(CmdLine, makeCmdLine("", "")); + + Cmd.addArgument("waldo"); + EXPECT_TRUE(Cmd.hasArgument("waldo")); + + CmdLine = Cmd.toString(); + EXPECT_EQ(CmdLine, makeCmdLine("waldo", "")); + + Cmd.removeArgument("waldo"); + EXPECT_FALSE(Cmd.hasArgument("waldo")); + + CmdLine = Cmd.toString(); + EXPECT_EQ(CmdLine, makeCmdLine("", "")); +} + +TEST(FuzzerCommand, ModifyFlags) { + Vector ArgsToAdd; + makeCommandArgs(&ArgsToAdd); + Command Cmd(ArgsToAdd); + std::string Value, CmdLine; + ASSERT_FALSE(Cmd.hasFlag("fred")); + + Value = Cmd.getFlagValue("fred"); + EXPECT_EQ(Value, ""); + + CmdLine = Cmd.toString(); + EXPECT_EQ(CmdLine, makeCmdLine("", "")); + + Cmd.addFlag("fred", "plugh"); + EXPECT_TRUE(Cmd.hasFlag("fred")); + + Value = Cmd.getFlagValue("fred"); + EXPECT_EQ(Value, "plugh"); + + CmdLine = Cmd.toString(); + EXPECT_EQ(CmdLine, makeCmdLine("-fred=plugh", "")); + + Cmd.removeFlag("fred"); + EXPECT_FALSE(Cmd.hasFlag("fred")); + + Value = Cmd.getFlagValue("fred"); + EXPECT_EQ(Value, ""); + + CmdLine = Cmd.toString(); + EXPECT_EQ(CmdLine, makeCmdLine("", "")); +} + +TEST(FuzzerCommand, SetOutput) { + Vector ArgsToAdd; + makeCommandArgs(&ArgsToAdd); + Command Cmd(ArgsToAdd); + std::string CmdLine; + ASSERT_FALSE(Cmd.hasOutputFile()); + ASSERT_FALSE(Cmd.isOutAndErrCombined()); + + Cmd.combineOutAndErr(true); + EXPECT_TRUE(Cmd.isOutAndErrCombined()); + + CmdLine = Cmd.toString(); + EXPECT_EQ(CmdLine, makeCmdLine("", "2>&1")); + + Cmd.combineOutAndErr(false); + EXPECT_FALSE(Cmd.isOutAndErrCombined()); + + Cmd.setOutputFile("xyzzy"); + EXPECT_TRUE(Cmd.hasOutputFile()); + + CmdLine = Cmd.toString(); + EXPECT_EQ(CmdLine, makeCmdLine("", ">xyzzy")); + + Cmd.setOutputFile("thud"); + EXPECT_TRUE(Cmd.hasOutputFile()); + + CmdLine = Cmd.toString(); + EXPECT_EQ(CmdLine, makeCmdLine("", ">thud")); + + Cmd.combineOutAndErr(); + EXPECT_TRUE(Cmd.isOutAndErrCombined()); + + CmdLine = Cmd.toString(); + EXPECT_EQ(CmdLine, makeCmdLine("", ">thud 2>&1")); +} + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 8567e3b911990f267c3b16e8cfdc9a214cd9078f Mon Sep 17 00:00:00 2001 From: julianvolodia Date: Tue, 5 May 2020 02:06:07 +0000 Subject: [PATCH 04/13] lesson 4: libFuzzer generate symbolized stacktrace by default --- lessons/04/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lessons/04/README.md b/lessons/04/README.md index d9411d2..1b23112 100644 --- a/lessons/04/README.md +++ b/lessons/04/README.md @@ -43,7 +43,7 @@ Create an empty directory for corpus and run the fuzzer: ```bash mkdir corpus1 -./first_fuzzer corpus1 +ASAN_OPTIONS=symbolize=0 ./first_fuzzer corpus1 ``` You should see the following input: @@ -77,10 +77,12 @@ reproduce the crash: $ ./first_fuzzer crash-0eb8e4ed029b774d80f2b66408203801cb982a60 ``` +Since some time ago, symbolized stacktrace is generated by default. To get a symbolized stacktrace, add `symbolize=1` option to `ASAN_OPTIONS` env -variable: +variable or no extra env variable: ```bash ASAN_OPTIONS=symbolize=1 ./first_fuzzer crash-0eb8e4ed029b774d80f2b66408203801cb982a60 +./first_fuzzer crash-0eb8e4ed029b774d80f2b66408203801cb982a60 ``` The symbolized result looks like: @@ -148,7 +150,7 @@ Shadow byte legend (one shadow byte represents 8 application bytes): ==15226==ABORTING ``` -To get symbolized stack-traces by default, let's export `ASAN_OPTIONS` env var: +To get symbolized stack-traces by default, for different version of libFuzzer, let's export `ASAN_OPTIONS` env var: ```bash export ASAN_OPTIONS=symbolize=1 ``` From cf30f977c923b3de3b3f6b86e5491201568128d8 Mon Sep 17 00:00:00 2001 From: julianvolodia Date: Tue, 5 May 2020 01:50:51 +0000 Subject: [PATCH 05/13] lesson 4: change sanitizer flag Error when starting fuzzer after compilation with flag -fsanitize-coverage=trace-pc-guard --- lessons/04/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lessons/04/README.md b/lessons/04/README.md index 1b23112..e0d31ac 100644 --- a/lessons/04/README.md +++ b/lessons/04/README.md @@ -34,7 +34,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { Compile the fuzzer in the following way: ```bash -clang++ -g -std=c++11 -fsanitize=address -fsanitize-coverage=trace-pc-guard \ +clang++ -g -std=c++11 -fsanitize=address,fuzzer \ first_fuzzer.cc ../../libFuzzer/libFuzzer.a \ -o first_fuzzer ``` @@ -205,7 +205,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { Compile the fuzzer: ```bash -clang++ -g -std=c++11 -fsanitize=address -fsanitize-coverage=trace-pc-guard \ +clang++ -g -std=c++11 -fsanitize=address,fuzzer \ second_fuzzer.cc ../../libFuzzer/libFuzzer.a \ -o second_fuzzer ``` @@ -256,7 +256,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { Compile the fuzzer: ```bash -clang++ -g -std=c++11 -fsanitize=address -fsanitize-coverage=trace-pc-guard \ +clang++ -g -std=c++11 -fsanitize=address,fuzzer \ third_fuzzer.cc ../../libFuzzer/libFuzzer.a \ -o third_fuzzer ``` @@ -363,7 +363,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { Compile the fuzzer: ```bash -clang++ -g -std=c++11 -fsanitize=address -fsanitize-coverage=trace-pc-guard \ +clang++ -g -std=c++11 -fsanitize=address,fuzzer \ fourth_fuzzer.cc ../../libFuzzer/libFuzzer.a \ -o fourth_fuzzer ``` From 352751dcaf3a15ff5bce365f18182405f5a96815 Mon Sep 17 00:00:00 2001 From: julianvolodia Date: Tue, 5 May 2020 21:48:31 +0000 Subject: [PATCH 06/13] lesson 5: openssl1.0.1f has unstable parallel make https://github.com/google/fuzzer-test-suite/blob/master/openssl-1.0.1f/build.sh ...but for '-j4' make work stable, so applied it to increase build speed --- lessons/05/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lessons/05/README.md b/lessons/05/README.md index 4f246cf..6fa9378 100644 --- a/lessons/05/README.md +++ b/lessons/05/README.md @@ -15,7 +15,7 @@ cd openssl1.0.1f/ ./config make clean -make CC="clang -O2 -fno-omit-frame-pointer -g -fsanitize=address -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-gep,trace-div" -j$(nproc) +make CC="clang -O2 -fno-omit-frame-pointer -g -fsanitize=address -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-gep,trace-div" -j4 ``` ### Build and run the fuzzer From e2e3866cc389e09cbd07f1c9b20d78a629e6276a Mon Sep 17 00:00:00 2001 From: julianvolodia Date: Wed, 6 May 2020 00:04:13 +0000 Subject: [PATCH 07/13] lesson 5: change sanitizer flag --- lessons/05/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lessons/05/README.md b/lessons/05/README.md index 6fa9378..8134a2f 100644 --- a/lessons/05/README.md +++ b/lessons/05/README.md @@ -15,7 +15,7 @@ cd openssl1.0.1f/ ./config make clean -make CC="clang -O2 -fno-omit-frame-pointer -g -fsanitize=address -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-gep,trace-div" -j4 +make CC="clang -O2 -fno-omit-frame-pointer -g -fsanitize=address,fuzzer-no-link -fsanitize-coverage=trace-cmp,trace-gep,trace-div" -j4 ``` ### Build and run the fuzzer @@ -71,8 +71,8 @@ Build the fuzzer: ```bash cd .. -clang++ -g openssl_fuzzer.cc -O2 -fno-omit-frame-pointer -fsanitize=address \ - -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-gep,trace-div \ +clang++ -g openssl_fuzzer.cc -O2 -fno-omit-frame-pointer -fsanitize=address,fuzzer \ + -fsanitize-coverage=trace-cmp,trace-gep,trace-div \ -Iopenssl1.0.1f/include openssl1.0.1f/libssl.a openssl1.0.1f/libcrypto.a \ ../../libFuzzer/libFuzzer.a -o openssl_fuzzer ``` From e159e73b451c87e73830e01efe8073067695a976 Mon Sep 17 00:00:00 2001 From: julianvolodia Date: Wed, 6 May 2020 01:03:07 +0000 Subject: [PATCH 08/13] lesson 5: (!) adds plot twist lib openssl build make execution must be fixed to enable fuzzing --- lessons/05/README.md | 45 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/lessons/05/README.md b/lessons/05/README.md index 8134a2f..98f8cf1 100644 --- a/lessons/05/README.md +++ b/lessons/05/README.md @@ -15,7 +15,7 @@ cd openssl1.0.1f/ ./config make clean -make CC="clang -O2 -fno-omit-frame-pointer -g -fsanitize=address,fuzzer-no-link -fsanitize-coverage=trace-cmp,trace-gep,trace-div" -j4 +make CC="clang -O2 -fno-omit-frame-pointer -g -fsanitize=address -fsanitize-coverage=trace-cmp,trace-gep,trace-div" -j4 ``` ### Build and run the fuzzer @@ -84,6 +84,49 @@ mkdir corpus1 ./openssl_fuzzer ./corpus1/ ``` +We see that nothing happens - nor new paths. That turns that library was not correctly build (without `-fsanitize=fuzzer`). +We were running dumb fuzzing, because library was not instrumentated. + +```bash +cd openssl1.0.1f/ + +make clean +make CC="clang -O2 -fno-omit-frame-pointer -g -fsanitize=address,fuzzer -fsanitize-coverage=trace-cmp,trace-gep,trace-div" -j4 +``` + +During build after enabling libFuzzer instrumentation within library build, we will see error: +``` +/usr/local/lib/clang/10.0.0/lib/linux/libclang_rt.fuzzer-x86_64.a(fuzzer.o): In function `main': +.../libfuzzer-workshop/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerMain.cpp:19: undefined reference to `LLVMFuzzerTestOneInput' +clang-10: error: linker command failed with exit code 1 (use -v to see invocation) +``` + +We should not link with standalone libFuzzer fuzzer function, because it is definied outside of library source code. +See libFuzzer code and LLVM user manual for deeper understanding. + +At the end we manage to build openssl without errors: + +```bash +make clean +make CC="clang -O2 -fno-omit-frame-pointer -g -fsanitize=address,fuzzer-no-link -fsanitize-coverage=trace-cmp,trace-gep,trace-div" -j4 +``` + +In both cases fuzzing would work neither error, but linking errors or compiler errors could prevent libraries from build. + +We are building fuzzer and try again. + +```bash +cd .. +clang++ -g openssl_fuzzer.cc -O2 -fno-omit-frame-pointer -fsanitize=address,fuzzer \ + -fsanitize-coverage=trace-cmp,trace-gep,trace-div \ + -Iopenssl1.0.1f/include openssl1.0.1f/libssl.a openssl1.0.1f/libcrypto.a \ + ../../libFuzzer/libFuzzer.a -o openssl_fuzzer + +rm -r corpus1 +mkdir corpus1 +./openssl_fuzzer ./corpus1/ +``` + After some time: ``` From 6c7812a05063a4fcc32c4827e9840d72468c047b Mon Sep 17 00:00:00 2001 From: julianvolodia Date: Wed, 6 May 2020 02:02:28 +0000 Subject: [PATCH 09/13] lesson 5-6: typo or not to typo? Need to check, because maybe it's workaround for some GitHub magic... --- lessons/05/README.md | 4 ++-- lessons/06/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lessons/05/README.md b/lessons/05/README.md index 98f8cf1..2507aea 100644 --- a/lessons/05/README.md +++ b/lessons/05/README.md @@ -3,7 +3,7 @@ Here we will find Heartbleed vulnerability (CVE-2014-0160). *** -This example has been taken from [google/fuzzer-stest-suite] repository. +This example has been taken from [google/fuzzer-test-suite] repository. *** @@ -164,4 +164,4 @@ vulnerabilities can be found in a few minutes. Fuzzing is awesome. [Heartbleed]: https://en.wikipedia.org/wiki/Heartbleed -[google/fuzzer-stest-suite]: https://github.com/google/fuzzer-test-suite/blob/master/tutorial/libFuzzerTutorial.md#heartbleed \ No newline at end of file +[google/fuzzer-test-suite]: https://github.com/google/fuzzer-test-suite/blob/master/tutorial/libFuzzerTutorial.md#heartbleed \ No newline at end of file diff --git a/lessons/06/README.md b/lessons/06/README.md index 1ff83ad..976d39d 100644 --- a/lessons/06/README.md +++ b/lessons/06/README.md @@ -4,7 +4,7 @@ Here we will find [c-ares] vulnerability (CVE-2016-5180) that has been exploited to obtain [remote code execution] with root privileges on ChromeOS. *** -This example has been taken from [google/fuzzer-stest-suite] repository. +This example has been taken from [google/fuzzer-test-suite] repository. *** @@ -93,4 +93,4 @@ WRITE of size 1 at 0x6030000470f5 thread T0 [c-ares]: https://c-ares.haxx.se/ [remote code execution]: https://googlechromereleases.blogspot.com/2016/09/stable-channel-updates-for-chrome-os.html -[google/fuzzer-stest-suite]: https://github.com/google/fuzzer-test-suite/blob/master/tutorial/libFuzzerTutorial.md#heartbleed +[google/fuzzer-test-suite]: https://github.com/google/fuzzer-test-suite/blob/master/tutorial/libFuzzerTutorial.md#heartbleed From e9c6437185baf35bd299626b220f82d67edb68f5 Mon Sep 17 00:00:00 2001 From: julianvolodia Date: Wed, 6 May 2020 03:04:53 +0000 Subject: [PATCH 10/13] lesson 5-6: add google/fuzzing reference --- lessons/05/README.md | 3 ++- lessons/06/README.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lessons/05/README.md b/lessons/05/README.md index 2507aea..0aadaf6 100644 --- a/lessons/05/README.md +++ b/lessons/05/README.md @@ -3,7 +3,7 @@ Here we will find Heartbleed vulnerability (CVE-2014-0160). *** -This example has been taken from [google/fuzzer-test-suite] repository. +This example has been taken from [google/fuzzer-test-suite] repository (tutorial moved to [google/fuzzing]). *** @@ -164,4 +164,5 @@ vulnerabilities can be found in a few minutes. Fuzzing is awesome. [Heartbleed]: https://en.wikipedia.org/wiki/Heartbleed +[google/fuzzing]: https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md#heartbleed [google/fuzzer-test-suite]: https://github.com/google/fuzzer-test-suite/blob/master/tutorial/libFuzzerTutorial.md#heartbleed \ No newline at end of file diff --git a/lessons/06/README.md b/lessons/06/README.md index 976d39d..4bcf250 100644 --- a/lessons/06/README.md +++ b/lessons/06/README.md @@ -4,7 +4,7 @@ Here we will find [c-ares] vulnerability (CVE-2016-5180) that has been exploited to obtain [remote code execution] with root privileges on ChromeOS. *** -This example has been taken from [google/fuzzer-test-suite] repository. +This example has been taken from [google/fuzzer-test-suite] repository (tutorial moved to [google/fuzzing]). *** @@ -93,4 +93,5 @@ WRITE of size 1 at 0x6030000470f5 thread T0 [c-ares]: https://c-ares.haxx.se/ [remote code execution]: https://googlechromereleases.blogspot.com/2016/09/stable-channel-updates-for-chrome-os.html +[google/fuzzing]: https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md#heartbleed [google/fuzzer-test-suite]: https://github.com/google/fuzzer-test-suite/blob/master/tutorial/libFuzzerTutorial.md#heartbleed From 576cba301ad01a29e2f494baa3a9121620fb7186 Mon Sep 17 00:00:00 2001 From: julianvolodia Date: Wed, 6 May 2020 03:07:36 +0000 Subject: [PATCH 11/13] lesson 5-6: change google/fuzzer-test-suite refs --- lessons/05/README.md | 2 +- lessons/06/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lessons/05/README.md b/lessons/05/README.md index 0aadaf6..c039320 100644 --- a/lessons/05/README.md +++ b/lessons/05/README.md @@ -165,4 +165,4 @@ vulnerabilities can be found in a few minutes. Fuzzing is awesome. [Heartbleed]: https://en.wikipedia.org/wiki/Heartbleed [google/fuzzing]: https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md#heartbleed -[google/fuzzer-test-suite]: https://github.com/google/fuzzer-test-suite/blob/master/tutorial/libFuzzerTutorial.md#heartbleed \ No newline at end of file +[google/fuzzer-test-suite]: https://github.com/google/fuzzer-test-suite/tree/master/openssl-1.0.1f \ No newline at end of file diff --git a/lessons/06/README.md b/lessons/06/README.md index 4bcf250..7e781e1 100644 --- a/lessons/06/README.md +++ b/lessons/06/README.md @@ -94,4 +94,4 @@ WRITE of size 1 at 0x6030000470f5 thread T0 [c-ares]: https://c-ares.haxx.se/ [remote code execution]: https://googlechromereleases.blogspot.com/2016/09/stable-channel-updates-for-chrome-os.html [google/fuzzing]: https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md#heartbleed -[google/fuzzer-test-suite]: https://github.com/google/fuzzer-test-suite/blob/master/tutorial/libFuzzerTutorial.md#heartbleed +[google/fuzzer-test-suite]: https://github.com/google/fuzzer-test-suite/tree/master/c-ares-CVE-2016-5180 From a2e29cf556c7b8f3966876c1a57643ffb7b7d55f Mon Sep 17 00:00:00 2001 From: julianvolodia Date: Thu, 7 May 2020 06:15:00 +0000 Subject: [PATCH 12/13] lesson 6: change sanitizer flag --- lessons/06/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lessons/06/README.md b/lessons/06/README.md index 7e781e1..05e56a8 100644 --- a/lessons/06/README.md +++ b/lessons/06/README.md @@ -15,7 +15,7 @@ tar xzvf c-ares.tgz cd c-ares ./buildconf -./configure CC="clang -O2 -fno-omit-frame-pointer -g -fsanitize=address -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-gep,trace-div" +./configure CC="clang -O2 -fno-omit-frame-pointer -g -fsanitize=address,fuzzer-no-link -fsanitize-coverage=trace-cmp,trace-gep,trace-div" make CFLAGS= ``` @@ -41,8 +41,8 @@ Build the fuzzer: ```bash cd .. -clang++ -g c_ares_fuzzer.cc -O2 -fno-omit-frame-pointer -fsanitize=address \ - -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-gep,trace-div \ +clang++ -g c_ares_fuzzer.cc -O2 -fno-omit-frame-pointer -fsanitize=address,fuzzer \ + -fsanitize-coverage=trace-cmp,trace-gep,trace-div \ -Ic-ares c-ares/.libs/libcares.a \ ../../libFuzzer/libFuzzer.a -o c_ares_fuzzer ``` From 031553066d5e44c9dfb8d814030819d4ba04e3f6 Mon Sep 17 00:00:00 2001 From: julianvolodia Date: Thu, 7 May 2020 06:15:00 +0000 Subject: [PATCH 13/13] lesson 8: change sanitizer flag --- lessons/08/README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lessons/08/README.md b/lessons/08/README.md index 24239e0..0af35e6 100644 --- a/lessons/08/README.md +++ b/lessons/08/README.md @@ -15,11 +15,11 @@ cd libxml2 ./autogen.sh -export FUZZ_CXXFLAGS="-O2 -fno-omit-frame-pointer -g -fsanitize=address \ - -fsanitize-coverage=edge,indirect-calls,trace-cmp,trace-div,trace-gep,trace-pc-guard" +export FUZZ_CXXFLAGS_NO_LINK="-O2 -fno-omit-frame-pointer -g -fsanitize=address,fuzzer-no-link \ + -fsanitize-coverage=edge,indirect-calls,trace-cmp,trace-div,trace-gep" -CXX="clang++ $FUZZ_CXXFLAGS" CC="clang $FUZZ_CXXFLAGS" \ - CCLD="clang++ $FUZZ_CXXFLAGS" ./configure +CXX="clang++ $FUZZ_CXXFLAGS_NO_LINK" CC="clang $FUZZ_CXXFLAGS_NO_LINK" \ + CCLD="clang++ $FUZZ_CXXFLAGS_NO_LINK" ./configure make -j$(nproc) ``` @@ -53,6 +53,10 @@ Then build it: ```bash cd .. + +export FUZZ_CXXFLAGS="-O2 -fno-omit-frame-pointer -g -fsanitize=address,fuzzer \ + -fsanitize-coverage=edge,indirect-calls,trace-cmp,trace-div,trace-gep" + clang++ -std=c++11 xml_read_memory_fuzzer.cc $FUZZ_CXXFLAGS -I libxml2/include \ libxml2/.libs/libxml2.a ../../libFuzzer/libFuzzer.a -lz \ -o xml_read_memory_fuzzer