From 57b32d97736d204e303be26f44ea6159c3041ad9 Mon Sep 17 00:00:00 2001 From: Andreas Smas Date: Tue, 20 Dec 2016 21:38:01 +0100 Subject: [PATCH 01/35] Add Doozer build badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 97ae6854..16c7eabf 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json) [![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json) +[![Build status](https://doozer.io/badge/nlohmann/json/buildstatus/develop)](https://doozer.io/user/nlohmann/json) [![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json) [![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json) [![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/8soFCqS532vOyZcK) From 69ed19e483681c2a64944f3b9b9351326872d350 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 09:08:50 +0100 Subject: [PATCH 02/35] :heavy_plus_sign: added libFuzzer --- .gitignore | 1 + test/fuzz_test/Fuzzer/CMakeLists.txt | 45 + test/fuzz_test/Fuzzer/FuzzerCorpus.h | 217 +++++ test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp | 52 ++ test/fuzz_test/Fuzzer/FuzzerDefs.h | 89 ++ test/fuzz_test/Fuzzer/FuzzerDictionary.h | 124 +++ test/fuzz_test/Fuzzer/FuzzerDriver.cpp | 545 ++++++++++++ test/fuzz_test/Fuzzer/FuzzerExtFunctions.def | 50 ++ test/fuzz_test/Fuzzer/FuzzerExtFunctions.h | 35 + .../Fuzzer/FuzzerExtFunctionsDlsym.cpp | 52 ++ .../Fuzzer/FuzzerExtFunctionsWeak.cpp | 53 ++ .../Fuzzer/FuzzerExtFunctionsWeakAlias.cpp | 56 ++ test/fuzz_test/Fuzzer/FuzzerFlags.def | 115 +++ test/fuzz_test/Fuzzer/FuzzerIO.cpp | 117 +++ test/fuzz_test/Fuzzer/FuzzerIO.h | 64 ++ test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp | 88 ++ test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp | 282 +++++++ test/fuzz_test/Fuzzer/FuzzerInterface.h | 67 ++ test/fuzz_test/Fuzzer/FuzzerInternal.h | 182 ++++ test/fuzz_test/Fuzzer/FuzzerLoop.cpp | 792 ++++++++++++++++++ test/fuzz_test/Fuzzer/FuzzerMain.cpp | 21 + test/fuzz_test/Fuzzer/FuzzerMerge.cpp | 261 ++++++ test/fuzz_test/Fuzzer/FuzzerMerge.h | 70 ++ test/fuzz_test/Fuzzer/FuzzerMutate.cpp | 527 ++++++++++++ test/fuzz_test/Fuzzer/FuzzerMutate.h | 145 ++++ test/fuzz_test/Fuzzer/FuzzerOptions.h | 68 ++ test/fuzz_test/Fuzzer/FuzzerRandom.h | 36 + test/fuzz_test/Fuzzer/FuzzerSHA1.cpp | 222 +++++ test/fuzz_test/Fuzzer/FuzzerSHA1.h | 33 + test/fuzz_test/Fuzzer/FuzzerTracePC.cpp | 339 ++++++++ test/fuzz_test/Fuzzer/FuzzerTracePC.h | 158 ++++ test/fuzz_test/Fuzzer/FuzzerTraceState.cpp | 325 +++++++ test/fuzz_test/Fuzzer/FuzzerUtil.cpp | 218 +++++ test/fuzz_test/Fuzzer/FuzzerUtil.h | 72 ++ test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp | 152 ++++ test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp | 24 + test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp | 117 +++ test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp | 182 ++++ test/fuzz_test/Fuzzer/FuzzerValueBitMap.h | 87 ++ test/fuzz_test/Fuzzer/README.txt | 2 + test/fuzz_test/Fuzzer/afl/afl_driver.cpp | 295 +++++++ test/fuzz_test/Fuzzer/build.sh | 10 + test/fuzz_test/Fuzzer/cxx.dict | 122 +++ .../standalone/StandaloneFuzzTargetMain.c | 41 + test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp | 22 + .../Fuzzer/test/AbsNegAndConstant64Test.cpp | 23 + .../Fuzzer/test/AbsNegAndConstantTest.cpp | 23 + .../Fuzzer/test/AccumulateAllocationsTest.cpp | 17 + .../Fuzzer/test/BufferOverflowOnInput.cpp | 23 + test/fuzz_test/Fuzzer/test/CMakeLists.txt | 217 +++++ .../Fuzzer/test/CallerCalleeTest.cpp | 59 ++ test/fuzz_test/Fuzzer/test/CounterTest.cpp | 18 + .../Fuzzer/test/CustomCrossOverTest.cpp | 63 ++ .../Fuzzer/test/CustomMutatorTest.cpp | 38 + test/fuzz_test/Fuzzer/test/DSO1.cpp | 12 + test/fuzz_test/Fuzzer/test/DSO2.cpp | 12 + test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp | 11 + test/fuzz_test/Fuzzer/test/DSOTestMain.cpp | 31 + test/fuzz_test/Fuzzer/test/DivTest.cpp | 20 + test/fuzz_test/Fuzzer/test/EmptyTest.cpp | 11 + .../test/FourIndependentBranchesTest.cpp | 22 + .../Fuzzer/test/FullCoverageSetTest.cpp | 24 + test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp | 738 ++++++++++++++++ test/fuzz_test/Fuzzer/test/InitializeTest.cpp | 28 + test/fuzz_test/Fuzzer/test/LeakTest.cpp | 17 + .../fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp | 17 + test/fuzz_test/Fuzzer/test/LoadTest.cpp | 22 + test/fuzz_test/Fuzzer/test/MemcmpTest.cpp | 31 + .../fuzz_test/Fuzzer/test/NthRunCrashTest.cpp | 18 + .../Fuzzer/test/NullDerefOnEmptyTest.cpp | 19 + test/fuzz_test/Fuzzer/test/NullDerefTest.cpp | 26 + .../Fuzzer/test/OneHugeAllocTest.cpp | 28 + .../test/OutOfMemorySingleLargeMallocTest.cpp | 27 + .../fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp | 31 + .../Fuzzer/test/RepeatedBytesTest.cpp | 29 + test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp | 22 + .../Fuzzer/test/ShrinkControlFlowTest.cpp | 28 + .../Fuzzer/test/ShrinkValueProfileTest.cpp | 22 + .../Fuzzer/test/SignedIntOverflowTest.cpp | 28 + test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp | 46 + .../Fuzzer/test/SimpleDictionaryTest.cpp | 29 + test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp | 40 + test/fuzz_test/Fuzzer/test/SimpleTest.cpp | 27 + .../Fuzzer/test/SimpleThreadedTest.cpp | 25 + .../Fuzzer/test/SingleMemcmpTest.cpp | 17 + .../Fuzzer/test/SingleStrcmpTest.cpp | 17 + .../Fuzzer/test/SingleStrncmpTest.cpp | 17 + test/fuzz_test/Fuzzer/test/SpamyTest.cpp | 21 + test/fuzz_test/Fuzzer/test/StrcmpTest.cpp | 32 + test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp | 21 + test/fuzz_test/Fuzzer/test/StrncmpTest.cpp | 28 + test/fuzz_test/Fuzzer/test/StrstrTest.cpp | 28 + test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp | 34 + test/fuzz_test/Fuzzer/test/Switch2Test.cpp | 35 + test/fuzz_test/Fuzzer/test/SwitchTest.cpp | 58 ++ .../Fuzzer/test/ThreadedLeakTest.cpp | 18 + test/fuzz_test/Fuzzer/test/ThreadedTest.cpp | 26 + .../Fuzzer/test/TimeoutEmptyTest.cpp | 14 + test/fuzz_test/Fuzzer/test/TimeoutTest.cpp | 26 + .../fuzz_test/Fuzzer/test/TraceMallocTest.cpp | 27 + .../Fuzzer/test/UninstrumentedTest.cpp | 11 + .../Fuzzer/test/afl-driver-extra-stats.test | 28 + .../Fuzzer/test/afl-driver-stderr.test | 10 + test/fuzz_test/Fuzzer/test/caller-callee.test | 2 + test/fuzz_test/Fuzzer/test/coverage.test | 19 + test/fuzz_test/Fuzzer/test/dict1.txt | 4 + test/fuzz_test/Fuzzer/test/dump_coverage.test | 16 + .../Fuzzer/test/fuzzer-customcrossover.test | 10 + .../Fuzzer/test/fuzzer-custommutator.test | 4 + test/fuzz_test/Fuzzer/test/fuzzer-dict.test | 6 + test/fuzz_test/Fuzzer/test/fuzzer-dirs.test | 15 + test/fuzz_test/Fuzzer/test/fuzzer-fdmask.test | 30 + .../Fuzzer/test/fuzzer-finalstats.test | 11 + test/fuzz_test/Fuzzer/test/fuzzer-flags.test | 10 + test/fuzz_test/Fuzzer/test/fuzzer-jobs.test | 29 + test/fuzz_test/Fuzzer/test/fuzzer-leak.test | 35 + .../Fuzzer/test/fuzzer-oom-with-profile.test | 6 + test/fuzz_test/Fuzzer/test/fuzzer-oom.test | 11 + .../Fuzzer/test/fuzzer-printcovpcs.test | 8 + test/fuzz_test/Fuzzer/test/fuzzer-runs.test | 8 + test/fuzz_test/Fuzzer/test/fuzzer-seed.test | 3 + test/fuzz_test/Fuzzer/test/fuzzer-segv.test | 5 + .../Fuzzer/test/fuzzer-singleinputs.test | 16 + .../Fuzzer/test/fuzzer-threaded.test | 7 + .../fuzz_test/Fuzzer/test/fuzzer-timeout.test | 19 + .../Fuzzer/test/fuzzer-traces-hooks.test | 25 + test/fuzz_test/Fuzzer/test/fuzzer-ubsan.test | 4 + test/fuzz_test/Fuzzer/test/fuzzer.test | 57 ++ test/fuzz_test/Fuzzer/test/hi.txt | 1 + test/fuzz_test/Fuzzer/test/lit.cfg | 29 + test/fuzz_test/Fuzzer/test/lit.site.cfg.in | 4 + test/fuzz_test/Fuzzer/test/merge.test | 46 + .../fuzz_test/Fuzzer/test/minimize_crash.test | 6 + .../Fuzzer/test/no-coverage/CMakeLists.txt | 29 + .../fuzz_test/Fuzzer/test/repeated-bytes.test | 2 + test/fuzz_test/Fuzzer/test/shrink.test | 7 + test/fuzz_test/Fuzzer/test/simple-cmp.test | 2 + test/fuzz_test/Fuzzer/test/standalone.test | 4 + test/fuzz_test/Fuzzer/test/swap-cmp.test | 2 + test/fuzz_test/Fuzzer/test/trace-malloc.test | 10 + .../Fuzzer/test/ubsan/CMakeLists.txt | 15 + test/fuzz_test/Fuzzer/test/ulimit.test | 2 + .../Fuzzer/test/uninstrumented/CMakeLists.txt | 16 + test/fuzz_test/Fuzzer/test/unit/lit.cfg | 7 + .../Fuzzer/test/unit/lit.site.cfg.in | 2 + .../Fuzzer/test/value-profile-cmp.test | 2 + .../Fuzzer/test/value-profile-cmp2.test | 2 + .../Fuzzer/test/value-profile-cmp3.test | 2 + .../Fuzzer/test/value-profile-cmp4.test | 2 + .../Fuzzer/test/value-profile-div.test | 3 + .../Fuzzer/test/value-profile-load.test | 3 + .../Fuzzer/test/value-profile-mem.test | 2 + .../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 + 156 files changed, 9535 insertions(+) create mode 100644 test/fuzz_test/Fuzzer/CMakeLists.txt create mode 100644 test/fuzz_test/Fuzzer/FuzzerCorpus.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerDefs.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerDictionary.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerDriver.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerExtFunctions.def create mode 100644 test/fuzz_test/Fuzzer/FuzzerExtFunctions.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerExtFunctionsDlsym.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeak.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerFlags.def create mode 100644 test/fuzz_test/Fuzzer/FuzzerIO.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerIO.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerInterface.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerInternal.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerLoop.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerMain.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerMerge.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerMerge.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerMutate.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerMutate.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerOptions.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerRandom.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerSHA1.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerSHA1.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerTracePC.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerTracePC.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerTraceState.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerUtil.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerUtil.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerValueBitMap.h create mode 100644 test/fuzz_test/Fuzzer/README.txt create mode 100644 test/fuzz_test/Fuzzer/afl/afl_driver.cpp create mode 100755 test/fuzz_test/Fuzzer/build.sh create mode 100644 test/fuzz_test/Fuzzer/cxx.dict create mode 100644 test/fuzz_test/Fuzzer/standalone/StandaloneFuzzTargetMain.c create mode 100644 test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/AbsNegAndConstant64Test.cpp create mode 100644 test/fuzz_test/Fuzzer/test/AbsNegAndConstantTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/AccumulateAllocationsTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/BufferOverflowOnInput.cpp create mode 100644 test/fuzz_test/Fuzzer/test/CMakeLists.txt create mode 100644 test/fuzz_test/Fuzzer/test/CallerCalleeTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/CounterTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/CustomCrossOverTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/CustomMutatorTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/DSO1.cpp create mode 100644 test/fuzz_test/Fuzzer/test/DSO2.cpp create mode 100644 test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp create mode 100644 test/fuzz_test/Fuzzer/test/DSOTestMain.cpp create mode 100644 test/fuzz_test/Fuzzer/test/DivTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/EmptyTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/FourIndependentBranchesTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/FullCoverageSetTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/InitializeTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/LeakTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/LoadTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/MemcmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/NthRunCrashTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/NullDerefOnEmptyTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/NullDerefTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/OneHugeAllocTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/RepeatedBytesTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp create mode 100644 test/fuzz_test/Fuzzer/test/ShrinkControlFlowTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/ShrinkValueProfileTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SignedIntOverflowTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SimpleDictionaryTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SimpleTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SimpleThreadedTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SingleMemcmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SingleStrcmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SingleStrncmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SpamyTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/StrcmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/StrncmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/StrstrTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/Switch2Test.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SwitchTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/ThreadedLeakTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/ThreadedTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/TimeoutEmptyTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/TimeoutTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/TraceMallocTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/UninstrumentedTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/afl-driver-extra-stats.test create mode 100644 test/fuzz_test/Fuzzer/test/afl-driver-stderr.test create mode 100644 test/fuzz_test/Fuzzer/test/caller-callee.test create mode 100644 test/fuzz_test/Fuzzer/test/coverage.test create mode 100644 test/fuzz_test/Fuzzer/test/dict1.txt create mode 100644 test/fuzz_test/Fuzzer/test/dump_coverage.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-customcrossover.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-custommutator.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-dict.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-dirs.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-fdmask.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-finalstats.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-flags.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-jobs.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-leak.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-oom-with-profile.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-oom.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-printcovpcs.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-runs.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-seed.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-segv.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-singleinputs.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-threaded.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-timeout.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-traces-hooks.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-ubsan.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer.test create mode 100644 test/fuzz_test/Fuzzer/test/hi.txt create mode 100644 test/fuzz_test/Fuzzer/test/lit.cfg create mode 100644 test/fuzz_test/Fuzzer/test/lit.site.cfg.in create mode 100644 test/fuzz_test/Fuzzer/test/merge.test create mode 100644 test/fuzz_test/Fuzzer/test/minimize_crash.test create mode 100644 test/fuzz_test/Fuzzer/test/no-coverage/CMakeLists.txt create mode 100644 test/fuzz_test/Fuzzer/test/repeated-bytes.test create mode 100644 test/fuzz_test/Fuzzer/test/shrink.test create mode 100644 test/fuzz_test/Fuzzer/test/simple-cmp.test create mode 100644 test/fuzz_test/Fuzzer/test/standalone.test create mode 100644 test/fuzz_test/Fuzzer/test/swap-cmp.test create mode 100644 test/fuzz_test/Fuzzer/test/trace-malloc.test create mode 100644 test/fuzz_test/Fuzzer/test/ubsan/CMakeLists.txt create mode 100644 test/fuzz_test/Fuzzer/test/ulimit.test create mode 100644 test/fuzz_test/Fuzzer/test/uninstrumented/CMakeLists.txt create mode 100644 test/fuzz_test/Fuzzer/test/unit/lit.cfg create mode 100644 test/fuzz_test/Fuzzer/test/unit/lit.site.cfg.in create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-cmp.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-cmp2.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-cmp3.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-cmp4.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-div.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-load.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-mem.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-set.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-strcmp.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-strncmp.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-switch.test diff --git a/.gitignore b/.gitignore index 42a18e82..099cad5c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ cmake-build-debug test/test-* +test/fuzz_test/Fuzzer/.svn diff --git a/test/fuzz_test/Fuzzer/CMakeLists.txt b/test/fuzz_test/Fuzzer/CMakeLists.txt new file mode 100644 index 00000000..70bd017b --- /dev/null +++ b/test/fuzz_test/Fuzzer/CMakeLists.txt @@ -0,0 +1,45 @@ +set(LIBFUZZER_FLAGS_BASE "${CMAKE_CXX_FLAGS}") +# Disable the coverage and sanitizer instrumentation for the fuzzer itself. +set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fno-sanitize-coverage=trace-pc-guard,edge,trace-cmp,indirect-calls,8bit-counters -Werror") +if( LLVM_USE_SANITIZE_COVERAGE ) + if(NOT "${LLVM_USE_SANITIZER}" STREQUAL "Address") + message(FATAL_ERROR + "LibFuzzer and its tests require LLVM_USE_SANITIZER=Address and " + "LLVM_USE_SANITIZE_COVERAGE=YES to be set." + ) + endif() + add_library(LLVMFuzzerNoMainObjects OBJECT + FuzzerCrossOver.cpp + FuzzerDriver.cpp + FuzzerExtFunctionsDlsym.cpp + FuzzerExtFunctionsWeak.cpp + FuzzerExtFunctionsWeakAlias.cpp + FuzzerIO.cpp + FuzzerIOPosix.cpp + FuzzerIOWindows.cpp + FuzzerLoop.cpp + FuzzerMerge.cpp + FuzzerMutate.cpp + FuzzerSHA1.cpp + FuzzerTracePC.cpp + FuzzerTraceState.cpp + FuzzerUtil.cpp + FuzzerUtilDarwin.cpp + FuzzerUtilLinux.cpp + FuzzerUtilPosix.cpp + FuzzerUtilWindows.cpp + ) + add_library(LLVMFuzzerNoMain STATIC + $ + ) + target_link_libraries(LLVMFuzzerNoMain ${PTHREAD_LIB}) + add_library(LLVMFuzzer STATIC + FuzzerMain.cpp + $ + ) + target_link_libraries(LLVMFuzzer ${PTHREAD_LIB}) + + if( LLVM_INCLUDE_TESTS ) + add_subdirectory(test) + endif() +endif() diff --git a/test/fuzz_test/Fuzzer/FuzzerCorpus.h b/test/fuzz_test/Fuzzer/FuzzerCorpus.h new file mode 100644 index 00000000..663c5854 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerCorpus.h @@ -0,0 +1,217 @@ +//===- FuzzerCorpus.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::InputCorpus +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_CORPUS +#define LLVM_FUZZER_CORPUS + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" +#include "FuzzerRandom.h" +#include "FuzzerSHA1.h" +#include "FuzzerTracePC.h" +#include +#include +#include + +namespace fuzzer { + +struct InputInfo { + Unit U; // The actual input data. + uint8_t Sha1[kSHA1NumBytes]; // Checksum. + // Number of features that this input has and no smaller input has. + size_t NumFeatures = 0; + size_t Tmp = 0; // Used by ValidateFeatureSet. + // Stats. + size_t NumExecutedMutations = 0; + size_t NumSuccessfullMutations = 0; + bool MayDeleteFile = false; +}; + +class InputCorpus { + public: + static const size_t kFeatureSetSize = 1 << 16; + InputCorpus(const std::string &OutputCorpus) : OutputCorpus(OutputCorpus) { + memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature)); + memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature)); + } + ~InputCorpus() { + for (auto II : Inputs) + delete II; + } + size_t size() const { return Inputs.size(); } + size_t SizeInBytes() const { + size_t Res = 0; + for (auto II : Inputs) + Res += II->U.size(); + return Res; + } + size_t NumActiveUnits() const { + size_t Res = 0; + for (auto II : Inputs) + Res += !II->U.empty(); + return Res; + } + bool empty() const { return Inputs.empty(); } + const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; } + void AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile = false) { + assert(!U.empty()); + uint8_t Hash[kSHA1NumBytes]; + if (FeatureDebug) + Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures); + ComputeSHA1(U.data(), U.size(), Hash); + Hashes.insert(Sha1ToString(Hash)); + Inputs.push_back(new InputInfo()); + InputInfo &II = *Inputs.back(); + II.U = U; + II.NumFeatures = NumFeatures; + II.MayDeleteFile = MayDeleteFile; + memcpy(II.Sha1, Hash, kSHA1NumBytes); + UpdateCorpusDistribution(); + ValidateFeatureSet(); + } + + bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); } + bool HasUnit(const std::string &H) { return Hashes.count(H); } + InputInfo &ChooseUnitToMutate(Random &Rand) { + InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)]; + assert(!II.U.empty()); + return II; + }; + + // Returns an index of random unit from the corpus to mutate. + // Hypothesis: units added to the corpus last are more likely to be + // interesting. This function gives more weight to the more recent units. + size_t ChooseUnitIdxToMutate(Random &Rand) { + size_t Idx = static_cast(CorpusDistribution(Rand.Get_mt19937())); + assert(Idx < Inputs.size()); + return Idx; + } + + void PrintStats() { + for (size_t i = 0; i < Inputs.size(); i++) { + const auto &II = *Inputs[i]; + Printf(" [%zd %s]\tsz: %zd\truns: %zd\tsucc: %zd\n", i, + Sha1ToString(II.Sha1).c_str(), II.U.size(), + II.NumExecutedMutations, II.NumSuccessfullMutations); + } + } + + void PrintFeatureSet() { + for (size_t i = 0; i < kFeatureSetSize; i++) { + if(size_t Sz = GetFeature(i)) + Printf("[%zd: id %zd sz%zd] ", i, SmallestElementPerFeature[i], Sz); + } + Printf("\n\t"); + for (size_t i = 0; i < Inputs.size(); i++) + if (size_t N = Inputs[i]->NumFeatures) + Printf(" %zd=>%zd ", i, N); + Printf("\n"); + } + + void DeleteInput(size_t Idx) { + InputInfo &II = *Inputs[Idx]; + if (!OutputCorpus.empty() && II.MayDeleteFile) + RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1))); + Unit().swap(II.U); + if (FeatureDebug) + Printf("EVICTED %zd\n", Idx); + } + + bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) { + assert(NewSize); + Idx = Idx % kFeatureSetSize; + uint32_t OldSize = GetFeature(Idx); + if (OldSize == 0 || (Shrink && OldSize > NewSize)) { + if (OldSize > 0) { + size_t OldIdx = SmallestElementPerFeature[Idx]; + InputInfo &II = *Inputs[OldIdx]; + assert(II.NumFeatures > 0); + II.NumFeatures--; + if (II.NumFeatures == 0) + DeleteInput(OldIdx); + } + if (FeatureDebug) + Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize); + SmallestElementPerFeature[Idx] = Inputs.size(); + InputSizesPerFeature[Idx] = NewSize; + CountingFeatures = true; + return true; + } + return false; + } + + size_t NumFeatures() const { + size_t Res = 0; + for (size_t i = 0; i < kFeatureSetSize; i++) + Res += GetFeature(i) != 0; + return Res; + } + + void ResetFeatureSet() { + assert(Inputs.empty()); + memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature)); + memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature)); + } + +private: + + static const bool FeatureDebug = false; + + size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; } + + void ValidateFeatureSet() { + if (!CountingFeatures) return; + if (FeatureDebug) + PrintFeatureSet(); + for (size_t Idx = 0; Idx < kFeatureSetSize; Idx++) + if (GetFeature(Idx)) + Inputs[SmallestElementPerFeature[Idx]]->Tmp++; + for (auto II: Inputs) { + if (II->Tmp != II->NumFeatures) + Printf("ZZZ %zd %zd\n", II->Tmp, II->NumFeatures); + assert(II->Tmp == II->NumFeatures); + II->Tmp = 0; + } + } + + // Updates the probability distribution for the units in the corpus. + // Must be called whenever the corpus or unit weights are changed. + void UpdateCorpusDistribution() { + size_t N = Inputs.size(); + Intervals.resize(N + 1); + Weights.resize(N); + std::iota(Intervals.begin(), Intervals.end(), 0); + if (CountingFeatures) + for (size_t i = 0; i < N; i++) + Weights[i] = Inputs[i]->NumFeatures * (i + 1); + else + std::iota(Weights.begin(), Weights.end(), 1); + CorpusDistribution = std::piecewise_constant_distribution( + Intervals.begin(), Intervals.end(), Weights.begin()); + } + std::piecewise_constant_distribution CorpusDistribution; + + std::vector Intervals; + std::vector Weights; + + std::unordered_set Hashes; + std::vector Inputs; + + bool CountingFeatures = false; + uint32_t InputSizesPerFeature[kFeatureSetSize]; + uint32_t SmallestElementPerFeature[kFeatureSetSize]; + + std::string OutputCorpus; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_CORPUS diff --git a/test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp b/test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp new file mode 100644 index 00000000..8b0fd7d5 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp @@ -0,0 +1,52 @@ +//===- FuzzerCrossOver.cpp - Cross over two test inputs -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Cross over test inputs. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include + +namespace fuzzer { + +// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out. +size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize) { + assert(Size1 || Size2); + MaxOutSize = Rand(MaxOutSize) + 1; + size_t OutPos = 0; + size_t Pos1 = 0; + size_t Pos2 = 0; + size_t *InPos = &Pos1; + size_t InSize = Size1; + const uint8_t *Data = Data1; + bool CurrentlyUsingFirstData = true; + while (OutPos < MaxOutSize && (Pos1 < Size1 || Pos2 < Size2)) { + // Merge a part of Data into Out. + size_t OutSizeLeft = MaxOutSize - OutPos; + if (*InPos < InSize) { + size_t InSizeLeft = InSize - *InPos; + size_t MaxExtraSize = std::min(OutSizeLeft, InSizeLeft); + size_t ExtraSize = Rand(MaxExtraSize) + 1; + memcpy(Out + OutPos, Data + *InPos, ExtraSize); + OutPos += ExtraSize; + (*InPos) += ExtraSize; + } + // Use the other input data on the next iteration. + InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1; + InSize = CurrentlyUsingFirstData ? Size2 : Size1; + Data = CurrentlyUsingFirstData ? Data2 : Data1; + CurrentlyUsingFirstData = !CurrentlyUsingFirstData; + } + return OutPos; +} + +} // namespace fuzzer diff --git a/test/fuzz_test/Fuzzer/FuzzerDefs.h b/test/fuzz_test/Fuzzer/FuzzerDefs.h new file mode 100644 index 00000000..0f5b8a7c --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerDefs.h @@ -0,0 +1,89 @@ +//===- FuzzerDefs.h - Internal header for the Fuzzer ------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Basic definitions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DEFS_H +#define LLVM_FUZZER_DEFS_H + +#include +#include +#include +#include +#include +#include + +// Platform detection. +#ifdef __linux__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_LINUX 1 +#define LIBFUZZER_WINDOWS 0 +#elif __APPLE__ +#define LIBFUZZER_APPLE 1 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_WINDOWS 0 +#elif _WIN32 +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_WINDOWS 1 +#else +#error "Support for your platform has not been implemented" +#endif + +#define LIBFUZZER_POSIX LIBFUZZER_APPLE || LIBFUZZER_LINUX + +#ifdef __x86_64 +#define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt"))) +#else +#define ATTRIBUTE_TARGET_POPCNT +#endif + + +#ifdef __clang__ // avoid gcc warning. +# define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +#else +# define ATTRIBUTE_NO_SANITIZE_MEMORY +#endif + +namespace fuzzer { + +template T Min(T a, T b) { return a < b ? a : b; } +template T Max(T a, T b) { return a > b ? a : b; } + +class Random; +class Dictionary; +class DictionaryEntry; +class MutationDispatcher; +struct FuzzingOptions; +class InputCorpus; +struct InputInfo; +struct ExternalFunctions; + +// Global interface to functions that may or may not be available. +extern ExternalFunctions *EF; + +typedef std::vector Unit; +typedef std::vector UnitVector; +typedef int (*UserCallback)(const uint8_t *Data, size_t Size); + +int FuzzerDriver(int *argc, char ***argv, UserCallback Callback); + +struct ScopedDoingMyOwnMemmem { + ScopedDoingMyOwnMemmem(); + ~ScopedDoingMyOwnMemmem(); +}; + +inline uint8_t Bswap(uint8_t x) { return x; } +inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); } +inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); } +inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); } + +} // namespace fuzzer + +#endif // LLVM_FUZZER_DEFS_H diff --git a/test/fuzz_test/Fuzzer/FuzzerDictionary.h b/test/fuzz_test/Fuzzer/FuzzerDictionary.h new file mode 100644 index 00000000..eba0eabb --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerDictionary.h @@ -0,0 +1,124 @@ +//===- FuzzerDictionary.h - Internal header for the Fuzzer ------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::Dictionary +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DICTIONARY_H +#define LLVM_FUZZER_DICTIONARY_H + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" +#include "FuzzerUtil.h" +#include +#include + +namespace fuzzer { +// A simple POD sized array of bytes. +template class FixedWord { +public: + FixedWord() {} + FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); } + + void Set(const uint8_t *B, uint8_t S) { + assert(S <= kMaxSize); + memcpy(Data, B, S); + Size = S; + } + + bool operator==(const FixedWord &w) const { + 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; } + +private: + uint8_t Size = 0; + uint8_t Data[kMaxSize]; +}; + +typedef FixedWord<27> Word; // 28 bytes. + +class DictionaryEntry { + public: + DictionaryEntry() {} + DictionaryEntry(Word W) : W(W) {} + DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {} + const Word &GetW() const { return W; } + + bool HasPositionHint() const { return PositionHint != std::numeric_limits::max(); } + size_t GetPositionHint() const { + assert(HasPositionHint()); + return PositionHint; + } + void IncUseCount() { UseCount++; } + void IncSuccessCount() { SuccessCount++; } + size_t GetUseCount() const { return UseCount; } + size_t GetSuccessCount() const {return SuccessCount; } + + void Print(const char *PrintAfter = "\n") { + PrintASCII(W.data(), W.size()); + if (HasPositionHint()) + Printf("@%zd", GetPositionHint()); + Printf("%s", PrintAfter); + } + +private: + Word W; + size_t PositionHint = std::numeric_limits::max(); + size_t UseCount = 0; + size_t SuccessCount = 0; +}; + +class Dictionary { + public: + static const size_t kMaxDictSize = 1 << 14; + + bool ContainsWord(const Word &W) const { + return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) { + return DE.GetW() == W; + }); + } + const DictionaryEntry *begin() const { return &DE[0]; } + const DictionaryEntry *end() const { return begin() + Size; } + DictionaryEntry & operator[] (size_t Idx) { + assert(Idx < Size); + return DE[Idx]; + } + void push_back(DictionaryEntry DE) { + if (Size < kMaxDictSize) + this->DE[Size++] = DE; + } + void clear() { Size = 0; } + bool empty() const { return Size == 0; } + size_t size() const { return Size; } + +private: + DictionaryEntry DE[kMaxDictSize]; + size_t Size = 0; +}; + +// Parses one dictionary entry. +// If successfull, write the enty to Unit and returns true, +// otherwise returns false. +bool ParseOneDictionaryEntry(const std::string &Str, Unit *U); +// Parses the dictionary file, fills Units, returns true iff all lines +// were parsed succesfully. +bool ParseDictionaryFile(const std::string &Text, std::vector *Units); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_DICTIONARY_H diff --git a/test/fuzz_test/Fuzzer/FuzzerDriver.cpp b/test/fuzz_test/Fuzzer/FuzzerDriver.cpp new file mode 100644 index 00000000..95b0721c --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerDriver.cpp @@ -0,0 +1,545 @@ +//===- FuzzerDriver.cpp - FuzzerDriver function and flags -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// FuzzerDriver and flag parsing. +//===----------------------------------------------------------------------===// + +#include "FuzzerCorpus.h" +#include "FuzzerInterface.h" +#include "FuzzerInternal.h" +#include "FuzzerIO.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include +#include +#include +#include + +// This function should be present in the libFuzzer so that the client +// binary can test for its existence. +extern "C" __attribute__((used)) void __libfuzzer_is_present() {} + +namespace fuzzer { + +// Program arguments. +struct FlagDescription { + const char *Name; + const char *Description; + int Default; + int *IntFlag; + const char **StrFlag; + unsigned int *UIntFlag; +}; + +struct { +#define FUZZER_DEPRECATED_FLAG(Name) +#define FUZZER_FLAG_INT(Name, Default, Description) int Name; +#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) unsigned int Name; +#define FUZZER_FLAG_STRING(Name, Description) const char *Name; +#include "FuzzerFlags.def" +#undef FUZZER_DEPRECATED_FLAG +#undef FUZZER_FLAG_INT +#undef FUZZER_FLAG_UNSIGNED +#undef FUZZER_FLAG_STRING +} Flags; + +static const FlagDescription FlagDescriptions [] { +#define FUZZER_DEPRECATED_FLAG(Name) \ + {#Name, "Deprecated; don't use", 0, nullptr, nullptr, nullptr}, +#define FUZZER_FLAG_INT(Name, Default, Description) \ + {#Name, Description, Default, &Flags.Name, nullptr, nullptr}, +#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) \ + {#Name, Description, static_cast(Default), \ + nullptr, nullptr, &Flags.Name}, +#define FUZZER_FLAG_STRING(Name, Description) \ + {#Name, Description, 0, nullptr, &Flags.Name, nullptr}, +#include "FuzzerFlags.def" +#undef FUZZER_DEPRECATED_FLAG +#undef FUZZER_FLAG_INT +#undef FUZZER_FLAG_UNSIGNED +#undef FUZZER_FLAG_STRING +}; + +static const size_t kNumFlags = + sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]); + +static std::vector *Inputs; +static std::string *ProgName; + +static void PrintHelp() { + Printf("Usage:\n"); + auto Prog = ProgName->c_str(); + Printf("\nTo run fuzzing pass 0 or more directories.\n"); + Printf("%s [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n", Prog); + + Printf("\nTo run individual tests without fuzzing pass 1 or more files:\n"); + Printf("%s [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n", Prog); + + Printf("\nFlags: (strictly in form -flag=value)\n"); + size_t MaxFlagLen = 0; + for (size_t F = 0; F < kNumFlags; F++) + MaxFlagLen = std::max(strlen(FlagDescriptions[F].Name), MaxFlagLen); + + for (size_t F = 0; F < kNumFlags; F++) { + const auto &D = FlagDescriptions[F]; + if (strstr(D.Description, "internal flag") == D.Description) continue; + Printf(" %s", D.Name); + for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++) + Printf(" "); + Printf("\t"); + Printf("%d\t%s\n", D.Default, D.Description); + } + Printf("\nFlags starting with '--' will be ignored and " + "will be passed verbatim to subprocesses.\n"); +} + +static const char *FlagValue(const char *Param, const char *Name) { + size_t Len = strlen(Name); + if (Param[0] == '-' && strstr(Param + 1, Name) == Param + 1 && + Param[Len + 1] == '=') + return &Param[Len + 2]; + return nullptr; +} + +// Avoid calling stol as it triggers a bug in clang/glibc build. +static long MyStol(const char *Str) { + long Res = 0; + long Sign = 1; + if (*Str == '-') { + Str++; + Sign = -1; + } + for (size_t i = 0; Str[i]; i++) { + char Ch = Str[i]; + if (Ch < '0' || Ch > '9') + return Res; + Res = Res * 10 + (Ch - '0'); + } + return Res * Sign; +} + +static bool ParseOneFlag(const char *Param) { + if (Param[0] != '-') return false; + if (Param[1] == '-') { + static bool PrintedWarning = false; + if (!PrintedWarning) { + PrintedWarning = true; + Printf("INFO: libFuzzer ignores flags that start with '--'\n"); + } + for (size_t F = 0; F < kNumFlags; F++) + if (FlagValue(Param + 1, FlagDescriptions[F].Name)) + Printf("WARNING: did you mean '%s' (single dash)?\n", Param + 1); + return true; + } + for (size_t F = 0; F < kNumFlags; F++) { + const char *Name = FlagDescriptions[F].Name; + const char *Str = FlagValue(Param, Name); + if (Str) { + if (FlagDescriptions[F].IntFlag) { + int Val = MyStol(Str); + *FlagDescriptions[F].IntFlag = Val; + if (Flags.verbosity >= 2) + Printf("Flag: %s %d\n", Name, Val);; + return true; + } else if (FlagDescriptions[F].UIntFlag) { + unsigned int Val = std::stoul(Str); + *FlagDescriptions[F].UIntFlag = Val; + if (Flags.verbosity >= 2) + Printf("Flag: %s %u\n", Name, Val); + return true; + } else if (FlagDescriptions[F].StrFlag) { + *FlagDescriptions[F].StrFlag = Str; + if (Flags.verbosity >= 2) + Printf("Flag: %s %s\n", Name, Str); + return true; + } else { // Deprecated flag. + Printf("Flag: %s: deprecated, don't use\n", Name); + return true; + } + } + } + Printf("\n\nWARNING: unrecognized flag '%s'; " + "use -help=1 to list all flags\n\n", Param); + return true; +} + +// We don't use any library to minimize dependencies. +static void ParseFlags(const std::vector &Args) { + for (size_t F = 0; F < kNumFlags; F++) { + if (FlagDescriptions[F].IntFlag) + *FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default; + if (FlagDescriptions[F].UIntFlag) + *FlagDescriptions[F].UIntFlag = + static_cast(FlagDescriptions[F].Default); + if (FlagDescriptions[F].StrFlag) + *FlagDescriptions[F].StrFlag = nullptr; + } + Inputs = new std::vector; + for (size_t A = 1; A < Args.size(); A++) { + if (ParseOneFlag(Args[A].c_str())) continue; + Inputs->push_back(Args[A]); + } +} + +static std::mutex Mu; + +static void PulseThread() { + while (true) { + SleepSeconds(600); + std::lock_guard Lock(Mu); + Printf("pulse...\n"); + } +} + +static void WorkerThread(const std::string &Cmd, 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); + if (ExitCode != 0) + *HasErrors = true; + std::lock_guard Lock(Mu); + Printf("================== Job %u exited with exit code %d ============\n", + C, ExitCode); + fuzzer::CopyFileToErr(Log); + } +} + +std::string CloneArgsWithoutX(const std::vector &Args, + const char *X1, const char *X2) { + std::string Cmd; + for (auto &S : Args) { + if (FlagValue(S.c_str(), X1) || FlagValue(S.c_str(), X2)) + continue; + Cmd += S + " "; + } + return Cmd; +} + +static int RunInMultipleProcesses(const std::vector &Args, + unsigned NumWorkers, unsigned NumJobs) { + std::atomic Counter(0); + std::atomic HasErrors(false); + std::string Cmd = CloneArgsWithoutX(Args, "jobs", "workers"); + std::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)); + for (auto &T : V) + T.join(); + return HasErrors ? 1 : 0; +} + +static void RssThread(Fuzzer *F, size_t RssLimitMb) { + while (true) { + SleepSeconds(1); + size_t Peak = GetPeakRSSMb(); + if (Peak > RssLimitMb) + F->RssLimitCallback(); + } +} + +static void StartRssThread(Fuzzer *F, size_t RssLimitMb) { + if (!RssLimitMb) return; + std::thread T(RssThread, F, RssLimitMb); + T.detach(); +} + +int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) { + Unit U = FileToVector(InputFilePath); + if (MaxLen && MaxLen < U.size()) + U.resize(MaxLen); + F->RunOne(U.data(), U.size()); + F->TryDetectingAMemoryLeak(U.data(), U.size(), true); + return 0; +} + +static bool AllInputsAreFiles() { + if (Inputs->empty()) return false; + for (auto &Path : *Inputs) + if (!IsFile(Path)) + return false; + return true; +} + +int MinimizeCrashInput(const std::vector &Args) { + if (Inputs->size() != 1) { + Printf("ERROR: -minimize_crash should be given one input file\n"); + exit(1); + } + std::string InputFilePath = Inputs->at(0); + std::string BaseCmd = + CloneArgsWithoutX(Args, "minimize_crash", "exact_artifact_path"); + auto InputPos = BaseCmd.find(" " + InputFilePath + " "); + assert(InputPos != std::string::npos); + BaseCmd.erase(InputPos, InputFilePath.size() + 1); + if (Flags.runs <= 0 && Flags.max_total_time == 0) { + Printf("INFO: you need to specify -runs=N or " + "-max_total_time=N with -minimize_crash=1\n" + "INFO: defaulting to -max_total_time=600\n"); + BaseCmd += " -max_total_time=600"; + } + // BaseCmd += " > /dev/null 2>&1 "; + + std::string CurrentFilePath = InputFilePath; + while (true) { + Unit U = FileToVector(CurrentFilePath); + if (U.size() < 2) { + Printf("CRASH_MIN: '%s' is small enough\n", CurrentFilePath.c_str()); + return 0; + } + Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n", + CurrentFilePath.c_str(), U.size()); + + auto Cmd = BaseCmd + " " + CurrentFilePath; + + Printf("CRASH_MIN: executing: %s\n", Cmd.c_str()); + int ExitCode = ExecuteCommand(Cmd); + if (ExitCode == 0) { + Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str()); + exit(1); + } + Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize " + "it further\n", + CurrentFilePath.c_str(), U.size()); + + std::string ArtifactPath = "minimized-from-" + Hash(U); + Cmd += " -minimize_crash_internal_step=1 -exact_artifact_path=" + + ArtifactPath; + Printf("CRASH_MIN: executing: %s\n", Cmd.c_str()); + ExitCode = ExecuteCommand(Cmd); + if (ExitCode == 0) { + if (Flags.exact_artifact_path) { + CurrentFilePath = Flags.exact_artifact_path; + WriteToFile(U, CurrentFilePath); + } + Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n", + CurrentFilePath.c_str(), U.size()); + return 0; + } + CurrentFilePath = ArtifactPath; + Printf("\n\n\n\n\n\n*********************************\n"); + } + return 0; +} + +int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { + assert(Inputs->size() == 1); + std::string InputFilePath = Inputs->at(0); + Unit U = FileToVector(InputFilePath); + assert(U.size() > 2); + Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size()); + Corpus->AddToCorpus(U, 0); + F->SetMaxInputLen(U.size()); + F->SetMaxMutationLen(U.size() - 1); + F->MinimizeCrashLoop(U); + Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n"); + exit(0); + return 0; +} + +int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { + using namespace fuzzer; + assert(argc && argv && "Argument pointers cannot be nullptr"); + EF = new ExternalFunctions(); + if (EF->LLVMFuzzerInitialize) + EF->LLVMFuzzerInitialize(argc, argv); + const std::vector Args(*argv, *argv + *argc); + assert(!Args.empty()); + ProgName = new std::string(Args[0]); + ParseFlags(Args); + if (Flags.help) { + PrintHelp(); + return 0; + } + + if (Flags.minimize_crash) + return MinimizeCrashInput(Args); + + if (Flags.close_fd_mask & 2) + DupAndCloseStderr(); + if (Flags.close_fd_mask & 1) + CloseStdout(); + + if (Flags.jobs > 0 && Flags.workers == 0) { + Flags.workers = std::min(NumberOfCpuCores() / 2, Flags.jobs); + if (Flags.workers > 1) + Printf("Running %u workers\n", Flags.workers); + } + + if (Flags.workers > 0 && Flags.jobs > 0) + return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs); + + const size_t kMaxSaneLen = 1 << 20; + const size_t kMinDefaultLen = 64; + FuzzingOptions Options; + Options.Verbosity = Flags.verbosity; + Options.MaxLen = Flags.max_len; + Options.UnitTimeoutSec = Flags.timeout; + Options.ErrorExitCode = Flags.error_exitcode; + Options.TimeoutExitCode = Flags.timeout_exitcode; + Options.MaxTotalTimeSec = Flags.max_total_time; + Options.DoCrossOver = Flags.cross_over; + Options.MutateDepth = Flags.mutate_depth; + Options.UseCounters = Flags.use_counters; + Options.UseIndirCalls = Flags.use_indir_calls; + Options.UseMemcmp = Flags.use_memcmp; + Options.UseMemmem = Flags.use_memmem; + Options.UseCmp = Flags.use_cmp; + Options.UseValueProfile = Flags.use_value_profile; + Options.Shrink = Flags.shrink; + Options.ShuffleAtStartUp = Flags.shuffle; + Options.PreferSmall = Flags.prefer_small; + Options.ReloadIntervalSec = Flags.reload; + Options.OnlyASCII = Flags.only_ascii; + Options.OutputCSV = Flags.output_csv; + Options.DetectLeaks = Flags.detect_leaks; + Options.TraceMalloc = Flags.trace_malloc; + Options.RssLimitMb = Flags.rss_limit_mb; + if (Flags.runs >= 0) + Options.MaxNumberOfRuns = Flags.runs; + if (!Inputs->empty() && !Flags.minimize_crash_internal_step) + Options.OutputCorpus = (*Inputs)[0]; + Options.ReportSlowUnits = Flags.report_slow_units; + if (Flags.artifact_prefix) + Options.ArtifactPrefix = Flags.artifact_prefix; + if (Flags.exact_artifact_path) + Options.ExactArtifactPath = Flags.exact_artifact_path; + std::vector Dictionary; + if (Flags.dict) + if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary)) + return 1; + if (Flags.verbosity > 0 && !Dictionary.empty()) + Printf("Dictionary: %zd entries\n", Dictionary.size()); + bool DoPlainRun = AllInputsAreFiles(); + Options.SaveArtifacts = + !DoPlainRun || Flags.minimize_crash_internal_step; + Options.PrintNewCovPcs = Flags.print_pcs; + Options.PrintFinalStats = Flags.print_final_stats; + Options.PrintCorpusStats = Flags.print_corpus_stats; + Options.PrintCoverage = Flags.print_coverage; + Options.DumpCoverage = Flags.dump_coverage; + if (Flags.exit_on_src_pos) + Options.ExitOnSrcPos = Flags.exit_on_src_pos; + if (Flags.exit_on_item) + Options.ExitOnItem = Flags.exit_on_item; + + unsigned Seed = Flags.seed; + // Initialize Seed. + if (Seed == 0) + Seed = (std::chrono::system_clock::now().time_since_epoch().count() << 10) + + GetPid(); + if (Flags.verbosity) + Printf("INFO: Seed: %u\n", Seed); + + Random Rand(Seed); + auto *MD = new MutationDispatcher(Rand, Options); + auto *Corpus = new InputCorpus(Options.OutputCorpus); + auto *F = new Fuzzer(Callback, *Corpus, *MD, Options); + + for (auto &U: Dictionary) + if (U.size() <= Word::GetMaxSize()) + MD->AddWordToManualDictionary(Word(U.data(), U.size())); + + StartRssThread(F, Flags.rss_limit_mb); + + Options.HandleAbrt = Flags.handle_abrt; + Options.HandleBus = Flags.handle_bus; + Options.HandleFpe = Flags.handle_fpe; + Options.HandleIll = Flags.handle_ill; + Options.HandleInt = Flags.handle_int; + Options.HandleSegv = Flags.handle_segv; + Options.HandleTerm = Flags.handle_term; + SetSignalHandler(Options); + + if (Flags.minimize_crash_internal_step) + return MinimizeCrashInputInternalStep(F, Corpus); + + if (DoPlainRun) { + Options.SaveArtifacts = false; + int Runs = std::max(1, Flags.runs); + Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(), + Inputs->size(), Runs); + for (auto &Path : *Inputs) { + auto StartTime = system_clock::now(); + Printf("Running: %s\n", Path.c_str()); + for (int Iter = 0; Iter < Runs; Iter++) + RunOneTest(F, Path.c_str(), Options.MaxLen); + auto StopTime = system_clock::now(); + auto MS = duration_cast(StopTime - StartTime).count(); + Printf("Executed %s in %zd ms\n", Path.c_str(), (long)MS); + } + Printf("***\n" + "*** NOTE: fuzzing was not performed, you have only\n" + "*** executed the target code on a fixed set of inputs.\n" + "***\n"); + F->PrintFinalStats(); + exit(0); + } + + if (Flags.merge) { + if (Options.MaxLen == 0) + F->SetMaxInputLen(kMaxSaneLen); + if (TPC.UsingTracePcGuard()) { + if (Flags.merge_control_file) + F->CrashResistantMergeInternalStep(Flags.merge_control_file); + else + F->CrashResistantMerge(Args, *Inputs); + } else { + F->Merge(*Inputs); + } + exit(0); + } + + size_t TemporaryMaxLen = Options.MaxLen ? Options.MaxLen : kMaxSaneLen; + + UnitVector InitialCorpus; + for (auto &Inp : *Inputs) { + Printf("Loading corpus dir: %s\n", Inp.c_str()); + ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr, + TemporaryMaxLen, /*ExitOnError=*/false); + } + + if (Options.MaxLen == 0) { + size_t MaxLen = 0; + for (auto &U : InitialCorpus) + MaxLen = std::max(U.size(), MaxLen); + F->SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxLen), kMaxSaneLen)); + } + + if (InitialCorpus.empty()) { + InitialCorpus.push_back(Unit({'\n'})); // Valid ASCII input. + if (Options.Verbosity) + Printf("INFO: A corpus is not provided, starting from an empty corpus\n"); + } + F->ShuffleAndMinimize(&InitialCorpus); + InitialCorpus.clear(); // Don't need this memory any more. + F->Loop(); + + if (Flags.verbosity) + Printf("Done %d runs in %zd second(s)\n", F->getTotalNumberOfRuns(), + F->secondsSinceProcessStartUp()); + F->PrintFinalStats(); + + exit(0); // Don't let F destroy itself. +} + +// Storage for global ExternalFunctions object. +ExternalFunctions *EF = nullptr; + +} // namespace fuzzer diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctions.def b/test/fuzz_test/Fuzzer/FuzzerExtFunctions.def new file mode 100644 index 00000000..61c72e4a --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerExtFunctions.def @@ -0,0 +1,50 @@ +//===- FuzzerExtFunctions.def - External functions --------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This defines the external function pointers that +// ``fuzzer::ExternalFunctions`` should contain and try to initialize. The +// EXT_FUNC macro must be defined at the point of inclusion. The signature of +// the macro is: +// +// EXT_FUNC(, , , ) +//===----------------------------------------------------------------------===// + +// Optional user functions +EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false); +EXT_FUNC(LLVMFuzzerCustomMutator, size_t, + (uint8_t * Data, size_t Size, size_t MaxSize, unsigned int Seed), + false); +EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t, + (const uint8_t * Data1, size_t Size1, + const uint8_t * Data2, size_t Size2, + uint8_t * Out, size_t MaxOutSize, unsigned int Seed), + false); + +// Sanitizer functions +EXT_FUNC(__lsan_enable, void, (), false); +EXT_FUNC(__lsan_disable, void, (), false); +EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false); +EXT_FUNC(__sanitizer_get_number_of_counters, size_t, (), false); +EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int, + (void (*malloc_hook)(const volatile void *, size_t), + void (*free_hook)(const volatile void *)), + false); +EXT_FUNC(__sanitizer_get_total_unique_caller_callee_pairs, size_t, (), false); +EXT_FUNC(__sanitizer_get_total_unique_coverage, size_t, (), true); +EXT_FUNC(__sanitizer_print_memory_profile, int, (size_t), false); +EXT_FUNC(__sanitizer_print_stack_trace, void, (), true); +EXT_FUNC(__sanitizer_symbolize_pc, void, + (void *, const char *fmt, char *out_buf, size_t out_buf_size), false); +EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int, + (void *pc, char *module_path, + size_t module_path_len,void **pc_offset), false); +EXT_FUNC(__sanitizer_reset_coverage, void, (), true); +EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true); +EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false); +EXT_FUNC(__sanitizer_update_counter_bitset_and_clear_counters, uintptr_t, + (uint8_t*), false); diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctions.h b/test/fuzz_test/Fuzzer/FuzzerExtFunctions.h new file mode 100644 index 00000000..2672a385 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerExtFunctions.h @@ -0,0 +1,35 @@ +//===- FuzzerExtFunctions.h - Interface to external functions ---*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Defines an interface to (possibly optional) functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_EXT_FUNCTIONS_H +#define LLVM_FUZZER_EXT_FUNCTIONS_H + +#include +#include + +namespace fuzzer { + +struct ExternalFunctions { + // Initialize function pointers. Functions that are not available will be set + // to nullptr. Do not call this constructor before ``main()`` has been + // entered. + ExternalFunctions(); + +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + RETURN_TYPE(*NAME) FUNC_SIG = nullptr + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +}; +} // namespace fuzzer + +#endif diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctionsDlsym.cpp b/test/fuzz_test/Fuzzer/FuzzerExtFunctionsDlsym.cpp new file mode 100644 index 00000000..06bddd5d --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerExtFunctionsDlsym.cpp @@ -0,0 +1,52 @@ +//===- FuzzerExtFunctionsDlsym.cpp - Interface to external functions ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementation for operating systems that support dlsym(). We only use it on +// Apple platforms for now. We don't use this approach on Linux because it +// requires that clients of LibFuzzer pass ``--export-dynamic`` to the linker. +// That is a complication we don't wish to expose to clients right now. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_APPLE + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include + +using namespace fuzzer; + +template +static T GetFnPtr(const char *FnName, bool WarnIfMissing) { + dlerror(); // Clear any previous errors. + void *Fn = dlsym(RTLD_DEFAULT, FnName); + if (Fn == nullptr) { + if (WarnIfMissing) { + const char *ErrorMsg = dlerror(); + Printf("WARNING: Failed to find function \"%s\".", FnName); + if (ErrorMsg) + Printf(" Reason %s.", ErrorMsg); + Printf("\n"); + } + } + return reinterpret_cast(Fn); +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = GetFnPtr(#NAME, WARN) + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +} // namespace fuzzer + +#endif // LIBFUZZER_APPLE diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeak.cpp b/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeak.cpp new file mode 100644 index 00000000..7b02b6f0 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeak.cpp @@ -0,0 +1,53 @@ +//===- FuzzerExtFunctionsWeak.cpp - Interface to external functions -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementation for Linux. This relies on the linker's support for weak +// symbols. We don't use this approach on Apple platforms because it requires +// clients of LibFuzzer to pass ``-U _`` to the linker to allow +// weak symbols to be undefined. That is a complication we don't want to expose +// to clients right now. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_LINUX + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" + +extern "C" { +// Declare these symbols as weak to allow them to be optionally defined. +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + __attribute__((weak)) RETURN_TYPE NAME FUNC_SIG + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +using namespace fuzzer; + +static void CheckFnPtr(void *FnPtr, const char *FnName, bool WarnIfMissing) { + if (FnPtr == nullptr && WarnIfMissing) { + Printf("WARNING: Failed to find function \"%s\".\n", FnName); + } +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = ::NAME; \ + CheckFnPtr((void *)::NAME, #NAME, WARN); + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +} // namespace fuzzer + +#endif // LIBFUZZER_LINUX diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp b/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp new file mode 100644 index 00000000..e10f7b4d --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp @@ -0,0 +1,56 @@ +//===- FuzzerExtFunctionsWeakAlias.cpp - Interface to external functions --===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementation using weak aliases. Works for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_WINDOWS + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" + +using namespace fuzzer; + +extern "C" { +// Declare these symbols as weak to allow them to be optionally defined. +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + RETURN_TYPE NAME##Def FUNC_SIG { \ + Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \ + exit(1); \ + } \ + RETURN_TYPE NAME FUNC_SIG __attribute__((weak, alias(#NAME "Def"))); + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +template +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/test/fuzz_test/Fuzzer/FuzzerFlags.def b/test/fuzz_test/Fuzzer/FuzzerFlags.def new file mode 100644 index 00000000..25ef1741 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerFlags.def @@ -0,0 +1,115 @@ +//===- FuzzerFlags.def - Run-time flags -------------------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the +// point of inclusion. We are not using any flag parsing library for better +// portability and independence. +//===----------------------------------------------------------------------===// +FUZZER_FLAG_INT(verbosity, 1, "Verbosity level.") +FUZZER_FLAG_UNSIGNED(seed, 0, "Random seed. If 0, seed is generated.") +FUZZER_FLAG_INT(runs, -1, + "Number of individual test runs (-1 for infinite runs).") +FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. " + "If 0, libFuzzer tries to guess a good value based on the corpus " + "and reports it. ") +FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.") +FUZZER_FLAG_INT(mutate_depth, 5, + "Apply this number of consecutive mutations to each input.") +FUZZER_FLAG_INT(shuffle, 1, "Shuffle inputs at startup") +FUZZER_FLAG_INT(prefer_small, 1, + "If 1, always prefer smaller inputs during the corpus shuffle.") +FUZZER_FLAG_INT( + timeout, 1200, + "Timeout in seconds (if positive). " + "If one unit runs more than this number of seconds the process will abort.") +FUZZER_FLAG_INT(error_exitcode, 77, "When libFuzzer itself reports a bug " + "this exit code will be used.") +FUZZER_FLAG_INT(timeout_exitcode, 77, "When libFuzzer reports a timeout " + "this exit code will be used.") +FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total " + "time in seconds to run the fuzzer.") +FUZZER_FLAG_INT(help, 0, "Print help.") +FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be " + "merged into the 1-st corpus. Only interesting units will be taken. " + "This flag can be used to minimize a corpus.") +FUZZER_FLAG_STRING(merge_control_file, "internal flag") +FUZZER_FLAG_INT(minimize_crash, 0, "If 1, minimizes the provided" + " crash input. Use with -runs=N or -max_total_time=N to limit " + "the number attempts") +FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag") +FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters") +FUZZER_FLAG_INT(use_indir_calls, 1, "Use indirect caller-callee counters") +FUZZER_FLAG_INT(use_memcmp, 1, + "Use hints from intercepting memcmp, strcmp, etc") +FUZZER_FLAG_INT(use_memmem, 1, + "Use hints from intercepting memmem, strstr, etc") +FUZZER_FLAG_INT(use_value_profile, 0, + "Experimental. Use value profile to guide fuzzing.") +FUZZER_FLAG_INT(use_cmp, 1, "Use CMP traces to guide mutations") +FUZZER_FLAG_INT(shrink, 0, "Experimental. Try to shrink corpus elements.") +FUZZER_FLAG_UNSIGNED(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn" + " this number of jobs in separate worker processes" + " with stdout/stderr redirected to fuzz-JOB.log.") +FUZZER_FLAG_UNSIGNED(workers, 0, + "Number of simultaneous worker processes to run the jobs." + " If zero, \"min(jobs,NumberOfCpuCores()/2)\" is used.") +FUZZER_FLAG_INT(reload, 1, + "Reload the main corpus every seconds to get new units" + " discovered by other processes. If 0, disabled") +FUZZER_FLAG_INT(report_slow_units, 10, + "Report slowest units if they run for more than this number of seconds.") +FUZZER_FLAG_INT(only_ascii, 0, + "If 1, generate only ASCII (isprint+isspace) inputs.") +FUZZER_FLAG_STRING(dict, "Experimental. Use the dictionary file.") +FUZZER_FLAG_STRING(artifact_prefix, "Write fuzzing artifacts (crash, " + "timeout, or slow inputs) as " + "$(artifact_prefix)file") +FUZZER_FLAG_STRING(exact_artifact_path, + "Write the single artifact on failure (crash, timeout) " + "as $(exact_artifact_path). This overrides -artifact_prefix " + "and will not use checksum in the file name. Do not " + "use the same path for several parallel processes.") +FUZZER_FLAG_INT(output_csv, 0, "Enable pulse output in CSV format.") +FUZZER_FLAG_INT(print_pcs, 0, "If 1, print out newly covered PCs.") +FUZZER_FLAG_INT(print_final_stats, 0, "If 1, print statistics at exit.") +FUZZER_FLAG_INT(print_corpus_stats, 0, + "If 1, print statistics on corpus elements at exit.") +FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information at exit." + " Experimental, only with trace-pc-guard") +FUZZER_FLAG_INT(dump_coverage, 0, "If 1, dump coverage information at exit." + " Experimental, only with trace-pc-guard") +FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.") +FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGSEGV.") +FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.") +FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.") +FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.") +FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.") +FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.") +FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; " + "if 2, close stderr; if 3, close both. " + "Be careful, this will also close e.g. asan's stderr/stdout.") +FUZZER_FLAG_INT(detect_leaks, 1, "If 1, and if LeakSanitizer is enabled " + "try to detect memory leaks during fuzzing (i.e. not only at shut down).") +FUZZER_FLAG_INT(trace_malloc, 0, "If >= 1 will print all mallocs/frees. " + "If >= 2 will also print stack traces.") +FUZZER_FLAG_INT(rss_limit_mb, 2048, "If non-zero, the fuzzer will exit upon" + "reaching this limit of RSS memory usage.") +FUZZER_FLAG_STRING(exit_on_src_pos, "Exit if a newly found PC originates" + " from the given source location. Example: -exit_on_src_pos=foo.cc:123. " + "Used primarily for testing libFuzzer itself.") +FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum" + " was added to the corpus. " + "Used primarily for testing libFuzzer itself.") + +FUZZER_DEPRECATED_FLAG(exit_on_first) +FUZZER_DEPRECATED_FLAG(save_minimized_corpus) +FUZZER_DEPRECATED_FLAG(sync_command) +FUZZER_DEPRECATED_FLAG(sync_timeout) +FUZZER_DEPRECATED_FLAG(test_single_input) +FUZZER_DEPRECATED_FLAG(drill) +FUZZER_DEPRECATED_FLAG(truncate_units) diff --git a/test/fuzz_test/Fuzzer/FuzzerIO.cpp b/test/fuzz_test/Fuzzer/FuzzerIO.cpp new file mode 100644 index 00000000..eda8e877 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerIO.cpp @@ -0,0 +1,117 @@ +//===- FuzzerIO.cpp - IO utils. -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// IO functions. +//===----------------------------------------------------------------------===// + +#include "FuzzerIO.h" +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +static FILE *OutputFile = stderr; + +long GetEpoch(const std::string &Path) { + struct stat St; + if (stat(Path.c_str(), &St)) + return 0; // Can't stat, be conservative. + return St.st_mtime; +} + +Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) { + std::ifstream T(Path); + if (ExitOnError && !T) { + Printf("No such directory: %s; exiting\n", Path.c_str()); + exit(1); + } + + T.seekg(0, T.end); + size_t FileLen = T.tellg(); + if (MaxSize) + FileLen = std::min(FileLen, MaxSize); + + T.seekg(0, T.beg); + Unit Res(FileLen); + T.read(reinterpret_cast(Res.data()), FileLen); + return Res; +} + +std::string FileToString(const std::string &Path) { + std::ifstream T(Path); + return std::string((std::istreambuf_iterator(T)), + std::istreambuf_iterator()); +} + +void CopyFileToErr(const std::string &Path) { + Printf("%s", FileToString(Path).c_str()); +} + +void WriteToFile(const Unit &U, const std::string &Path) { + // Use raw C interface because this function may be called from a sig handler. + FILE *Out = fopen(Path.c_str(), "w"); + if (!Out) return; + fwrite(U.data(), sizeof(U[0]), U.size(), Out); + fclose(Out); +} + +void ReadDirToVectorOfUnits(const char *Path, std::vector *V, + long *Epoch, size_t MaxSize, bool ExitOnError) { + long E = Epoch ? *Epoch : 0; + std::vector Files; + ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true); + size_t NumLoaded = 0; + for (size_t i = 0; i < Files.size(); i++) { + auto &X = Files[i]; + if (Epoch && GetEpoch(X) < E) continue; + NumLoaded++; + if ((NumLoaded & (NumLoaded - 1)) == 0 && NumLoaded >= 1024) + Printf("Loaded %zd/%zd files from %s\n", NumLoaded, Files.size(), Path); + auto S = FileToVector(X, MaxSize, ExitOnError); + if (!S.empty()) + V->push_back(S); + } +} + +std::string DirPlusFile(const std::string &DirPath, + const std::string &FileName) { + return DirPath + GetSeparator() + FileName; +} + +void DupAndCloseStderr() { + int OutputFd = DuplicateFile(2); + if (OutputFd > 0) { + FILE *NewOutputFile = OpenFile(OutputFd, "w"); + if (NewOutputFile) { + OutputFile = NewOutputFile; + if (EF->__sanitizer_set_report_fd) + EF->__sanitizer_set_report_fd(reinterpret_cast(OutputFd)); + CloseFile(2); + } + } +} + +void CloseStdout() { + CloseFile(1); +} + +void Printf(const char *Fmt, ...) { + va_list ap; + va_start(ap, Fmt); + vfprintf(OutputFile, Fmt, ap); + va_end(ap); + fflush(OutputFile); +} + +} // namespace fuzzer diff --git a/test/fuzz_test/Fuzzer/FuzzerIO.h b/test/fuzz_test/Fuzzer/FuzzerIO.h new file mode 100644 index 00000000..741fecf4 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerIO.h @@ -0,0 +1,64 @@ +//===- FuzzerIO.h - Internal header for IO utils ----------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// IO interface. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_IO_H +#define LLVM_FUZZER_IO_H + +#include "FuzzerDefs.h" + +namespace fuzzer { + +long GetEpoch(const std::string &Path); + +Unit FileToVector(const std::string &Path, size_t MaxSize = 0, + bool ExitOnError = true); + +std::string FileToString(const std::string &Path); + +void CopyFileToErr(const std::string &Path); + +void WriteToFile(const Unit &U, const std::string &Path); + +void ReadDirToVectorOfUnits(const char *Path, std::vector *V, + long *Epoch, size_t MaxSize, bool ExitOnError); + +// Returns "Dir/FileName" or equivalent for the current OS. +std::string DirPlusFile(const std::string &DirPath, + const std::string &FileName); + +// Returns the name of the dir, similar to the 'dirname' utility. +std::string DirName(const std::string &FileName); + +void DupAndCloseStderr(); + +void CloseStdout(); + +void Printf(const char *Fmt, ...); + +// Platform specific functions: +bool IsFile(const std::string &Path); + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + std::vector *V, bool TopDir); + +char GetSeparator(); + +FILE* OpenFile(int Fd, const char *Mode); + +int CloseFile(int Fd); + +int DuplicateFile(int Fd); + +void RemoveFile(const std::string &Path); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_IO_H diff --git a/test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp b/test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp new file mode 100644 index 00000000..720bc130 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp @@ -0,0 +1,88 @@ +//===- FuzzerIOPosix.cpp - IO utils for Posix. ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// IO functions implementation using Posix API. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_POSIX + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +bool IsFile(const std::string &Path) { + struct stat St; + if (stat(Path.c_str(), &St)) + return false; + return S_ISREG(St.st_mode); +} + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + std::vector *V, bool TopDir) { + auto E = GetEpoch(Dir); + if (Epoch) + if (E && *Epoch >= E) return; + + DIR *D = opendir(Dir.c_str()); + if (!D) { + Printf("No such directory: %s; exiting\n", Dir.c_str()); + exit(1); + } + while (auto E = readdir(D)) { + std::string Path = DirPlusFile(Dir, E->d_name); + if (E->d_type == DT_REG || E->d_type == DT_LNK) + V->push_back(Path); + else if (E->d_type == DT_DIR && *E->d_name != '.') + ListFilesInDirRecursive(Path, Epoch, V, false); + } + closedir(D); + if (Epoch && TopDir) + *Epoch = E; +} + +char GetSeparator() { + return '/'; +} + +FILE* OpenFile(int Fd, const char* Mode) { + return fdopen(Fd, Mode); +} + +int CloseFile(int fd) { + return close(fd); +} + +int DuplicateFile(int Fd) { + return dup(Fd); +} + +void RemoveFile(const std::string &Path) { + unlink(Path.c_str()); +} + +std::string DirName(const std::string &FileName) { + char *Tmp = new char[FileName.size() + 1]; + memcpy(Tmp, FileName.c_str(), FileName.size() + 1); + std::string Res = dirname(Tmp); + delete [] Tmp; + return Res; +} + +} // namespace fuzzer + +#endif // LIBFUZZER_POSIX diff --git a/test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp b/test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp new file mode 100644 index 00000000..a4738eb9 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp @@ -0,0 +1,282 @@ +//===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// IO functions implementation for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_WINDOWS + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +static bool IsFile(const std::string &Path, const DWORD &FileAttributes) { + + if (FileAttributes & FILE_ATTRIBUTE_NORMAL) + return true; + + if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + return false; + + HANDLE FileHandle( + CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, 0)); + + if (FileHandle == INVALID_HANDLE_VALUE) { + Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(), + GetLastError()); + return false; + } + + DWORD FileType = GetFileType(FileHandle); + + if (FileType == FILE_TYPE_UNKNOWN) { + Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(), + GetLastError()); + CloseHandle(FileHandle); + return false; + } + + if (FileType != FILE_TYPE_DISK) { + CloseHandle(FileHandle); + return false; + } + + CloseHandle(FileHandle); + return true; +} + +bool IsFile(const std::string &Path) { + DWORD Att = GetFileAttributesA(Path.c_str()); + + if (Att == INVALID_FILE_ATTRIBUTES) { + Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), GetLastError()); + return false; + } + + return IsFile(Path, Att); +} + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + std::vector *V, bool TopDir) { + auto E = GetEpoch(Dir); + if (Epoch) + if (E && *Epoch >= E) return; + + std::string Path(Dir); + assert(!Path.empty()); + if (Path.back() != '\\') + Path.push_back('\\'); + Path.push_back('*'); + + // Get the first directory entry. + WIN32_FIND_DATAA FindInfo; + HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo)); + if (FindHandle == INVALID_HANDLE_VALUE) + { + Printf("No file found in: %s.\n", Dir.c_str()); + return; + } + + do { + std::string FileName = DirPlusFile(Dir, FindInfo.cFileName); + + if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + size_t FilenameLen = strlen(FindInfo.cFileName); + if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') || + (FilenameLen == 2 && FindInfo.cFileName[0] == '.' && + FindInfo.cFileName[1] == '.')) + continue; + + ListFilesInDirRecursive(FileName, Epoch, V, false); + } + else if (IsFile(FileName, FindInfo.dwFileAttributes)) + V->push_back(FileName); + } while (FindNextFileA(FindHandle, &FindInfo)); + + DWORD LastError = GetLastError(); + if (LastError != ERROR_NO_MORE_FILES) + Printf("FindNextFileA failed (Error code: %lu).\n", LastError); + + FindClose(FindHandle); + + if (Epoch && TopDir) + *Epoch = E; +} + +char GetSeparator() { + return '\\'; +} + +FILE* OpenFile(int Fd, const char* Mode) { + return _fdopen(Fd, Mode); +} + +int CloseFile(int Fd) { + return _close(Fd); +} + +int DuplicateFile(int Fd) { + return _dup(Fd); +} + +void RemoveFile(const std::string &Path) { + _unlink(Path.c_str()); +} + +static bool IsSeparator(char C) { + return C == '\\' || C == '/'; +} + +// Parse disk designators, like "C:\". If Relative == true, also accepts: "C:". +// Returns number of characters considered if successful. +static size_t ParseDrive(const std::string &FileName, const size_t Offset, + bool Relative = true) { + if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':') + return 0; + if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) { + if (!Relative) // Accept relative path? + return 0; + else + return 2; + } + return 3; +} + +// Parse a file name, like: SomeFile.txt +// Returns number of characters considered if successful. +static size_t ParseFileName(const std::string &FileName, const size_t Offset) { + size_t Pos = Offset; + const size_t End = FileName.size(); + for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos) + ; + return Pos - Offset; +} + +// Parse a directory ending in separator, like: SomeDir\ +// Returns number of characters considered if successful. +static size_t ParseDir(const std::string &FileName, const size_t Offset) { + size_t Pos = Offset; + const size_t End = FileName.size(); + if (Pos >= End || IsSeparator(FileName[Pos])) + return 0; + for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos) + ; + if (Pos >= End) + return 0; + ++Pos; // Include separator. + return Pos - Offset; +} + +// Parse a servername and share, like: SomeServer\SomeShare\ +// Returns number of characters considered if successful. +static size_t ParseServerAndShare(const std::string &FileName, + const size_t Offset) { + size_t Pos = Offset, Res; + if (!(Res = ParseDir(FileName, Pos))) + return 0; + Pos += Res; + if (!(Res = ParseDir(FileName, Pos))) + return 0; + Pos += Res; + return Pos - Offset; +} + +// Parse the given Ref string from the position Offset, to exactly match the given +// string Patt. +// Returns number of characters considered if successful. +static size_t ParseCustomString(const std::string &Ref, size_t Offset, + const char *Patt) { + size_t Len = strlen(Patt); + if (Offset + Len > Ref.size()) + return 0; + return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0; +} + +// Parse a location, like: +// \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C: +// Returns number of characters considered if successful. +static size_t ParseLocation(const std::string &FileName) { + size_t Pos = 0, Res; + + if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) { + Pos += Res; + if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) { + Pos += Res; + if ((Res = ParseServerAndShare(FileName, Pos))) + return Pos + Res; + return 0; + } + if ((Res = ParseDrive(FileName, Pos, false))) + return Pos + Res; + return 0; + } + + if (Pos < FileName.size() && IsSeparator(FileName[Pos])) { + ++Pos; + if (Pos < FileName.size() && IsSeparator(FileName[Pos])) { + ++Pos; + if ((Res = ParseServerAndShare(FileName, Pos))) + return Pos + Res; + return 0; + } + return Pos; + } + + if ((Res = ParseDrive(FileName, Pos))) + return Pos + Res; + + return Pos; +} + +std::string DirName(const std::string &FileName) { + size_t LocationLen = ParseLocation(FileName); + size_t DirLen = 0, Res; + while ((Res = ParseDir(FileName, LocationLen + DirLen))) + DirLen += Res; + size_t FileLen = ParseFileName(FileName, LocationLen + DirLen); + + if (LocationLen + DirLen + FileLen != FileName.size()) { + Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str()); + exit(1); + } + + if (DirLen) { + --DirLen; // Remove trailing separator. + if (!FileLen) { // Path ended in separator. + assert(DirLen); + // Remove file name from Dir. + while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1])) + --DirLen; + if (DirLen) // Remove trailing separator. + --DirLen; + } + } + + if (!LocationLen) { // Relative path. + if (!DirLen) + return "."; + return std::string(".\\").append(FileName, 0, DirLen); + } + + return FileName.substr(0, LocationLen + DirLen); +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS diff --git a/test/fuzz_test/Fuzzer/FuzzerInterface.h b/test/fuzz_test/Fuzzer/FuzzerInterface.h new file mode 100644 index 00000000..d47e20e3 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerInterface.h @@ -0,0 +1,67 @@ +//===- FuzzerInterface.h - Interface header for the Fuzzer ------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Define the interface between libFuzzer and the library being tested. +//===----------------------------------------------------------------------===// + +// NOTE: the libFuzzer interface is thin and in the majority of cases +// you should not include this file into your target. In 95% of cases +// all you need is to define the following function in your file: +// extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +// WARNING: keep the interface in C. + +#ifndef LLVM_FUZZER_INTERFACE_H +#define LLVM_FUZZER_INTERFACE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Mandatory user-provided target function. +// Executes the code under test with [Data, Data+Size) as the input. +// libFuzzer will invoke this function *many* times with different inputs. +// Must return 0. +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +// Optional user-provided initialization function. +// If provided, this function will be called by libFuzzer once at startup. +// It may read and modify argc/argv. +// Must return 0. +int LLVMFuzzerInitialize(int *argc, char ***argv); + +// Optional user-provided custom mutator. +// Mutates raw data in [Data, Data+Size) inplace. +// Returns the new size, which is not greater than MaxSize. +// Given the same Seed produces the same mutation. +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, + unsigned int Seed); + +// Optional user-provided custom cross-over function. +// Combines pieces of Data1 & Data2 together into Out. +// Returns the new size, which is not greater than MaxOutSize. +// Should produce the same mutation given the same Seed. +size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize, + unsigned int Seed); + +// Experimental, may go away in future. +// libFuzzer-provided function to be used inside LLVMFuzzerTestOneInput. +// Mutates raw data in [Data, Data+Size) inplace. +// Returns the new size, which is not greater than MaxSize. +size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // LLVM_FUZZER_INTERFACE_H diff --git a/test/fuzz_test/Fuzzer/FuzzerInternal.h b/test/fuzz_test/Fuzzer/FuzzerInternal.h new file mode 100644 index 00000000..c0417060 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerInternal.h @@ -0,0 +1,182 @@ +//===- FuzzerInternal.h - Internal header for the Fuzzer --------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Define the main class fuzzer::Fuzzer and most functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_INTERNAL_H +#define LLVM_FUZZER_INTERNAL_H + +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerInterface.h" +#include "FuzzerOptions.h" +#include "FuzzerSHA1.h" +#include "FuzzerValueBitMap.h" +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +using namespace std::chrono; + +class Fuzzer { +public: + + // Aggregates all available coverage measurements. + struct Coverage { + Coverage() { Reset(); } + + void Reset() { + BlockCoverage = 0; + CallerCalleeCoverage = 0; + CounterBitmapBits = 0; + CounterBitmap.clear(); + VPMap.Reset(); + } + + size_t BlockCoverage; + size_t CallerCalleeCoverage; + // Precalculated number of bits in CounterBitmap. + size_t CounterBitmapBits; + std::vector CounterBitmap; + ValueBitMap VPMap; + }; + + Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, + FuzzingOptions Options); + ~Fuzzer(); + void Loop(); + void MinimizeCrashLoop(const Unit &U); + void ShuffleAndMinimize(UnitVector *V); + void InitializeTraceState(); + void RereadOutputCorpus(size_t MaxSize); + + size_t secondsSinceProcessStartUp() { + return duration_cast(system_clock::now() - ProcessStartTime) + .count(); + } + + bool TimedOut() { + return Options.MaxTotalTimeSec > 0 && + secondsSinceProcessStartUp() > + static_cast(Options.MaxTotalTimeSec); + } + + size_t execPerSec() { + size_t Seconds = secondsSinceProcessStartUp(); + return Seconds ? TotalNumberOfRuns / Seconds : 0; + } + + size_t getTotalNumberOfRuns() { return TotalNumberOfRuns; } + + static void StaticAlarmCallback(); + static void StaticCrashSignalCallback(); + static void StaticInterruptCallback(); + + void ExecuteCallback(const uint8_t *Data, size_t Size); + size_t RunOne(const uint8_t *Data, size_t Size); + + // Merge Corpora[1:] into Corpora[0]. + void Merge(const std::vector &Corpora); + void CrashResistantMerge(const std::vector &Args, + const std::vector &Corpora); + void CrashResistantMergeInternalStep(const std::string &ControlFilePath); + // Returns a subset of 'Extra' that adds coverage to 'Initial'. + UnitVector FindExtraUnits(const UnitVector &Initial, const UnitVector &Extra); + MutationDispatcher &GetMD() { return MD; } + void PrintFinalStats(); + void SetMaxInputLen(size_t MaxInputLen); + void SetMaxMutationLen(size_t MaxMutationLen); + void RssLimitCallback(); + + // Public for tests. + void ResetCoverage(); + + bool InFuzzingThread() const { return IsMyThread; } + size_t GetCurrentUnitInFuzzingThead(const uint8_t **Data) const; + void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, + bool DuringInitialCorpusExecution); + + void HandleMalloc(size_t Size); + +private: + void AlarmCallback(); + void CrashCallback(); + void InterruptCallback(); + void MutateAndTestOne(); + void ReportNewCoverage(InputInfo *II, const Unit &U); + size_t RunOne(const Unit &U) { return RunOne(U.data(), U.size()); } + void WriteToOutputCorpus(const Unit &U); + void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix); + void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0); + void PrintStatusForNewUnit(const Unit &U); + void ShuffleCorpus(UnitVector *V); + void AddToCorpus(const Unit &U); + void CheckExitOnSrcPosOrItem(); + + // Trace-based fuzzing: we run a unit with some kind of tracing + // enabled and record potentially useful mutations. Then + // We apply these mutations one by one to the unit and run it again. + + // Start tracing; forget all previously proposed mutations. + void StartTraceRecording(); + // Stop tracing. + void StopTraceRecording(); + + void SetDeathCallback(); + static void StaticDeathCallback(); + void DumpCurrentUnit(const char *Prefix); + void DeathCallback(); + + void ResetEdgeCoverage(); + void ResetCounters(); + void PrepareCounters(Fuzzer::Coverage *C); + bool RecordMaxCoverage(Fuzzer::Coverage *C); + + void AllocateCurrentUnitData(); + uint8_t *CurrentUnitData = nullptr; + std::atomic CurrentUnitSize; + uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit. + bool RunningCB = false; + + size_t TotalNumberOfRuns = 0; + size_t NumberOfNewUnitsAdded = 0; + + bool HasMoreMallocsThanFrees = false; + size_t NumberOfLeakDetectionAttempts = 0; + + UserCallback CB; + InputCorpus &Corpus; + MutationDispatcher &MD; + FuzzingOptions Options; + + system_clock::time_point ProcessStartTime = system_clock::now(); + system_clock::time_point UnitStartTime, UnitStopTime; + long TimeOfLongestUnitInSeconds = 0; + long EpochOfLastReadOfOutputCorpus = 0; + + // Maximum recorded coverage. + Coverage MaxCoverage; + + size_t MaxInputLen = 0; + size_t MaxMutationLen = 0; + + // Need to know our own thread. + static thread_local bool IsMyThread; + + bool InMergeMode = false; +}; + +}; // namespace fuzzer + +#endif // LLVM_FUZZER_INTERNAL_H diff --git a/test/fuzz_test/Fuzzer/FuzzerLoop.cpp b/test/fuzz_test/Fuzzer/FuzzerLoop.cpp new file mode 100644 index 00000000..73e058ff --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerLoop.cpp @@ -0,0 +1,792 @@ +//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Fuzzer's main loop. +//===----------------------------------------------------------------------===// + +#include "FuzzerCorpus.h" +#include "FuzzerInternal.h" +#include "FuzzerIO.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include + +#if defined(__has_include) +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#endif + +#define NO_SANITIZE_MEMORY +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) +#undef NO_SANITIZE_MEMORY +#define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) +#endif +#endif + +namespace fuzzer { +static const size_t kMaxUnitSizeToPrint = 256; + +thread_local bool Fuzzer::IsMyThread; + +static void MissingExternalApiFunction(const char *FnName) { + Printf("ERROR: %s is not defined. Exiting.\n" + "Did you use -fsanitize-coverage=... to build your code?\n", + FnName); + exit(1); +} + +#define CHECK_EXTERNAL_FUNCTION(fn) \ + do { \ + if (!(EF->fn)) \ + MissingExternalApiFunction(#fn); \ + } while (false) + +// Only one Fuzzer per process. +static Fuzzer *F; + +void Fuzzer::ResetEdgeCoverage() { + CHECK_EXTERNAL_FUNCTION(__sanitizer_reset_coverage); + EF->__sanitizer_reset_coverage(); +} + +void Fuzzer::ResetCounters() { + if (Options.UseCounters) + EF->__sanitizer_update_counter_bitset_and_clear_counters(0); +} + +void Fuzzer::PrepareCounters(Fuzzer::Coverage *C) { + if (Options.UseCounters) { + size_t NumCounters = EF->__sanitizer_get_number_of_counters(); + C->CounterBitmap.resize(NumCounters); + } +} + +// Records data to a maximum coverage tracker. Returns true if additional +// coverage was discovered. +bool Fuzzer::RecordMaxCoverage(Fuzzer::Coverage *C) { + bool Res = false; + + uint64_t NewBlockCoverage = EF->__sanitizer_get_total_unique_coverage(); + if (NewBlockCoverage > C->BlockCoverage) { + Res = true; + C->BlockCoverage = NewBlockCoverage; + } + + if (Options.UseIndirCalls && + EF->__sanitizer_get_total_unique_caller_callee_pairs) { + uint64_t NewCallerCalleeCoverage = + EF->__sanitizer_get_total_unique_caller_callee_pairs(); + if (NewCallerCalleeCoverage > C->CallerCalleeCoverage) { + Res = true; + C->CallerCalleeCoverage = NewCallerCalleeCoverage; + } + } + + if (Options.UseCounters) { + uint64_t CounterDelta = + EF->__sanitizer_update_counter_bitset_and_clear_counters( + C->CounterBitmap.data()); + if (CounterDelta > 0) { + Res = true; + C->CounterBitmapBits += CounterDelta; + } + } + + return Res; +} + +// Leak detection is expensive, so we first check if there were more mallocs +// than frees (using the sanitizer malloc hooks) and only then try to call lsan. +struct MallocFreeTracer { + void Start(int TraceLevel) { + this->TraceLevel = TraceLevel; + if (TraceLevel) + Printf("MallocFreeTracer: START\n"); + Mallocs = 0; + Frees = 0; + } + // Returns true if there were more mallocs than frees. + bool Stop() { + if (TraceLevel) + Printf("MallocFreeTracer: STOP %zd %zd (%s)\n", Mallocs.load(), + Frees.load(), Mallocs == Frees ? "same" : "DIFFERENT"); + bool Result = Mallocs > Frees; + Mallocs = 0; + Frees = 0; + TraceLevel = 0; + return Result; + } + std::atomic Mallocs; + std::atomic Frees; + int TraceLevel = 0; +}; + +static MallocFreeTracer AllocTracer; + +ATTRIBUTE_NO_SANITIZE_MEMORY +void MallocHook(const volatile void *ptr, size_t size) { + size_t N = AllocTracer.Mallocs++; + F->HandleMalloc(size); + if (int TraceLevel = AllocTracer.TraceLevel) { + Printf("MALLOC[%zd] %p %zd\n", N, ptr, size); + if (TraceLevel >= 2 && EF) + EF->__sanitizer_print_stack_trace(); + } +} + +ATTRIBUTE_NO_SANITIZE_MEMORY +void FreeHook(const volatile void *ptr) { + size_t N = AllocTracer.Frees++; + if (int TraceLevel = AllocTracer.TraceLevel) { + Printf("FREE[%zd] %p\n", N, ptr); + if (TraceLevel >= 2 && EF) + EF->__sanitizer_print_stack_trace(); + } +} + +// Crash on a single malloc that exceeds the rss limit. +void Fuzzer::HandleMalloc(size_t Size) { + if (!Options.RssLimitMb || (Size >> 20) < (size_t)Options.RssLimitMb) + return; + Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(), + Size); + Printf(" To change the out-of-memory limit use -rss_limit_mb=\n\n"); + if (EF->__sanitizer_print_stack_trace) + EF->__sanitizer_print_stack_trace(); + DumpCurrentUnit("oom-"); + Printf("SUMMARY: libFuzzer: out-of-memory\n"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // Stop right now. +} + +Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, + FuzzingOptions Options) + : CB(CB), Corpus(Corpus), MD(MD), Options(Options) { + SetDeathCallback(); + InitializeTraceState(); + assert(!F); + F = this; + TPC.ResetMaps(); + ResetCoverage(); + IsMyThread = true; + if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks) + EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook); + TPC.SetUseCounters(Options.UseCounters); + TPC.SetUseValueProfile(Options.UseValueProfile); + TPC.SetPrintNewPCs(Options.PrintNewCovPcs); + + if (Options.Verbosity) + TPC.PrintModuleInfo(); + if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec) + EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus); + MaxInputLen = MaxMutationLen = Options.MaxLen; + AllocateCurrentUnitData(); + CurrentUnitSize = 0; + memset(BaseSha1, 0, sizeof(BaseSha1)); +} + +Fuzzer::~Fuzzer() { } + +void Fuzzer::AllocateCurrentUnitData() { + if (CurrentUnitData || MaxInputLen == 0) return; + CurrentUnitData = new uint8_t[MaxInputLen]; +} + +void Fuzzer::SetDeathCallback() { + CHECK_EXTERNAL_FUNCTION(__sanitizer_set_death_callback); + EF->__sanitizer_set_death_callback(StaticDeathCallback); +} + +void Fuzzer::StaticDeathCallback() { + assert(F); + F->DeathCallback(); +} + +static void WarnOnUnsuccessfullMerge(bool DoWarn) { + if (!DoWarn) return; + Printf( + "***\n" + "***\n" + "***\n" + "*** NOTE: merge did not succeed due to a failure on one of the inputs.\n" + "*** You will need to filter out crashes from the corpus, e.g. like this:\n" + "*** for f in WITH_CRASHES/*; do ./fuzzer $f && cp $f NO_CRASHES; done\n" + "*** Future versions may have crash-resistant merge, stay tuned.\n" + "***\n" + "***\n" + "***\n"); +} + +void Fuzzer::DumpCurrentUnit(const char *Prefix) { + WarnOnUnsuccessfullMerge(InMergeMode); + if (!CurrentUnitData) return; // Happens when running individual inputs. + MD.PrintMutationSequence(); + Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str()); + size_t UnitSize = CurrentUnitSize; + if (UnitSize <= kMaxUnitSizeToPrint) { + PrintHexArray(CurrentUnitData, UnitSize, "\n"); + PrintASCII(CurrentUnitData, UnitSize, "\n"); + } + WriteUnitToFileWithPrefix({CurrentUnitData, CurrentUnitData + UnitSize}, + Prefix); +} + +NO_SANITIZE_MEMORY +void Fuzzer::DeathCallback() { + DumpCurrentUnit("crash-"); + PrintFinalStats(); +} + +void Fuzzer::StaticAlarmCallback() { + assert(F); + F->AlarmCallback(); +} + +void Fuzzer::StaticCrashSignalCallback() { + assert(F); + F->CrashCallback(); +} + +void Fuzzer::StaticInterruptCallback() { + assert(F); + F->InterruptCallback(); +} + +void Fuzzer::CrashCallback() { + Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid()); + if (EF->__sanitizer_print_stack_trace) + EF->__sanitizer_print_stack_trace(); + Printf("NOTE: libFuzzer has rudimentary signal handlers.\n" + " Combine libFuzzer with AddressSanitizer or similar for better " + "crash reports.\n"); + Printf("SUMMARY: libFuzzer: deadly signal\n"); + DumpCurrentUnit("crash-"); + PrintFinalStats(); + exit(Options.ErrorExitCode); +} + +void Fuzzer::InterruptCallback() { + Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid()); + PrintFinalStats(); + _Exit(0); // Stop right now, don't perform any at-exit actions. +} + +NO_SANITIZE_MEMORY +void Fuzzer::AlarmCallback() { + assert(Options.UnitTimeoutSec > 0); + if (!InFuzzingThread()) return; + if (!RunningCB) + return; // We have not started running units yet. + size_t Seconds = + duration_cast(system_clock::now() - UnitStartTime).count(); + if (Seconds == 0) + return; + if (Options.Verbosity >= 2) + Printf("AlarmCallback %zd\n", Seconds); + if (Seconds >= (size_t)Options.UnitTimeoutSec) { + Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds); + Printf(" and the timeout value is %d (use -timeout=N to change)\n", + Options.UnitTimeoutSec); + DumpCurrentUnit("timeout-"); + Printf("==%lu== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(), + Seconds); + if (EF->__sanitizer_print_stack_trace) + EF->__sanitizer_print_stack_trace(); + Printf("SUMMARY: libFuzzer: timeout\n"); + PrintFinalStats(); + _Exit(Options.TimeoutExitCode); // Stop right now. + } +} + +void Fuzzer::RssLimitCallback() { + Printf( + "==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n", + GetPid(), GetPeakRSSMb(), Options.RssLimitMb); + Printf(" To change the out-of-memory limit use -rss_limit_mb=\n\n"); + if (EF->__sanitizer_print_memory_profile) + EF->__sanitizer_print_memory_profile(95); + DumpCurrentUnit("oom-"); + Printf("SUMMARY: libFuzzer: out-of-memory\n"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // Stop right now. +} + +void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) { + size_t ExecPerSec = execPerSec(); + if (Options.OutputCSV) { + static bool csvHeaderPrinted = false; + if (!csvHeaderPrinted) { + csvHeaderPrinted = true; + Printf("runs,block_cov,bits,cc_cov,corpus,execs_per_sec,tbms,reason\n"); + } + Printf("%zd,%zd,%zd,%zd,%zd,%zd,%s\n", TotalNumberOfRuns, + MaxCoverage.BlockCoverage, MaxCoverage.CounterBitmapBits, + MaxCoverage.CallerCalleeCoverage, Corpus.size(), ExecPerSec, Where); + } + + if (!Options.Verbosity) + return; + Printf("#%zd\t%s", TotalNumberOfRuns, Where); + if (MaxCoverage.BlockCoverage) + Printf(" cov: %zd", MaxCoverage.BlockCoverage); + if (size_t N = MaxCoverage.VPMap.GetNumBitsSinceLastMerge()) + Printf(" vp: %zd", N); + if (size_t N = TPC.GetTotalPCCoverage()) + Printf(" cov: %zd", N); + if (auto TB = MaxCoverage.CounterBitmapBits) + Printf(" bits: %zd", TB); + if (size_t N = Corpus.NumFeatures()) + Printf( " ft: %zd", N); + if (MaxCoverage.CallerCalleeCoverage) + Printf(" indir: %zd", MaxCoverage.CallerCalleeCoverage); + if (!Corpus.empty()) { + Printf(" corp: %zd", Corpus.NumActiveUnits()); + if (size_t N = Corpus.SizeInBytes()) { + if (N < (1<<14)) + Printf("/%zdb", N); + else if (N < (1 << 24)) + Printf("/%zdKb", N >> 10); + else + Printf("/%zdMb", N >> 20); + } + } + if (Units) + Printf(" units: %zd", Units); + + Printf(" exec/s: %zd", ExecPerSec); + Printf(" rss: %zdMb", GetPeakRSSMb()); + Printf("%s", End); +} + +void Fuzzer::PrintFinalStats() { + if (Options.PrintCoverage) + TPC.PrintCoverage(); + if (Options.DumpCoverage) + TPC.DumpCoverage(); + if (Options.PrintCorpusStats) + Corpus.PrintStats(); + if (!Options.PrintFinalStats) return; + size_t ExecPerSec = execPerSec(); + Printf("stat::number_of_executed_units: %zd\n", TotalNumberOfRuns); + Printf("stat::average_exec_per_sec: %zd\n", ExecPerSec); + Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded); + Printf("stat::slowest_unit_time_sec: %zd\n", TimeOfLongestUnitInSeconds); + Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb()); +} + +void Fuzzer::SetMaxInputLen(size_t MaxInputLen) { + assert(this->MaxInputLen == 0); // Can only reset MaxInputLen from 0 to non-0. + assert(MaxInputLen); + this->MaxInputLen = MaxInputLen; + this->MaxMutationLen = MaxInputLen; + AllocateCurrentUnitData(); + Printf("INFO: -max_len is not provided, using %zd\n", MaxInputLen); +} + +void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) { + assert(MaxMutationLen && MaxMutationLen <= MaxInputLen); + this->MaxMutationLen = MaxMutationLen; +} + +void Fuzzer::CheckExitOnSrcPosOrItem() { + if (!Options.ExitOnSrcPos.empty()) { + static auto *PCsSet = new std::set; + for (size_t i = 1, N = TPC.GetNumPCs(); i < N; i++) { + uintptr_t PC = TPC.GetPC(i); + if (!PC) continue; + if (!PCsSet->insert(PC).second) continue; + std::string Descr = DescribePC("%L", PC); + if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) { + Printf("INFO: found line matching '%s', exiting.\n", + Options.ExitOnSrcPos.c_str()); + _Exit(0); + } + } + } + if (!Options.ExitOnItem.empty()) { + if (Corpus.HasUnit(Options.ExitOnItem)) { + Printf("INFO: found item with checksum '%s', exiting.\n", + Options.ExitOnItem.c_str()); + _Exit(0); + } + } +} + +void Fuzzer::RereadOutputCorpus(size_t MaxSize) { + if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return; + std::vector AdditionalCorpus; + ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus, + &EpochOfLastReadOfOutputCorpus, MaxSize, + /*ExitOnError*/ false); + if (Options.Verbosity >= 2) + Printf("Reload: read %zd new units.\n", AdditionalCorpus.size()); + bool Reloaded = false; + for (auto &U : AdditionalCorpus) { + if (U.size() > MaxSize) + U.resize(MaxSize); + if (!Corpus.HasUnit(U)) { + if (size_t NumFeatures = RunOne(U)) { + CheckExitOnSrcPosOrItem(); + Corpus.AddToCorpus(U, NumFeatures); + Reloaded = true; + } + } + } + if (Reloaded) + PrintStats("RELOAD"); +} + +void Fuzzer::ShuffleCorpus(UnitVector *V) { + std::random_shuffle(V->begin(), V->end(), MD.GetRand()); + if (Options.PreferSmall) + std::stable_sort(V->begin(), V->end(), [](const Unit &A, const Unit &B) { + return A.size() < B.size(); + }); +} + +void Fuzzer::ShuffleAndMinimize(UnitVector *InitialCorpus) { + Printf("#0\tREAD units: %zd\n", InitialCorpus->size()); + if (Options.ShuffleAtStartUp) + ShuffleCorpus(InitialCorpus); + + // Test the callback with empty input and never try it again. + uint8_t dummy; + ExecuteCallback(&dummy, 0); + + for (const auto &U : *InitialCorpus) { + if (size_t NumFeatures = RunOne(U)) { + CheckExitOnSrcPosOrItem(); + Corpus.AddToCorpus(U, NumFeatures); + if (Options.Verbosity >= 2) + Printf("NEW0: %zd L %zd\n", MaxCoverage.BlockCoverage, U.size()); + } + TryDetectingAMemoryLeak(U.data(), U.size(), + /*DuringInitialCorpusExecution*/ true); + } + PrintStats("INITED"); + if (Corpus.empty()) { + Printf("ERROR: no interesting inputs were found. " + "Is the code instrumented for coverage? Exiting.\n"); + exit(1); + } +} + +size_t Fuzzer::RunOne(const uint8_t *Data, size_t Size) { + if (!Size) return 0; + TotalNumberOfRuns++; + + ExecuteCallback(Data, Size); + + size_t Res = 0; + if (size_t NumFeatures = TPC.CollectFeatures([&](size_t Feature) -> bool { + return Corpus.AddFeature(Feature, Size, Options.Shrink); + })) + Res = NumFeatures; + + if (!TPC.UsingTracePcGuard()) { + if (TPC.UpdateValueProfileMap(&MaxCoverage.VPMap)) + Res = 1; + if (!Res && RecordMaxCoverage(&MaxCoverage)) + Res = 1; + } + + auto TimeOfUnit = + duration_cast(UnitStopTime - UnitStartTime).count(); + if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && + secondsSinceProcessStartUp() >= 2) + PrintStats("pulse "); + if (TimeOfUnit > TimeOfLongestUnitInSeconds * 1.1 && + TimeOfUnit >= Options.ReportSlowUnits) { + TimeOfLongestUnitInSeconds = TimeOfUnit; + Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds); + WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-"); + } + return Res; +} + +size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { + assert(InFuzzingThread()); + *Data = CurrentUnitData; + return CurrentUnitSize; +} + +void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { + assert(InFuzzingThread()); + // We copy the contents of Unit into a separate heap buffer + // so that we reliably find buffer overflows in it. + uint8_t *DataCopy = new uint8_t[Size]; + memcpy(DataCopy, Data, Size); + if (CurrentUnitData && CurrentUnitData != Data) + memcpy(CurrentUnitData, Data, Size); + CurrentUnitSize = Size; + AllocTracer.Start(Options.TraceMalloc); + UnitStartTime = system_clock::now(); + ResetCounters(); // Reset coverage right before the callback. + TPC.ResetMaps(); + RunningCB = true; + int Res = CB(DataCopy, Size); + RunningCB = false; + UnitStopTime = system_clock::now(); + (void)Res; + assert(Res == 0); + HasMoreMallocsThanFrees = AllocTracer.Stop(); + CurrentUnitSize = 0; + delete[] DataCopy; +} + +void Fuzzer::WriteToOutputCorpus(const Unit &U) { + if (Options.OnlyASCII) + assert(IsASCII(U)); + if (Options.OutputCorpus.empty()) + return; + std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U)); + WriteToFile(U, Path); + if (Options.Verbosity >= 2) + Printf("Written to %s\n", Path.c_str()); +} + +void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) { + if (!Options.SaveArtifacts) + return; + std::string Path = Options.ArtifactPrefix + Prefix + Hash(U); + if (!Options.ExactArtifactPath.empty()) + Path = Options.ExactArtifactPath; // Overrides ArtifactPrefix. + WriteToFile(U, Path); + Printf("artifact_prefix='%s'; Test unit written to %s\n", + Options.ArtifactPrefix.c_str(), Path.c_str()); + if (U.size() <= kMaxUnitSizeToPrint) + Printf("Base64: %s\n", Base64(U).c_str()); +} + +void Fuzzer::PrintStatusForNewUnit(const Unit &U) { + if (!Options.PrintNEW) + return; + PrintStats("NEW ", ""); + if (Options.Verbosity) { + Printf(" L: %zd ", U.size()); + MD.PrintMutationSequence(); + Printf("\n"); + } +} + +void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) { + II->NumSuccessfullMutations++; + MD.RecordSuccessfulMutationSequence(); + PrintStatusForNewUnit(U); + WriteToOutputCorpus(U); + NumberOfNewUnitsAdded++; + TPC.PrintNewPCs(); +} + +// Finds minimal number of units in 'Extra' that add coverage to 'Initial'. +// We do it by actually executing the units, sometimes more than once, +// because we may be using different coverage-like signals and the only +// common thing between them is that we can say "this unit found new stuff". +UnitVector Fuzzer::FindExtraUnits(const UnitVector &Initial, + const UnitVector &Extra) { + UnitVector Res = Extra; + UnitVector Tmp; + size_t OldSize = Res.size(); + for (int Iter = 0; Iter < 10; Iter++) { + ShuffleCorpus(&Res); + TPC.ResetMaps(); + Corpus.ResetFeatureSet(); + ResetCoverage(); + + for (auto &U : Initial) { + TPC.ResetMaps(); + RunOne(U); + } + + Tmp.clear(); + for (auto &U : Res) { + TPC.ResetMaps(); + if (RunOne(U)) + Tmp.push_back(U); + } + + char Stat[7] = "MIN "; + Stat[3] = '0' + Iter; + PrintStats(Stat, "\n", Tmp.size()); + + size_t NewSize = Tmp.size(); + assert(NewSize <= OldSize); + Res.swap(Tmp); + + if (NewSize + 5 >= OldSize) + break; + OldSize = NewSize; + } + return Res; +} + +void Fuzzer::Merge(const std::vector &Corpora) { + if (Corpora.size() <= 1) { + Printf("Merge requires two or more corpus dirs\n"); + return; + } + InMergeMode = true; + std::vector ExtraCorpora(Corpora.begin() + 1, Corpora.end()); + + assert(MaxInputLen > 0); + UnitVector Initial, Extra; + ReadDirToVectorOfUnits(Corpora[0].c_str(), &Initial, nullptr, MaxInputLen, + true); + for (auto &C : ExtraCorpora) + ReadDirToVectorOfUnits(C.c_str(), &Extra, nullptr, MaxInputLen, true); + + if (!Initial.empty()) { + Printf("=== Minimizing the initial corpus of %zd units\n", Initial.size()); + Initial = FindExtraUnits({}, Initial); + } + + Printf("=== Merging extra %zd units\n", Extra.size()); + auto Res = FindExtraUnits(Initial, Extra); + + for (auto &U: Res) + WriteToOutputCorpus(U); + + Printf("=== Merge: written %zd units\n", Res.size()); +} + +// Tries detecting a memory leak on the particular input that we have just +// executed before calling this function. +void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, + bool DuringInitialCorpusExecution) { + if (!HasMoreMallocsThanFrees) return; // mallocs==frees, a leak is unlikely. + if (!Options.DetectLeaks) return; + if (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) || + !(EF->__lsan_do_recoverable_leak_check)) + return; // No lsan. + // Run the target once again, but with lsan disabled so that if there is + // a real leak we do not report it twice. + EF->__lsan_disable(); + ExecuteCallback(Data, Size); + EF->__lsan_enable(); + if (!HasMoreMallocsThanFrees) return; // a leak is unlikely. + if (NumberOfLeakDetectionAttempts++ > 1000) { + Options.DetectLeaks = false; + Printf("INFO: libFuzzer disabled leak detection after every mutation.\n" + " Most likely the target function accumulates allocated\n" + " memory in a global state w/o actually leaking it.\n" + " You may try running this binary with -trace_malloc=[12]" + " to get a trace of mallocs and frees.\n" + " If LeakSanitizer is enabled in this process it will still\n" + " run on the process shutdown.\n"); + return; + } + // Now perform the actual lsan pass. This is expensive and we must ensure + // we don't call it too often. + if (EF->__lsan_do_recoverable_leak_check()) { // Leak is found, report it. + if (DuringInitialCorpusExecution) + Printf("\nINFO: a leak has been found in the initial corpus.\n\n"); + Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n"); + CurrentUnitSize = Size; + DumpCurrentUnit("leak-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // not exit() to disable lsan further on. + } +} + +void Fuzzer::MutateAndTestOne() { + MD.StartMutationSequence(); + + auto &II = Corpus.ChooseUnitToMutate(MD.GetRand()); + const auto &U = II.U; + memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1)); + assert(CurrentUnitData); + size_t Size = U.size(); + assert(Size <= MaxInputLen && "Oversized Unit"); + memcpy(CurrentUnitData, U.data(), Size); + + assert(MaxMutationLen > 0); + + for (int i = 0; i < Options.MutateDepth; i++) { + if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) + break; + size_t NewSize = 0; + NewSize = MD.Mutate(CurrentUnitData, Size, MaxMutationLen); + assert(NewSize > 0 && "Mutator returned empty unit"); + assert(NewSize <= MaxMutationLen && "Mutator return overisized unit"); + Size = NewSize; + if (i == 0) + StartTraceRecording(); + II.NumExecutedMutations++; + if (size_t NumFeatures = RunOne(CurrentUnitData, Size)) { + Corpus.AddToCorpus({CurrentUnitData, CurrentUnitData + Size}, NumFeatures, + /*MayDeleteFile=*/true); + ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size}); + CheckExitOnSrcPosOrItem(); + } + StopTraceRecording(); + TryDetectingAMemoryLeak(CurrentUnitData, Size, + /*DuringInitialCorpusExecution*/ false); + } +} + +void Fuzzer::ResetCoverage() { + ResetEdgeCoverage(); + MaxCoverage.Reset(); + PrepareCounters(&MaxCoverage); +} + +void Fuzzer::Loop() { + system_clock::time_point LastCorpusReload = system_clock::now(); + if (Options.DoCrossOver) + MD.SetCorpus(&Corpus); + while (true) { + auto Now = system_clock::now(); + if (duration_cast(Now - LastCorpusReload).count() >= + Options.ReloadIntervalSec) { + RereadOutputCorpus(MaxInputLen); + LastCorpusReload = system_clock::now(); + } + if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) + break; + if (TimedOut()) break; + // Perform several mutations and runs. + MutateAndTestOne(); + } + + PrintStats("DONE ", "\n"); + MD.PrintRecommendedDictionary(); +} + +void Fuzzer::MinimizeCrashLoop(const Unit &U) { + if (U.size() <= 2) return; + while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) { + MD.StartMutationSequence(); + memcpy(CurrentUnitData, U.data(), U.size()); + for (int i = 0; i < Options.MutateDepth; i++) { + size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen); + assert(NewSize > 0 && NewSize <= MaxMutationLen); + RunOne(CurrentUnitData, NewSize); + TryDetectingAMemoryLeak(CurrentUnitData, NewSize, + /*DuringInitialCorpusExecution*/ false); + } + } +} + +} // namespace fuzzer + +extern "C" { + +size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { + assert(fuzzer::F); + return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize); +} +} // extern "C" diff --git a/test/fuzz_test/Fuzzer/FuzzerMain.cpp b/test/fuzz_test/Fuzzer/FuzzerMain.cpp new file mode 100644 index 00000000..af865720 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerMain.cpp @@ -0,0 +1,21 @@ +//===- FuzzerMain.cpp - main() function and flags -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// main() and flags. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" + +extern "C" { +// This function should be defined by the user. +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +} // extern "C" + +int main(int argc, char **argv) { + return fuzzer::FuzzerDriver(&argc, &argv, LLVMFuzzerTestOneInput); +} diff --git a/test/fuzz_test/Fuzzer/FuzzerMerge.cpp b/test/fuzz_test/Fuzzer/FuzzerMerge.cpp new file mode 100644 index 00000000..84660e0f --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerMerge.cpp @@ -0,0 +1,261 @@ +//===- FuzzerMerge.cpp - merging corpora ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Merging corpora. +//===----------------------------------------------------------------------===// + +#include "FuzzerInternal.h" +#include "FuzzerIO.h" +#include "FuzzerMerge.h" +#include "FuzzerTracePC.h" +#include "FuzzerUtil.h" + +#include +#include +#include + +namespace fuzzer { + +bool Merger::Parse(const std::string &Str, bool ParseCoverage) { + std::istringstream SS(Str); + return Parse(SS, ParseCoverage); +} + +void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) { + if (!Parse(IS, ParseCoverage)) { + Printf("MERGE: failed to parse the control file (unexpected error)\n"); + exit(1); + } +} + +// The control file example: +// +// 3 # The number of inputs +// 1 # The number of inputs in the first corpus, <= the previous number +// file0 +// file1 +// file2 # One file name per line. +// STARTED 0 123 # FileID, file size +// DONE 0 1 4 6 8 # FileID COV1 COV2 ... +// STARTED 1 456 # If DONE is missing, the input crashed while processing. +// STARTED 2 567 +// DONE 2 8 9 +bool Merger::Parse(std::istream &IS, bool ParseCoverage) { + LastFailure.clear(); + std::string Line; + + // Parse NumFiles. + if (!std::getline(IS, Line, '\n')) return false; + std::istringstream L1(Line); + size_t NumFiles = 0; + L1 >> NumFiles; + if (NumFiles == 0 || NumFiles > 10000000) return false; + + // Parse NumFilesInFirstCorpus. + if (!std::getline(IS, Line, '\n')) return false; + std::istringstream L2(Line); + NumFilesInFirstCorpus = NumFiles + 1; + L2 >> NumFilesInFirstCorpus; + if (NumFilesInFirstCorpus > NumFiles) return false; + + // Parse file names. + Files.resize(NumFiles); + for (size_t i = 0; i < NumFiles; i++) + if (!std::getline(IS, Files[i].Name, '\n')) + return false; + + // Parse STARTED and DONE lines. + size_t ExpectedStartMarker = 0; + const size_t kInvalidStartMarker = -1; + size_t LastSeenStartMarker = kInvalidStartMarker; + while (std::getline(IS, Line, '\n')) { + std::istringstream ISS1(Line); + std::string Marker; + size_t N; + ISS1 >> Marker; + ISS1 >> N; + if (Marker == "STARTED") { + // STARTED FILE_ID FILE_SIZE + if (ExpectedStartMarker != N) + return false; + ISS1 >> Files[ExpectedStartMarker].Size; + LastSeenStartMarker = ExpectedStartMarker; + assert(ExpectedStartMarker < Files.size()); + ExpectedStartMarker++; + } else if (Marker == "DONE") { + // DONE FILE_SIZE COV1 COV2 COV3 ... + size_t CurrentFileIdx = N; + if (CurrentFileIdx != LastSeenStartMarker) + return false; + LastSeenStartMarker = kInvalidStartMarker; + if (ParseCoverage) { + auto &V = Files[CurrentFileIdx].Features; + V.clear(); + while (ISS1 >> std::hex >> N) + V.push_back(N); + std::sort(V.begin(), V.end()); + } + } else { + return false; + } + } + if (LastSeenStartMarker != kInvalidStartMarker) + LastFailure = Files[LastSeenStartMarker].Name; + + FirstNotProcessedFile = ExpectedStartMarker; + return true; +} + +// Decides which files need to be merged (add thost to NewFiles). +// Returns the number of new features added. +size_t Merger::Merge(std::vector *NewFiles) { + NewFiles->clear(); + assert(NumFilesInFirstCorpus <= Files.size()); + std::set AllFeatures; + + // What features are in the initial corpus? + for (size_t i = 0; i < NumFilesInFirstCorpus; i++) { + auto &Cur = Files[i].Features; + AllFeatures.insert(Cur.begin(), Cur.end()); + } + size_t InitialNumFeatures = AllFeatures.size(); + + // Remove all features that we already know from all other inputs. + for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) { + auto &Cur = Files[i].Features; + std::vector Tmp; + std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(), + AllFeatures.end(), std::inserter(Tmp, Tmp.begin())); + Cur.swap(Tmp); + } + + // Sort. Give preference to + // * smaller files + // * files with more features. + std::sort(Files.begin() + NumFilesInFirstCorpus, Files.end(), + [&](const MergeFileInfo &a, const MergeFileInfo &b) -> bool { + if (a.Size != b.Size) + return a.Size < b.Size; + return a.Features.size() > b.Features.size(); + }); + + // One greedy pass: add the file's features to AllFeatures. + // If new features were added, add this file to NewFiles. + for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) { + auto &Cur = Files[i].Features; + // Printf("%s -> sz %zd ft %zd\n", Files[i].Name.c_str(), + // Files[i].Size, Cur.size()); + size_t OldSize = AllFeatures.size(); + AllFeatures.insert(Cur.begin(), Cur.end()); + if (AllFeatures.size() > OldSize) + NewFiles->push_back(Files[i].Name); + } + return AllFeatures.size() - InitialNumFeatures; +} + +// Inner process. May crash if the target crashes. +void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { + Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str()); + Merger M; + std::ifstream IF(CFPath); + M.ParseOrExit(IF, false); + IF.close(); + if (!M.LastFailure.empty()) + Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n", + M.LastFailure.c_str()); + + Printf("MERGE-INNER: %zd total files;" + " %zd processed earlier; will process %zd files now\n", + M.Files.size(), M.FirstNotProcessedFile, + M.Files.size() - M.FirstNotProcessedFile); + + std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app); + for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) { + auto U = FileToVector(M.Files[i].Name); + if (U.size() > MaxInputLen) { + U.resize(MaxInputLen); + U.shrink_to_fit(); + } + std::ostringstream StartedLine; + // Write the pre-run marker. + OF << "STARTED " << std::dec << i << " " << U.size() << "\n"; + OF.flush(); // Flush is important since ExecuteCommand may crash. + // Run. + TPC.ResetMaps(); + ExecuteCallback(U.data(), U.size()); + // Collect coverage. + std::set Features; + TPC.CollectFeatures([&](size_t Feature) -> bool { + Features.insert(Feature); + return true; + }); + // Show stats. + TotalNumberOfRuns++; + if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1))) + PrintStats("pulse "); + // Write the post-run marker and the coverage. + OF << "DONE " << i; + for (size_t F : Features) + OF << " " << std::hex << F; + OF << "\n"; + } +} + +// Outer process. Does not call the target code and thus sohuld not fail. +void Fuzzer::CrashResistantMerge(const std::vector &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); + std::string CFPath = + "libFuzzerTemp." + std::to_string(GetPid()) + ".txt"; + // Write the control file. + RemoveFile(CFPath); + std::ofstream ControlFile(CFPath); + ControlFile << AllFiles.size() << "\n"; + ControlFile << NumFilesInFirstCorpus << "\n"; + for (auto &Path: AllFiles) + ControlFile << Path << "\n"; + ControlFile.close(); + + // Execute the inner process untill it passes. + // Every inner process should execute at least one input. + std::string BaseCmd = CloneArgsWithoutX(Args, "keep-all-flags"); + for (size_t i = 1; i <= AllFiles.size(); i++) { + Printf("MERGE-OUTER: attempt %zd\n", i); + auto ExitCode = + ExecuteCommand(BaseCmd + " -merge_control_file=" + CFPath); + if (!ExitCode) { + Printf("MERGE-OUTER: succesfull in %zd attempt(s)\n", i); + break; + } + } + // Read the control file and do the merge. + Merger M; + std::ifstream IF(CFPath); + M.ParseOrExit(IF, true); + IF.close(); + std::vector NewFiles; + size_t NumNewFeatures = M.Merge(&NewFiles); + Printf("MERGE-OUTER: %zd new files with %zd new features added\n", + NewFiles.size(), NumNewFeatures); + for (auto &F: NewFiles) + WriteToOutputCorpus(FileToVector(F)); + // We are done, delete the control file. + RemoveFile(CFPath); +} + +} // namespace fuzzer diff --git a/test/fuzz_test/Fuzzer/FuzzerMerge.h b/test/fuzz_test/Fuzzer/FuzzerMerge.h new file mode 100644 index 00000000..8a2fe5d7 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerMerge.h @@ -0,0 +1,70 @@ +//===- FuzzerMerge.h - merging corpa ----------------------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Merging Corpora. +// +// The task: +// Take the existing corpus (possibly empty) and merge new inputs into +// it so that only inputs with new coverage ('features') are added. +// The process should tolerate the crashes, OOMs, leaks, etc. +// +// Algorithm: +// The outter process collects the set of files and writes their names +// into a temporary "control" file, then repeatedly launches the inner +// process until all inputs are processed. +// The outer process does not actually execute the target code. +// +// The inner process reads the control file and sees a) list of all the inputs +// and b) the last processed input. Then it starts processing the inputs one +// by one. Before processing every input it writes one line to control file: +// STARTED INPUT_ID INPUT_SIZE +// After processing an input it write another line: +// DONE INPUT_ID Feature1 Feature2 Feature3 ... +// If a crash happens while processing an input the last line in the control +// file will be "STARTED INPUT_ID" and so the next process will know +// where to resume. +// +// Once all inputs are processed by the innner process(es) the outer process +// reads the control files and does the merge based entirely on the contents +// of control file. +// It uses a single pass greedy algorithm choosing first the smallest inputs +// within the same size the inputs that have more new features. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_MERGE_H +#define LLVM_FUZZER_MERGE_H + +#include "FuzzerDefs.h" + +#include +#include + +namespace fuzzer { + +struct MergeFileInfo { + std::string Name; + size_t Size = 0; + std::vector Features; +}; + +struct Merger { + std::vector Files; + size_t NumFilesInFirstCorpus = 0; + size_t FirstNotProcessedFile = 0; + std::string LastFailure; + + bool Parse(std::istream &IS, bool ParseCoverage); + bool Parse(const std::string &Str, bool ParseCoverage); + void ParseOrExit(std::istream &IS, bool ParseCoverage); + size_t Merge(std::vector *NewFiles); +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_MERGE_H diff --git a/test/fuzz_test/Fuzzer/FuzzerMutate.cpp b/test/fuzz_test/Fuzzer/FuzzerMutate.cpp new file mode 100644 index 00000000..80b7a5f9 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerMutate.cpp @@ -0,0 +1,527 @@ +//===- FuzzerMutate.cpp - Mutate a test input -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Mutate a test input. +//===----------------------------------------------------------------------===// + +#include "FuzzerCorpus.h" +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerMutate.h" +#include "FuzzerOptions.h" + +namespace fuzzer { + +const size_t Dictionary::kMaxDictSize; + +static void PrintASCII(const Word &W, const char *PrintAfter) { + PrintASCII(W.data(), W.size(), PrintAfter); +} + +MutationDispatcher::MutationDispatcher(Random &Rand, + const FuzzingOptions &Options) + : Rand(Rand), Options(Options) { + DefaultMutators.insert( + DefaultMutators.begin(), + { + {&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"}, + {&MutationDispatcher::Mutate_InsertByte, "InsertByte"}, + {&MutationDispatcher::Mutate_InsertRepeatedBytes, + "InsertRepeatedBytes"}, + {&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"}, + {&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"}, + {&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"}, + {&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"}, + {&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"}, + {&MutationDispatcher::Mutate_CopyPart, "CopyPart"}, + {&MutationDispatcher::Mutate_CrossOver, "CrossOver"}, + {&MutationDispatcher::Mutate_AddWordFromManualDictionary, + "ManualDict"}, + {&MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary, + "TempAutoDict"}, + {&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary, + "PersAutoDict"}, + }); + if(Options.UseCmp) + DefaultMutators.push_back( + {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"}); + + if (EF->LLVMFuzzerCustomMutator) + Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"}); + else + Mutators = DefaultMutators; + + if (EF->LLVMFuzzerCustomCrossOver) + Mutators.push_back( + {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"}); +} + +static char RandCh(Random &Rand) { + if (Rand.RandBool()) return Rand(256); + const char *Special = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00"; + return Special[Rand(sizeof(Special) - 1)]; +} + +size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size, + size_t MaxSize) { + return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand()); +} + +size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (!Corpus || Corpus->size() < 2 || Size == 0) + return 0; + size_t Idx = Rand(Corpus->size()); + const Unit &Other = (*Corpus)[Idx]; + if (Other.empty()) + return 0; + MutateInPlaceHere.resize(MaxSize); + auto &U = MutateInPlaceHere; + size_t NewSize = EF->LLVMFuzzerCustomCrossOver( + Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand()); + if (!NewSize) + return 0; + assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit"); + memcpy(Data, U.data(), NewSize); + return NewSize; +} + +size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (Size > MaxSize) return 0; + assert(Size); + size_t ShuffleAmount = + Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size. + size_t ShuffleStart = Rand(Size - ShuffleAmount); + assert(ShuffleStart + ShuffleAmount <= Size); + std::random_shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, + Rand); + return Size; +} + +size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size, + size_t MaxSize) { + assert(Size); + if (Size == 1) return 0; + size_t N = Rand(Size / 2) + 1; + assert(N < Size); + size_t Idx = Rand(Size - N + 1); + // Erase Data[Idx:Idx+N]. + memmove(Data + Idx, Data + Idx + N, Size - Idx - N); + // Printf("Erase: %zd %zd => %zd; Idx %zd\n", N, Size, Size - N, Idx); + return Size - N; +} + +size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (Size >= MaxSize) return 0; + size_t Idx = Rand(Size + 1); + // Insert new value at Data[Idx]. + memmove(Data + Idx + 1, Data + Idx, Size - Idx); + Data[Idx] = RandCh(Rand); + return Size + 1; +} + +size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data, + size_t Size, + size_t MaxSize) { + const size_t kMinBytesToInsert = 3; + if (Size + kMinBytesToInsert >= MaxSize) return 0; + size_t MaxBytesToInsert = std::min(MaxSize - Size, (size_t)128); + size_t N = Rand(MaxBytesToInsert - kMinBytesToInsert + 1) + kMinBytesToInsert; + assert(Size + N <= MaxSize && N); + size_t Idx = Rand(Size + 1); + // Insert new values at Data[Idx]. + memmove(Data + Idx + N, Data + Idx, Size - Idx); + // Give preference to 0x00 and 0xff. + uint8_t Byte = Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255); + for (size_t i = 0; i < N; i++) + Data[Idx + i] = Byte; + return Size + N; +} + +size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (Size > MaxSize) return 0; + size_t Idx = Rand(Size); + Data[Idx] = RandCh(Rand); + return Size; +} + +size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (Size > MaxSize) return 0; + size_t Idx = Rand(Size); + Data[Idx] ^= 1 << Rand(8); + return Size; +} + +size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data, + size_t Size, + size_t MaxSize) { + return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize); +} + +size_t MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary( + uint8_t *Data, size_t Size, size_t MaxSize) { + return AddWordFromDictionary(TempAutoDictionary, Data, Size, MaxSize); +} + +size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size, + size_t MaxSize, + DictionaryEntry &DE) { + const Word &W = DE.GetW(); + bool UsePositionHint = DE.HasPositionHint() && + DE.GetPositionHint() + W.size() < Size && + Rand.RandBool(); + if (Rand.RandBool()) { // Insert W. + if (Size + W.size() > MaxSize) return 0; + size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1); + memmove(Data + Idx + W.size(), Data + Idx, Size - Idx); + memcpy(Data + Idx, W.data(), W.size()); + Size += W.size(); + } else { // Overwrite some bytes with W. + if (W.size() > Size) return 0; + size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size - W.size()); + memcpy(Data + Idx, W.data(), W.size()); + } + return Size; +} + +// Somewhere in the past we have observed a comparison instructions +// with arguments Arg1 Arg2. This function tries to guess a dictionary +// entry that will satisfy that comparison. +// It first tries to find one of the arguments (possibly swapped) in the +// input and if it succeeds it creates a DE with a position hint. +// Otherwise it creates a DE with one of the arguments w/o a position hint. +template +DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( + T Arg1, T Arg2, const uint8_t *Data, size_t Size) { + ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem; + bool HandleFirst = Rand.RandBool(); + T ExistingBytes, DesiredBytes; + Word W; + const uint8_t *End = Data + Size; + for (int Arg = 0; Arg < 2; Arg++) { + ExistingBytes = HandleFirst ? Arg1 : Arg2; + DesiredBytes = HandleFirst ? Arg2 : Arg1; + DesiredBytes += Rand(-1, 1); + if (Rand.RandBool()) ExistingBytes = Bswap(ExistingBytes); + if (Rand.RandBool()) DesiredBytes = Bswap(DesiredBytes); + HandleFirst = !HandleFirst; + W.Set(reinterpret_cast(&DesiredBytes), sizeof(T)); + const size_t kMaxNumPositions = 8; + size_t Positions[kMaxNumPositions]; + size_t NumPositions = 0; + for (const uint8_t *Cur = Data; + Cur < End && NumPositions < kMaxNumPositions; Cur++) { + Cur = (uint8_t *)SearchMemory(Cur, End - Cur, &ExistingBytes, sizeof(T)); + if (!Cur) break; + Positions[NumPositions++] = Cur - Data; + } + if (!NumPositions) break; + return DictionaryEntry(W, Positions[Rand(NumPositions)]); + } + DictionaryEntry DE(W); + return DE; +} + +size_t MutationDispatcher::Mutate_AddWordFromTORC( + uint8_t *Data, size_t Size, size_t MaxSize) { + Word W; + DictionaryEntry DE; + if (Rand.RandBool()) { + auto X = TPC.TORC8.Get(Rand.Rand()); + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + } else { + auto X = TPC.TORC4.Get(Rand.Rand()); + if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool()) + DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data, + Size); + else + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + } + Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE); + if (!Size) return 0; + DictionaryEntry &DERef = + CmpDictionaryEntriesDeque[CmpDictionaryEntriesDequeIdx++ % + kCmpDictionaryEntriesDequeSize]; + DERef = DE; + CurrentDictionaryEntrySequence.push_back(&DERef); + return Size; +} + +size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary( + uint8_t *Data, size_t Size, size_t MaxSize) { + return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize); +} + +size_t MutationDispatcher::AddWordFromDictionary(Dictionary &D, uint8_t *Data, + size_t Size, size_t MaxSize) { + if (Size > MaxSize) return 0; + if (D.empty()) return 0; + DictionaryEntry &DE = D[Rand(D.size())]; + Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE); + if (!Size) return 0; + DE.IncUseCount(); + CurrentDictionaryEntrySequence.push_back(&DE); + return Size; +} + +// Overwrites part of To[0,ToSize) with a part of From[0,FromSize). +// Returns ToSize. +size_t MutationDispatcher::CopyPartOf(const uint8_t *From, size_t FromSize, + uint8_t *To, size_t ToSize) { + // Copy From[FromBeg, FromBeg + CopySize) into To[ToBeg, ToBeg + CopySize). + size_t ToBeg = Rand(ToSize); + size_t CopySize = Rand(ToSize - ToBeg) + 1; + assert(ToBeg + CopySize <= ToSize); + CopySize = std::min(CopySize, FromSize); + size_t FromBeg = Rand(FromSize - CopySize + 1); + assert(FromBeg + CopySize <= FromSize); + memmove(To + ToBeg, From + FromBeg, CopySize); + return ToSize; +} + +// Inserts part of From[0,ToSize) into To. +// Returns new size of To on success or 0 on failure. +size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize, + uint8_t *To, size_t ToSize, + size_t MaxToSize) { + if (ToSize >= MaxToSize) return 0; + size_t AvailableSpace = MaxToSize - ToSize; + size_t MaxCopySize = std::min(AvailableSpace, FromSize); + size_t CopySize = Rand(MaxCopySize) + 1; + size_t FromBeg = Rand(FromSize - CopySize + 1); + assert(FromBeg + CopySize <= FromSize); + size_t ToInsertPos = Rand(ToSize + 1); + assert(ToInsertPos + CopySize <= MaxToSize); + size_t TailSize = ToSize - ToInsertPos; + if (To == From) { + MutateInPlaceHere.resize(MaxToSize); + memcpy(MutateInPlaceHere.data(), From + FromBeg, CopySize); + memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize); + memmove(To + ToInsertPos, MutateInPlaceHere.data(), CopySize); + } else { + memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize); + memmove(To + ToInsertPos, From + FromBeg, CopySize); + } + return ToSize + CopySize; +} + +size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (Size > MaxSize) return 0; + if (Rand.RandBool()) + return CopyPartOf(Data, Size, Data, Size); + else + return InsertPartOf(Data, Size, Data, Size, MaxSize); +} + +size_t MutationDispatcher::Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (Size > MaxSize) return 0; + size_t B = Rand(Size); + while (B < Size && !isdigit(Data[B])) B++; + if (B == Size) return 0; + size_t E = B; + while (E < Size && isdigit(Data[E])) E++; + assert(B < E); + // now we have digits in [B, E). + // strtol and friends don't accept non-zero-teminated data, parse it manually. + uint64_t Val = Data[B] - '0'; + for (size_t i = B + 1; i < E; i++) + Val = Val * 10 + Data[i] - '0'; + + // Mutate the integer value. + switch(Rand(5)) { + case 0: Val++; break; + case 1: Val--; break; + case 2: Val /= 2; break; + case 3: Val *= 2; break; + case 4: Val = Rand(Val * Val); break; + default: assert(0); + } + // Just replace the bytes with the new ones, don't bother moving bytes. + for (size_t i = B; i < E; i++) { + size_t Idx = E + B - i - 1; + assert(Idx >= B && Idx < E); + Data[Idx] = (Val % 10) + '0'; + Val /= 10; + } + return Size; +} + +template +size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) { + if (Size < sizeof(T)) return 0; + size_t Off = Rand(Size - sizeof(T) + 1); + assert(Off + sizeof(T) <= Size); + T Val; + if (Off < 64 && !Rand(4)) { + Val = Size; + if (Rand.RandBool()) + Val = Bswap(Val); + } else { + memcpy(&Val, Data + Off, sizeof(Val)); + T Add = Rand(21); + Add -= 10; + if (Rand.RandBool()) + Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes. + else + Val = Val + Add; // Add assuming current endiannes. + if (Add == 0 || Rand.RandBool()) // Maybe negate. + Val = -Val; + } + memcpy(Data + Off, &Val, sizeof(Val)); + return Size; +} + +size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data, + size_t Size, + size_t MaxSize) { + if (Size > MaxSize) return 0; + switch (Rand(4)) { + case 3: return ChangeBinaryInteger(Data, Size, Rand); + case 2: return ChangeBinaryInteger(Data, Size, Rand); + case 1: return ChangeBinaryInteger(Data, Size, Rand); + case 0: return ChangeBinaryInteger(Data, Size, Rand); + default: assert(0); + } + return 0; +} + +size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (Size > MaxSize) return 0; + if (!Corpus || Corpus->size() < 2 || Size == 0) return 0; + size_t Idx = Rand(Corpus->size()); + const Unit &O = (*Corpus)[Idx]; + if (O.empty()) return 0; + MutateInPlaceHere.resize(MaxSize); + auto &U = MutateInPlaceHere; + size_t NewSize = 0; + switch(Rand(3)) { + case 0: + NewSize = CrossOver(Data, Size, O.data(), O.size(), U.data(), U.size()); + break; + case 1: + NewSize = InsertPartOf(O.data(), O.size(), U.data(), U.size(), MaxSize); + if (NewSize) + break; + // LLVM_FALLTHROUGH; + case 2: + NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size()); + break; + default: assert(0); + } + assert(NewSize > 0 && "CrossOver returned empty unit"); + assert(NewSize <= MaxSize && "CrossOver returned overisized unit"); + memcpy(Data, U.data(), NewSize); + return NewSize; +} + +void MutationDispatcher::StartMutationSequence() { + CurrentMutatorSequence.clear(); + CurrentDictionaryEntrySequence.clear(); +} + +// Copy successful dictionary entries to PersistentAutoDictionary. +void MutationDispatcher::RecordSuccessfulMutationSequence() { + for (auto DE : CurrentDictionaryEntrySequence) { + // PersistentAutoDictionary.AddWithSuccessCountOne(DE); + DE->IncSuccessCount(); + // Linear search is fine here as this happens seldom. + if (!PersistentAutoDictionary.ContainsWord(DE->GetW())) + PersistentAutoDictionary.push_back({DE->GetW(), 1}); + } +} + +void MutationDispatcher::PrintRecommendedDictionary() { + std::vector V; + for (auto &DE : PersistentAutoDictionary) + if (!ManualDictionary.ContainsWord(DE.GetW())) + V.push_back(DE); + if (V.empty()) return; + Printf("###### Recommended dictionary. ######\n"); + for (auto &DE: V) { + Printf("\""); + PrintASCII(DE.GetW(), "\""); + Printf(" # Uses: %zd\n", DE.GetUseCount()); + } + Printf("###### End of recommended dictionary. ######\n"); +} + +void MutationDispatcher::PrintMutationSequence() { + Printf("MS: %zd ", CurrentMutatorSequence.size()); + for (auto M : CurrentMutatorSequence) + Printf("%s-", M.Name); + if (!CurrentDictionaryEntrySequence.empty()) { + Printf(" DE: "); + for (auto DE : CurrentDictionaryEntrySequence) { + Printf("\""); + PrintASCII(DE->GetW(), "\"-"); + } + } +} + +size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) { + return MutateImpl(Data, Size, MaxSize, Mutators); +} + +size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size, + size_t MaxSize) { + return MutateImpl(Data, Size, MaxSize, DefaultMutators); +} + +// Mutates Data in place, returns new size. +size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size, + size_t MaxSize, + const std::vector &Mutators) { + assert(MaxSize > 0); + if (Size == 0) { + for (size_t i = 0; i < MaxSize; i++) + Data[i] = RandCh(Rand); + if (Options.OnlyASCII) + ToASCII(Data, MaxSize); + return MaxSize; + } + assert(Size > 0); + // Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize), + // in which case they will return 0. + // Try several times before returning un-mutated data. + for (int Iter = 0; Iter < 100; Iter++) { + auto M = Mutators[Rand(Mutators.size())]; + size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize); + if (NewSize && NewSize <= MaxSize) { + if (Options.OnlyASCII) + ToASCII(Data, NewSize); + CurrentMutatorSequence.push_back(M); + return NewSize; + } + } + return std::min(Size, MaxSize); +} + +void MutationDispatcher::AddWordToManualDictionary(const Word &W) { + ManualDictionary.push_back( + {W, std::numeric_limits::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/test/fuzz_test/Fuzzer/FuzzerMutate.h b/test/fuzz_test/Fuzzer/FuzzerMutate.h new file mode 100644 index 00000000..d3c0b001 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerMutate.h @@ -0,0 +1,145 @@ +//===- FuzzerMutate.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::MutationDispatcher +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_MUTATE_H +#define LLVM_FUZZER_MUTATE_H + +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" +#include "FuzzerRandom.h" + +namespace fuzzer { + +class MutationDispatcher { +public: + MutationDispatcher(Random &Rand, const FuzzingOptions &Options); + ~MutationDispatcher() {} + /// Indicate that we are about to start a new sequence of mutations. + void StartMutationSequence(); + /// Print the current sequence of mutations. + void PrintMutationSequence(); + /// Indicate that the current sequence of mutations was successfull. + void RecordSuccessfulMutationSequence(); + /// Mutates data by invoking user-provided mutator. + size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by invoking user-provided crossover. + size_t Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by shuffling bytes. + size_t Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by erasing bytes. + size_t Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by inserting a byte. + size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by inserting several repeated bytes. + size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by chanding one byte. + size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by chanding one bit. + size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by copying/inserting a part of data into a different place. + size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Mutates data by adding a word from the manual dictionary. + size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size, + size_t MaxSize); + + /// Mutates data by adding a word from the temporary automatic dictionary. + size_t Mutate_AddWordFromTemporaryAutoDictionary(uint8_t *Data, size_t Size, + size_t MaxSize); + + /// Mutates data by adding a word from the TORC. + size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Mutates data by adding a word from the persistent automatic dictionary. + size_t Mutate_AddWordFromPersistentAutoDictionary(uint8_t *Data, size_t Size, + size_t MaxSize); + + /// Tries to find an ASCII integer in Data, changes it to another ASCII int. + size_t Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, size_t MaxSize); + /// Change a 1-, 2-, 4-, or 8-byte integer in interesting ways. + size_t Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize); + + /// CrossOver Data with some other element of the corpus. + size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Applies one of the configured mutations. + /// Returns the new size of data which could be up to MaxSize. + size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize); + /// Applies one of the default mutations. Provided as a service + /// to mutation authors. + size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Creates a cross-over of two pieces of Data, returns its size. + size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2, + size_t Size2, uint8_t *Out, size_t MaxOutSize); + + void AddWordToManualDictionary(const Word &W); + + void AddWordToAutoDictionary(DictionaryEntry DE); + void ClearAutoDictionary(); + void PrintRecommendedDictionary(); + + void SetCorpus(const InputCorpus *Corpus) { this->Corpus = Corpus; } + + Random &GetRand() { return Rand; } + +private: + + struct Mutator { + size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max); + const char *Name; + }; + + size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size, + size_t MaxSize); + size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize, + const std::vector &Mutators); + + size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To, + size_t ToSize, size_t MaxToSize); + size_t CopyPartOf(const uint8_t *From, size_t FromSize, uint8_t *To, + size_t ToSize); + size_t ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize, + DictionaryEntry &DE); + + template + DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2, + const uint8_t *Data, size_t Size); + + Random &Rand; + const FuzzingOptions &Options; + + // Dictionary provided by the user via -dict=DICT_FILE. + Dictionary ManualDictionary; + // Temporary dictionary modified by the fuzzer itself, + // recreated periodically. + Dictionary TempAutoDictionary; + // Persistent dictionary modified by the fuzzer, consists of + // entries that led to successfull discoveries in the past mutations. + Dictionary PersistentAutoDictionary; + + std::vector CurrentMutatorSequence; + std::vector CurrentDictionaryEntrySequence; + + static const size_t kCmpDictionaryEntriesDequeSize = 16; + DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize]; + size_t CmpDictionaryEntriesDequeIdx = 0; + + const InputCorpus *Corpus = nullptr; + std::vector MutateInPlaceHere; + + std::vector Mutators; + std::vector DefaultMutators; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_MUTATE_H diff --git a/test/fuzz_test/Fuzzer/FuzzerOptions.h b/test/fuzz_test/Fuzzer/FuzzerOptions.h new file mode 100644 index 00000000..34e93fc3 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerOptions.h @@ -0,0 +1,68 @@ +//===- FuzzerOptions.h - Internal header for the Fuzzer ---------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::FuzzingOptions +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_OPTIONS_H +#define LLVM_FUZZER_OPTIONS_H + +#include "FuzzerDefs.h" + +namespace fuzzer { + +struct FuzzingOptions { + int Verbosity = 1; + size_t MaxLen = 0; + int UnitTimeoutSec = 300; + int TimeoutExitCode = 77; + int ErrorExitCode = 77; + int MaxTotalTimeSec = 0; + int RssLimitMb = 0; + bool DoCrossOver = true; + int MutateDepth = 5; + bool UseCounters = false; + bool UseIndirCalls = true; + bool UseMemcmp = true; + bool UseMemmem = true; + bool UseCmp = false; + bool UseValueProfile = false; + bool Shrink = false; + int ReloadIntervalSec = 1; + bool ShuffleAtStartUp = true; + bool PreferSmall = true; + size_t MaxNumberOfRuns = -1L; + int ReportSlowUnits = 10; + bool OnlyASCII = false; + std::string OutputCorpus; + std::string ArtifactPrefix = "./"; + std::string ExactArtifactPath; + std::string ExitOnSrcPos; + std::string ExitOnItem; + bool SaveArtifacts = true; + bool PrintNEW = true; // Print a status line when new units are found; + bool OutputCSV = false; + bool PrintNewCovPcs = false; + bool PrintFinalStats = false; + bool PrintCorpusStats = false; + bool PrintCoverage = false; + bool DumpCoverage = false; + bool DetectLeaks = true; + int TraceMalloc = 0; + bool HandleAbrt = false; + bool HandleBus = false; + bool HandleFpe = false; + bool HandleIll = false; + bool HandleInt = false; + bool HandleSegv = false; + bool HandleTerm = false; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_OPTIONS_H diff --git a/test/fuzz_test/Fuzzer/FuzzerRandom.h b/test/fuzz_test/Fuzzer/FuzzerRandom.h new file mode 100644 index 00000000..b1be0bb9 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerRandom.h @@ -0,0 +1,36 @@ +//===- FuzzerRandom.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::Random +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_RANDOM_H +#define LLVM_FUZZER_RANDOM_H + +#include + +namespace fuzzer { +class Random { + public: + Random(unsigned int seed) : R(seed) {} + size_t Rand() { return R(); } + size_t RandBool() { return Rand() % 2; } + size_t operator()(size_t n) { return n ? Rand() % n : 0; } + intptr_t operator()(intptr_t From, intptr_t To) { + assert(From < To); + intptr_t RangeSize = To - From + 1; + return operator()(RangeSize) + From; + } + std::mt19937 &Get_mt19937() { return R; } + private: + std::mt19937 R; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_RANDOM_H diff --git a/test/fuzz_test/Fuzzer/FuzzerSHA1.cpp b/test/fuzz_test/Fuzzer/FuzzerSHA1.cpp new file mode 100644 index 00000000..d2f8e811 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerSHA1.cpp @@ -0,0 +1,222 @@ +//===- FuzzerSHA1.h - Private copy of the SHA1 implementation ---*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This code is taken from public domain +// (http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c) +// and modified by adding anonymous namespace, adding an interface +// function fuzzer::ComputeSHA1() and removing unnecessary code. +// +// lib/Fuzzer can not use SHA1 implementation from openssl because +// openssl may not be available and because we may be fuzzing openssl itself. +// For the same reason we do not want to depend on SHA1 from LLVM tree. +//===----------------------------------------------------------------------===// + +#include "FuzzerSHA1.h" +#include "FuzzerDefs.h" + +/* This code is public-domain - it is based on libcrypt + * placed in the public domain by Wei Dai and other contributors. + */ + +#include +#include +#include +#include + +namespace { // Added for LibFuzzer + +#ifdef __BIG_ENDIAN__ +# define SHA_BIG_ENDIAN +#elif defined __LITTLE_ENDIAN__ +/* override */ +#elif defined __BYTE_ORDER +# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define SHA_BIG_ENDIAN +# endif +#else // ! defined __LITTLE_ENDIAN__ +# include // machine/endian.h +# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define SHA_BIG_ENDIAN +# endif +#endif + + +/* header */ + +#define HASH_LENGTH 20 +#define BLOCK_LENGTH 64 + +typedef struct sha1nfo { + uint32_t buffer[BLOCK_LENGTH/4]; + uint32_t state[HASH_LENGTH/4]; + uint32_t byteCount; + uint8_t bufferOffset; + uint8_t keyBuffer[BLOCK_LENGTH]; + uint8_t innerHash[HASH_LENGTH]; +} sha1nfo; + +/* public API - prototypes - TODO: doxygen*/ + +/** + */ +void sha1_init(sha1nfo *s); +/** + */ +void sha1_writebyte(sha1nfo *s, uint8_t data); +/** + */ +void sha1_write(sha1nfo *s, const char *data, size_t len); +/** + */ +uint8_t* sha1_result(sha1nfo *s); + + +/* code */ +#define SHA1_K0 0x5a827999 +#define SHA1_K20 0x6ed9eba1 +#define SHA1_K40 0x8f1bbcdc +#define SHA1_K60 0xca62c1d6 + +void sha1_init(sha1nfo *s) { + s->state[0] = 0x67452301; + s->state[1] = 0xefcdab89; + s->state[2] = 0x98badcfe; + s->state[3] = 0x10325476; + s->state[4] = 0xc3d2e1f0; + s->byteCount = 0; + s->bufferOffset = 0; +} + +uint32_t sha1_rol32(uint32_t number, uint8_t bits) { + return ((number << bits) | (number >> (32-bits))); +} + +void sha1_hashBlock(sha1nfo *s) { + uint8_t i; + uint32_t a,b,c,d,e,t; + + a=s->state[0]; + b=s->state[1]; + c=s->state[2]; + d=s->state[3]; + e=s->state[4]; + for (i=0; i<80; i++) { + if (i>=16) { + t = s->buffer[(i+13)&15] ^ s->buffer[(i+8)&15] ^ s->buffer[(i+2)&15] ^ s->buffer[i&15]; + s->buffer[i&15] = sha1_rol32(t,1); + } + if (i<20) { + t = (d ^ (b & (c ^ d))) + SHA1_K0; + } else if (i<40) { + t = (b ^ c ^ d) + SHA1_K20; + } else if (i<60) { + t = ((b & c) | (d & (b | c))) + SHA1_K40; + } else { + t = (b ^ c ^ d) + SHA1_K60; + } + t+=sha1_rol32(a,5) + e + s->buffer[i&15]; + e=d; + d=c; + c=sha1_rol32(b,30); + b=a; + a=t; + } + s->state[0] += a; + s->state[1] += b; + s->state[2] += c; + s->state[3] += d; + s->state[4] += e; +} + +void sha1_addUncounted(sha1nfo *s, uint8_t data) { + uint8_t * const b = (uint8_t*) s->buffer; +#ifdef SHA_BIG_ENDIAN + b[s->bufferOffset] = data; +#else + b[s->bufferOffset ^ 3] = data; +#endif + s->bufferOffset++; + if (s->bufferOffset == BLOCK_LENGTH) { + sha1_hashBlock(s); + s->bufferOffset = 0; + } +} + +void sha1_writebyte(sha1nfo *s, uint8_t data) { + ++s->byteCount; + sha1_addUncounted(s, data); +} + +void sha1_write(sha1nfo *s, const char *data, size_t len) { + for (;len--;) sha1_writebyte(s, (uint8_t) *data++); +} + +void sha1_pad(sha1nfo *s) { + // Implement SHA-1 padding (fips180-2 §5.1.1) + + // Pad with 0x80 followed by 0x00 until the end of the block + sha1_addUncounted(s, 0x80); + while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00); + + // Append length in the last 8 bytes + sha1_addUncounted(s, 0); // We're only using 32 bit lengths + sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths + sha1_addUncounted(s, 0); // So zero pad the top bits + sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8 + sha1_addUncounted(s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as + sha1_addUncounted(s, s->byteCount >> 13); // byte. + sha1_addUncounted(s, s->byteCount >> 5); + sha1_addUncounted(s, s->byteCount << 3); +} + +uint8_t* sha1_result(sha1nfo *s) { + // Pad to complete the last block + sha1_pad(s); + +#ifndef SHA_BIG_ENDIAN + // Swap byte order back + int i; + for (i=0; i<5; i++) { + s->state[i]= + (((s->state[i])<<24)& 0xff000000) + | (((s->state[i])<<8) & 0x00ff0000) + | (((s->state[i])>>8) & 0x0000ff00) + | (((s->state[i])>>24)& 0x000000ff); + } +#endif + + // Return pointer to hash (20 characters) + return (uint8_t*) s->state; +} + +} // namespace; Added for LibFuzzer + +namespace fuzzer { + +// The rest is added for LibFuzzer +void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out) { + sha1nfo s; + sha1_init(&s); + sha1_write(&s, (const char*)Data, Len); + memcpy(Out, sha1_result(&s), HASH_LENGTH); +} + +std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]) { + std::stringstream SS; + for (int i = 0; i < kSHA1NumBytes; i++) + SS << std::hex << std::setfill('0') << std::setw(2) << (unsigned)Sha1[i]; + return SS.str(); +} + +std::string Hash(const Unit &U) { + uint8_t Hash[kSHA1NumBytes]; + ComputeSHA1(U.data(), U.size(), Hash); + return Sha1ToString(Hash); +} + +} diff --git a/test/fuzz_test/Fuzzer/FuzzerSHA1.h b/test/fuzz_test/Fuzzer/FuzzerSHA1.h new file mode 100644 index 00000000..3b5e6e80 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerSHA1.h @@ -0,0 +1,33 @@ +//===- FuzzerSHA1.h - Internal header for the SHA1 utils --------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// SHA1 utils. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_SHA1_H +#define LLVM_FUZZER_SHA1_H + +#include "FuzzerDefs.h" +#include +#include + +namespace fuzzer { + +// Private copy of SHA1 implementation. +static const int kSHA1NumBytes = 20; + +// Computes SHA1 hash of 'Len' bytes in 'Data', writes kSHA1NumBytes to 'Out'. +void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out); + +std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]); + +std::string Hash(const Unit &U); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_SHA1_H diff --git a/test/fuzz_test/Fuzzer/FuzzerTracePC.cpp b/test/fuzz_test/Fuzzer/FuzzerTracePC.cpp new file mode 100644 index 00000000..81c084b5 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerTracePC.cpp @@ -0,0 +1,339 @@ +//===- FuzzerTracePC.cpp - PC tracing--------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Trace PCs. +// This module implements __sanitizer_cov_trace_pc_guard[_init], +// the callback required for -fsanitize-coverage=trace-pc-guard instrumentation. +// +//===----------------------------------------------------------------------===// + +#include "FuzzerCorpus.h" +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerTracePC.h" +#include "FuzzerValueBitMap.h" +#include +#include +#include +#include + +namespace fuzzer { + +TracePC TPC; + +void TracePC::HandleTrace(uint32_t *Guard, uintptr_t PC) { + uint32_t Idx = *Guard; + if (!Idx) return; + PCs[Idx % kNumPCs] = PC; + Counters[Idx % kNumCounters]++; +} + +size_t TracePC::GetTotalPCCoverage() { + size_t Res = 0; + for (size_t i = 1; i < GetNumPCs(); i++) + if (PCs[i]) + Res++; + return Res; +} + +void TracePC::HandleInit(uint32_t *Start, uint32_t *Stop) { + if (Start == Stop || *Start) return; + assert(NumModules < sizeof(Modules) / sizeof(Modules[0])); + for (uint32_t *P = Start; P < Stop; P++) + *P = ++NumGuards; + Modules[NumModules].Start = Start; + Modules[NumModules].Stop = Stop; + NumModules++; +} + +void TracePC::PrintModuleInfo() { + Printf("INFO: Loaded %zd modules (%zd guards): ", NumModules, NumGuards); + for (size_t i = 0; i < NumModules; i++) + Printf("[%p, %p), ", Modules[i].Start, Modules[i].Stop); + Printf("\n"); +} + +void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) { + const uintptr_t kBits = 12; + const uintptr_t kMask = (1 << kBits) - 1; + uintptr_t Idx = (Caller & kMask) | ((Callee & kMask) << kBits); + HandleValueProfile(Idx); +} + +static bool IsInterestingCoverageFile(std::string &File) { + if (File.find("compiler-rt/lib/") != std::string::npos) + return false; // sanitizer internal. + if (File.find("/usr/lib/") != std::string::npos) + return false; + if (File.find("/usr/include/") != std::string::npos) + return false; + if (File == "") + return false; + return true; +} + +void TracePC::PrintNewPCs() { + if (DoPrintNewPCs) { + if (!PrintedPCs) + PrintedPCs = new std::set; + for (size_t i = 1; i < GetNumPCs(); i++) + if (PCs[i] && PrintedPCs->insert(PCs[i]).second) + PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PCs[i]); + } +} + +void TracePC::PrintCoverage() { + if (!EF->__sanitizer_symbolize_pc || + !EF->__sanitizer_get_module_and_offset_for_pc) { + Printf("INFO: __sanitizer_symbolize_pc or " + "__sanitizer_get_module_and_offset_for_pc is not available," + " not printing coverage\n"); + return; + } + std::map> CoveredPCsPerModule; + std::map ModuleOffsets; + std::set CoveredDirs, CoveredFiles, CoveredFunctions, + CoveredLines; + Printf("COVERAGE:\n"); + for (size_t i = 1; i < GetNumPCs(); i++) { + if (!PCs[i]) continue; + std::string FileStr = DescribePC("%s", PCs[i]); + if (!IsInterestingCoverageFile(FileStr)) continue; + std::string FixedPCStr = DescribePC("%p", PCs[i]); + std::string FunctionStr = DescribePC("%F", PCs[i]); + std::string LineStr = DescribePC("%l", PCs[i]); + char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++? + void *OffsetRaw = nullptr; + if (!EF->__sanitizer_get_module_and_offset_for_pc( + reinterpret_cast(PCs[i]), ModulePathRaw, + sizeof(ModulePathRaw), &OffsetRaw)) + continue; + std::string Module = ModulePathRaw; + uintptr_t FixedPC = std::stol(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()); + } + + std::string CoveredDirsStr; + for (auto &Dir : CoveredDirs) { + if (!CoveredDirsStr.empty()) + CoveredDirsStr += ","; + CoveredDirsStr += Dir; + } + Printf("COVERED_DIRS: %s\n", CoveredDirsStr.c_str()); + + for (auto &M : CoveredPCsPerModule) { + std::set 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 = "objdump -d " + ModuleName + + " | grep 'call.*__sanitizer_cov_trace_pc_guard' | awk -F: '{print $1}'"; + std::string SanCovOutput; + if (!ExecuteCommandAndReadOutput(Cmd, &SanCovOutput)) { + Printf("INFO: Command failed: %s\n", Cmd.c_str()); + continue; + } + std::istringstream ISS(SanCovOutput); + std::string S; + while (std::getline(ISS, S, '\n')) { + uintptr_t PcOffset = std::stol(S, 0, 16); + if (!std::binary_search(CoveredOffsets.begin(), CoveredOffsets.end(), + PcOffset)) { + uintptr_t PC = ModuleOffset + PcOffset; + auto FileStr = DescribePC("%s", PC); + if (!IsInterestingCoverageFile(FileStr)) continue; + if (CoveredFiles.count(FileStr) == 0) { + UncoveredFiles.insert(FileStr); + continue; + } + auto FunctionStr = DescribePC("%F", PC); + if (CoveredFunctions.count(FunctionStr) == 0) { + UncoveredFunctions.insert(FunctionStr); + continue; + } + std::string LineStr = DescribePC("%l", PC); + uintptr_t Line = std::stoi(LineStr); + std::string FileLineStr = FileStr + ":" + LineStr; + if (CoveredLines.count(FileLineStr) == 0) + UncoveredLines[FunctionStr + " " + FileStr].insert(Line); + } + } + for (auto &FileLine: UncoveredLines) + for (int Line : FileLine.second) + Printf("UNCOVERED_LINE: %s:%d\n", FileLine.first.c_str(), Line); + for (auto &Func : UncoveredFunctions) + Printf("UNCOVERED_FUNC: %s\n", Func.c_str()); + for (auto &File : UncoveredFiles) + Printf("UNCOVERED_FILE: %s\n", File.c_str()); + } +} + +void TracePC::DumpCoverage() { + __sanitizer_dump_coverage(PCs, GetNumPCs()); +} + +// Value profile. +// We keep track of various values that affect control flow. +// These values are inserted into a bit-set-based hash map. +// Every new bit in the map is treated as a new coverage. +// +// For memcmp/strcmp/etc the interesting value is the length of the common +// prefix of the parameters. +// For cmp instructions the interesting value is a XOR of the parameters. +// The interesting value is mixed up with the PC and is then added to the map. + +ATTRIBUTE_NO_SANITIZE_MEMORY +void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, + size_t n) { + if (!n) return; + size_t Len = std::min(n, (size_t)32); + const uint8_t *A1 = reinterpret_cast(s1); + const uint8_t *A2 = reinterpret_cast(s2); + size_t I = 0; + for (; I < Len; I++) + if (A1[I] != A2[I]) + break; + size_t PC = reinterpret_cast(caller_pc); + size_t Idx = I; + // if (I < Len) + // Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1; + TPC.HandleValueProfile((PC & 4095) | (Idx << 12)); +} + +ATTRIBUTE_NO_SANITIZE_MEMORY +void TracePC::AddValueForStrcmp(void *caller_pc, const char *s1, const char *s2, + size_t n) { + if (!n) return; + size_t Len = std::min(n, (size_t)32); + const uint8_t *A1 = reinterpret_cast(s1); + const uint8_t *A2 = reinterpret_cast(s2); + size_t I = 0; + for (; I < Len; I++) + if (A1[I] != A2[I] || A1[I] == 0) + break; + size_t PC = reinterpret_cast(caller_pc); + size_t Idx = I; + // if (I < Len && A1[I]) + // Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1; + TPC.HandleValueProfile((PC & 4095) | (Idx << 12)); +} + +template +ATTRIBUTE_TARGET_POPCNT +#ifdef __clang__ // g++ can't handle this __attribute__ here :( +__attribute__((always_inline)) +#endif // __clang__ +void TracePC::HandleCmp(void *PC, T Arg1, T Arg2) { + uintptr_t PCuint = reinterpret_cast(PC); + uint64_t ArgXor = Arg1 ^ Arg2; + uint64_t ArgDistance = __builtin_popcountl(ArgXor) + 1; // [1,65] + uintptr_t Idx = ((PCuint & 4095) + 1) * ArgDistance; + if (sizeof(T) == 4) + TORC4.Insert(ArgXor, Arg1, Arg2); + else if (sizeof(T) == 8) + TORC8.Insert(ArgXor, Arg1, Arg2); + HandleValueProfile(Idx); +} + +} // namespace fuzzer + +extern "C" { +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) { + uintptr_t PC = (uintptr_t)__builtin_return_address(0); + fuzzer::TPC.HandleTrace(Guard, PC); +} + +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) { + fuzzer::TPC.HandleInit(Start, Stop); +} + +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) { + uintptr_t PC = (uintptr_t)__builtin_return_address(0); + fuzzer::TPC.HandleCallerCallee(PC, Callee); +} + +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) { + fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) { + fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) { + fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) { + fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); +} + +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { + // Updates the value profile based on the relative position of Val and Cases. + // We want to handle one random case at every call (handling all is slow). + // Since none of the arguments contain any random bits we use a thread-local + // counter to choose the random case to handle. + static thread_local size_t Counter; + Counter++; + uint64_t N = Cases[0]; + uint64_t *Vals = Cases + 2; + char *PC = (char*)__builtin_return_address(0); + // We need a random number < N using Counter as a seed. But w/o DIV. + // * find a power of two >= N + // * mask Counter with this power of two. + // * maybe subtract N. + size_t Nlog = sizeof(long) * 8 - __builtin_clzl((long)N); + size_t PowerOfTwoGeN = 1U << Nlog; + assert(PowerOfTwoGeN >= N); + size_t Idx = Counter & (PowerOfTwoGeN - 1); + if (Idx >= N) + Idx -= N; + assert(Idx < N); + uint64_t TwoIn32 = 1ULL << 32; + if ((Val | Vals[Idx]) < TwoIn32) + fuzzer::TPC.HandleCmp(PC + Idx, static_cast(Val), + static_cast(Vals[Idx])); + else + fuzzer::TPC.HandleCmp(PC + Idx, Val, Vals[Idx]); +} + +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_div4(uint32_t Val) { + fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint32_t)0); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_div8(uint64_t Val) { + fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint64_t)0); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_gep(uintptr_t Idx) { + fuzzer::TPC.HandleCmp(__builtin_return_address(0), Idx, (uintptr_t)0); +} + +} // extern "C" diff --git a/test/fuzz_test/Fuzzer/FuzzerTracePC.h b/test/fuzz_test/Fuzzer/FuzzerTracePC.h new file mode 100644 index 00000000..68827f80 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerTracePC.h @@ -0,0 +1,158 @@ +//===- FuzzerTracePC.h - Internal header for the Fuzzer ---------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::TracePC +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_TRACE_PC +#define LLVM_FUZZER_TRACE_PC + +#include "FuzzerDefs.h" +#include "FuzzerValueBitMap.h" +#include + +namespace fuzzer { + +// TableOfRecentCompares (TORC) remembers the most recently performed +// comparisons of type T. +// We record the arguments of CMP instructions in this table unconditionally +// because it seems cheaper this way than to compute some expensive +// conditions inside __sanitizer_cov_trace_cmp*. +// After the unit has been executed we may decide to use the contents of +// this table to populate a Dictionary. +template +struct TableOfRecentCompares { + static const size_t kSize = kSizeT; + struct Pair { + T A, B; + }; + void Insert(size_t Idx, T Arg1, T Arg2) { + Idx = Idx % kSize; + Table[Idx].A = Arg1; + Table[Idx].B = Arg2; + } + + Pair Get(size_t I) { return Table[I % kSize]; } + + Pair Table[kSize]; +}; + +class TracePC { + public: + static const size_t kFeatureSetSize = ValueBitMap::kNumberOfItems; + + void HandleTrace(uint32_t *guard, uintptr_t PC); + void HandleInit(uint32_t *start, uint32_t *stop); + void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee); + void HandleValueProfile(size_t Value) { ValueProfileMap.AddValue(Value); } + template void HandleCmp(void *PC, T Arg1, T Arg2); + size_t GetTotalPCCoverage(); + void SetUseCounters(bool UC) { UseCounters = UC; } + void SetUseValueProfile(bool VP) { UseValueProfile = VP; } + void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; } + template size_t CollectFeatures(Callback CB); + bool UpdateValueProfileMap(ValueBitMap *MaxValueProfileMap) { + return UseValueProfile && MaxValueProfileMap->MergeFrom(ValueProfileMap); + } + + void ResetMaps() { + ValueProfileMap.Reset(); + memset(Counters, 0, sizeof(Counters)); + } + + void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize); + void PrintFeatureSet(); + + void PrintModuleInfo(); + + void PrintCoverage(); + void DumpCoverage(); + + void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, + size_t n); + void AddValueForStrcmp(void *caller_pc, const char *s1, const char *s2, + size_t n); + + bool UsingTracePcGuard() const {return NumModules; } + + static const size_t kTORCSize = 1 << 5; + TableOfRecentCompares TORC4; + TableOfRecentCompares TORC8; + + void PrintNewPCs(); + size_t GetNumPCs() const { return Min(kNumPCs, NumGuards + 1); } + uintptr_t GetPC(size_t Idx) { + assert(Idx < GetNumPCs()); + return PCs[Idx]; + } + +private: + bool UseCounters = false; + bool UseValueProfile = false; + bool DoPrintNewPCs = false; + + struct Module { + uint32_t *Start, *Stop; + }; + + Module Modules[4096]; + size_t NumModules; // linker-initialized. + size_t NumGuards; // linker-initialized. + + static const size_t kNumCounters = 1 << 14; + alignas(8) uint8_t Counters[kNumCounters]; + + static const size_t kNumPCs = 1 << 24; + uintptr_t PCs[kNumPCs]; + + std::set *PrintedPCs; + + ValueBitMap ValueProfileMap; +}; + +template +size_t TracePC::CollectFeatures(Callback CB) { + if (!UsingTracePcGuard()) return 0; + size_t Res = 0; + const size_t Step = 8; + assert(reinterpret_cast(Counters) % Step == 0); + size_t N = Min(kNumCounters, NumGuards + 1); + N = (N + Step - 1) & ~(Step - 1); // Round up. + for (size_t Idx = 0; Idx < N; Idx += Step) { + uint64_t Bundle = *reinterpret_cast(&Counters[Idx]); + if (!Bundle) continue; + for (size_t i = Idx; i < Idx + Step; i++) { + uint8_t Counter = (Bundle >> ((i - Idx) * 8)) & 0xff; + if (!Counter) continue; + Counters[i] = 0; + unsigned Bit = 0; + /**/ if (Counter >= 128) Bit = 7; + else if (Counter >= 32) Bit = 6; + else if (Counter >= 16) Bit = 5; + else if (Counter >= 8) Bit = 4; + else if (Counter >= 4) Bit = 3; + else if (Counter >= 3) Bit = 2; + else if (Counter >= 2) Bit = 1; + size_t Feature = (i * 8 + Bit); + if (CB(Feature)) + Res++; + } + } + if (UseValueProfile) + ValueProfileMap.ForEach([&](size_t Idx) { + if (CB(NumGuards * 8 + Idx)) + Res++; + }); + return Res; +} + +extern TracePC TPC; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_TRACE_PC diff --git a/test/fuzz_test/Fuzzer/FuzzerTraceState.cpp b/test/fuzz_test/Fuzzer/FuzzerTraceState.cpp new file mode 100644 index 00000000..be62a662 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerTraceState.cpp @@ -0,0 +1,325 @@ +//===- FuzzerTraceState.cpp - Trace-based fuzzer mutator ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Data tracing. +//===----------------------------------------------------------------------===// + +#include "FuzzerDictionary.h" +#include "FuzzerInternal.h" +#include "FuzzerIO.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include +#include + +namespace fuzzer { + +// For now, very simple: put Size bytes of Data at position Pos. +struct TraceBasedMutation { + uint32_t Pos; + Word W; +}; + +// Declared as static globals for faster checks inside the hooks. +static bool RecordingMemcmp = false; +static bool RecordingMemmem = false; +static bool DoingMyOwnMemmem = false; + +ScopedDoingMyOwnMemmem::ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = true; } +ScopedDoingMyOwnMemmem::~ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = false; } + +class TraceState { +public: + TraceState(MutationDispatcher &MD, const FuzzingOptions &Options, + const Fuzzer *F) + : MD(MD), Options(Options), F(F) {} + + void TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1, + const uint8_t *Data2); + + void TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits, uint64_t Val, + size_t NumCases, uint64_t *Cases); + int TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData, + size_t DataSize); + int TryToAddDesiredData(const uint8_t *PresentData, + const uint8_t *DesiredData, size_t DataSize); + + void StartTraceRecording() { + if (!Options.UseMemcmp) + return; + RecordingMemcmp = Options.UseMemcmp; + RecordingMemmem = Options.UseMemmem; + NumMutations = 0; + InterestingWords.clear(); + MD.ClearAutoDictionary(); + } + + void StopTraceRecording() { + if (!RecordingMemcmp) + return; + RecordingMemcmp = false; + for (size_t i = 0; i < NumMutations; i++) { + auto &M = Mutations[i]; + if (Options.Verbosity >= 2) { + AutoDictUnitCounts[M.W]++; + AutoDictAdds++; + if ((AutoDictAdds & (AutoDictAdds - 1)) == 0) { + typedef std::pair CU; + std::vector CountedUnits; + for (auto &I : AutoDictUnitCounts) + CountedUnits.push_back(std::make_pair(I.second, I.first)); + std::sort(CountedUnits.begin(), CountedUnits.end(), + [](const CU &a, const CU &b) { return a.first > b.first; }); + Printf("AutoDict:\n"); + for (auto &I : CountedUnits) { + Printf(" %zd ", I.first); + PrintASCII(I.second.data(), I.second.size()); + Printf("\n"); + } + } + } + MD.AddWordToAutoDictionary({M.W, M.Pos}); + } + for (auto &W : InterestingWords) + MD.AddWordToAutoDictionary({W}); + } + + void AddMutation(uint32_t Pos, uint32_t Size, const uint8_t *Data) { + if (NumMutations >= kMaxMutations) return; + auto &M = Mutations[NumMutations++]; + M.Pos = Pos; + M.W.Set(Data, Size); + } + + void AddMutation(uint32_t Pos, uint32_t Size, uint64_t Data) { + assert(Size <= sizeof(Data)); + AddMutation(Pos, Size, reinterpret_cast(&Data)); + } + + void AddInterestingWord(const uint8_t *Data, size_t Size) { + if (!RecordingMemmem || !F->InFuzzingThread()) return; + if (Size <= 1) return; + Size = std::min(Size, Word::GetMaxSize()); + Word W(Data, Size); + InterestingWords.insert(W); + } + + private: + bool IsTwoByteData(uint64_t Data) { + int64_t Signed = static_cast(Data); + Signed >>= 16; + return Signed == 0 || Signed == -1L; + } + + // We don't want to create too many trace-based mutations as it is both + // expensive and useless. So after some number of mutations is collected, + // start rejecting some of them. The more there are mutations the more we + // reject. + bool WantToHandleOneMoreMutation() { + const size_t FirstN = 64; + // Gladly handle first N mutations. + if (NumMutations <= FirstN) return true; + size_t Diff = NumMutations - FirstN; + size_t DiffLog = sizeof(long) * 8 - __builtin_clzl((long)Diff); + assert(DiffLog > 0 && DiffLog < 64); + bool WantThisOne = MD.GetRand()(1 << DiffLog) == 0; // 1 out of DiffLog. + return WantThisOne; + } + + static const size_t kMaxMutations = 1 << 16; + size_t NumMutations; + TraceBasedMutation Mutations[kMaxMutations]; + // TODO: std::set is too inefficient, need to have a custom DS here. + std::set InterestingWords; + MutationDispatcher &MD; + const FuzzingOptions Options; + const Fuzzer *F; + std::map AutoDictUnitCounts; + size_t AutoDictAdds = 0; +}; + +int TraceState::TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData, + size_t DataSize) { + if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0; + ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem; + const uint8_t *UnitData; + auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData); + int Res = 0; + const uint8_t *Beg = UnitData; + const uint8_t *End = Beg + UnitSize; + for (const uint8_t *Cur = Beg; Cur < End; Cur++) { + Cur = (uint8_t *)SearchMemory(Cur, End - Cur, &PresentData, DataSize); + if (!Cur) + break; + size_t Pos = Cur - Beg; + assert(Pos < UnitSize); + AddMutation(Pos, DataSize, DesiredData); + AddMutation(Pos, DataSize, DesiredData + 1); + AddMutation(Pos, DataSize, DesiredData - 1); + Res++; + } + return Res; +} + +int TraceState::TryToAddDesiredData(const uint8_t *PresentData, + const uint8_t *DesiredData, + size_t DataSize) { + if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0; + ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem; + const uint8_t *UnitData; + auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData); + int Res = 0; + const uint8_t *Beg = UnitData; + const uint8_t *End = Beg + UnitSize; + for (const uint8_t *Cur = Beg; Cur < End; Cur++) { + Cur = (uint8_t *)SearchMemory(Cur, End - Cur, PresentData, DataSize); + if (!Cur) + break; + size_t Pos = Cur - Beg; + assert(Pos < UnitSize); + AddMutation(Pos, DataSize, DesiredData); + Res++; + } + return Res; +} + +void TraceState::TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1, + const uint8_t *Data2) { + if (!RecordingMemcmp || !F->InFuzzingThread()) return; + CmpSize = std::min(CmpSize, Word::GetMaxSize()); + int Added2 = TryToAddDesiredData(Data1, Data2, CmpSize); + int Added1 = TryToAddDesiredData(Data2, Data1, CmpSize); + if ((Added1 || Added2) && Options.Verbosity >= 3) { + Printf("MemCmp Added %d%d: ", Added1, Added2); + if (Added1) PrintASCII(Data1, CmpSize); + if (Added2) PrintASCII(Data2, CmpSize); + Printf("\n"); + } +} + +void TraceState::TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits, + uint64_t Val, size_t NumCases, + uint64_t *Cases) { + if (F->InFuzzingThread()) return; + size_t ValSize = ValSizeInBits / 8; + bool TryShort = IsTwoByteData(Val); + for (size_t i = 0; i < NumCases; i++) + TryShort &= IsTwoByteData(Cases[i]); + + if (Options.Verbosity >= 3) + Printf("TraceSwitch: %p %zd # %zd; TryShort %d\n", PC, Val, NumCases, + TryShort); + + for (size_t i = 0; i < NumCases; i++) { + TryToAddDesiredData(Val, Cases[i], ValSize); + if (TryShort) + TryToAddDesiredData(Val, Cases[i], 2); + } +} + +static TraceState *TS; + +void Fuzzer::StartTraceRecording() { + if (!TS) return; + TS->StartTraceRecording(); +} + +void Fuzzer::StopTraceRecording() { + if (!TS) return; + TS->StopTraceRecording(); +} + +void Fuzzer::InitializeTraceState() { + if (!Options.UseMemcmp) return; + TS = new TraceState(MD, Options, this); +} + +static size_t InternalStrnlen(const char *S, size_t MaxLen) { + size_t Len = 0; + for (; Len < MaxLen && S[Len]; Len++) {} + return Len; +} + +} // namespace fuzzer + +using fuzzer::TS; +using fuzzer::RecordingMemcmp; + +extern "C" { + +// We may need to avoid defining weak hooks to stay compatible with older clang. +#ifndef LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS +# define LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS 1 +#endif + +#if LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS +void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1, + const void *s2, size_t n, int result) { + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n); + if (!RecordingMemcmp) return; + if (result == 0) return; // No reason to mutate. + if (n <= 1) return; // Not interesting. + TS->TraceMemcmpCallback(n, reinterpret_cast(s1), + reinterpret_cast(s2)); +} + +void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1, + const char *s2, size_t n, int result) { + fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, n); + if (!RecordingMemcmp) return; + if (result == 0) return; // No reason to mutate. + size_t Len1 = fuzzer::InternalStrnlen(s1, n); + size_t Len2 = fuzzer::InternalStrnlen(s2, n); + n = std::min(n, Len1); + n = std::min(n, Len2); + if (n <= 1) return; // Not interesting. + TS->TraceMemcmpCallback(n, reinterpret_cast(s1), + reinterpret_cast(s2)); +} + +void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, + const char *s2, int result) { + fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, 64); + if (!RecordingMemcmp) return; + if (result == 0) return; // No reason to mutate. + size_t Len1 = strlen(s1); + size_t Len2 = strlen(s2); + size_t N = std::min(Len1, Len2); + if (N <= 1) return; // Not interesting. + TS->TraceMemcmpCallback(N, reinterpret_cast(s1), + reinterpret_cast(s2)); +} + +void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1, + const char *s2, size_t n, int result) { + return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result); +} +void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1, + const char *s2, int result) { + return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result); +} +void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1, + const char *s2, char *result) { + TS->AddInterestingWord(reinterpret_cast(s2), strlen(s2)); +} +void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1, + const char *s2, char *result) { + TS->AddInterestingWord(reinterpret_cast(s2), strlen(s2)); +} +void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1, + const void *s2, size_t len2, void *result) { + if (fuzzer::DoingMyOwnMemmem) return; + TS->AddInterestingWord(reinterpret_cast(s2), len2); +} + +#endif // LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS +} // extern "C" diff --git a/test/fuzz_test/Fuzzer/FuzzerUtil.cpp b/test/fuzz_test/Fuzzer/FuzzerUtil.cpp new file mode 100644 index 00000000..f5fd3a85 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerUtil.cpp @@ -0,0 +1,218 @@ +//===- FuzzerUtil.cpp - Misc utils ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Misc utils. +//===----------------------------------------------------------------------===// + +#include "FuzzerUtil.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +void PrintHexArray(const uint8_t *Data, size_t Size, + const char *PrintAfter) { + for (size_t i = 0; i < Size; i++) + Printf("0x%x,", (unsigned)Data[i]); + Printf("%s", PrintAfter); +} + +void Print(const Unit &v, const char *PrintAfter) { + PrintHexArray(v.data(), v.size(), PrintAfter); +} + +void PrintASCIIByte(uint8_t Byte) { + if (Byte == '\\') + Printf("\\\\"); + else if (Byte == '"') + Printf("\\\""); + else if (Byte >= 32 && Byte < 127) + Printf("%c", Byte); + else + Printf("\\x%02x", Byte); +} + +void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter) { + for (size_t i = 0; i < Size; i++) + PrintASCIIByte(Data[i]); + Printf("%s", PrintAfter); +} + +void PrintASCII(const Unit &U, const char *PrintAfter) { + PrintASCII(U.data(), U.size(), PrintAfter); +} + +bool ToASCII(uint8_t *Data, size_t Size) { + bool Changed = false; + for (size_t i = 0; i < Size; i++) { + uint8_t &X = Data[i]; + auto NewX = X; + NewX &= 127; + if (!isspace(NewX) && !isprint(NewX)) + NewX = ' '; + Changed |= NewX != X; + X = NewX; + } + return Changed; +} + +bool IsASCII(const Unit &U) { return IsASCII(U.data(), U.size()); } + +bool IsASCII(const uint8_t *Data, size_t Size) { + for (size_t i = 0; i < Size; i++) + if (!(isprint(Data[i]) || isspace(Data[i]))) return false; + return true; +} + +bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) { + U->clear(); + if (Str.empty()) return false; + size_t L = 0, R = Str.size() - 1; // We are parsing the range [L,R]. + // Skip spaces from both sides. + while (L < R && isspace(Str[L])) L++; + while (R > L && isspace(Str[R])) R--; + if (R - L < 2) return false; + // Check the closing " + if (Str[R] != '"') return false; + R--; + // Find the opening " + while (L < R && Str[L] != '"') L++; + if (L >= R) return false; + assert(Str[L] == '\"'); + L++; + assert(L <= R); + for (size_t Pos = L; Pos <= R; Pos++) { + uint8_t V = (uint8_t)Str[Pos]; + if (!isprint(V) && !isspace(V)) return false; + if (V =='\\') { + // Handle '\\' + if (Pos + 1 <= R && (Str[Pos + 1] == '\\' || Str[Pos + 1] == '"')) { + U->push_back(Str[Pos + 1]); + Pos++; + continue; + } + // Handle '\xAB' + if (Pos + 3 <= R && Str[Pos + 1] == 'x' + && isxdigit(Str[Pos + 2]) && isxdigit(Str[Pos + 3])) { + char Hex[] = "0xAA"; + Hex[2] = Str[Pos + 2]; + Hex[3] = Str[Pos + 3]; + U->push_back(strtol(Hex, nullptr, 16)); + Pos += 3; + continue; + } + return false; // Invalid escape. + } else { + // Any other character. + U->push_back(V); + } + } + return true; +} + +bool ParseDictionaryFile(const std::string &Text, std::vector *Units) { + if (Text.empty()) { + Printf("ParseDictionaryFile: file does not exist or is empty\n"); + return false; + } + std::istringstream ISS(Text); + Units->clear(); + Unit U; + int LineNo = 0; + std::string S; + while (std::getline(ISS, S, '\n')) { + LineNo++; + size_t Pos = 0; + while (Pos < S.size() && isspace(S[Pos])) Pos++; // Skip spaces. + if (Pos == S.size()) continue; // Empty line. + if (S[Pos] == '#') continue; // Comment line. + if (ParseOneDictionaryEntry(S, &U)) { + Units->push_back(U); + } else { + Printf("ParseDictionaryFile: error in line %d\n\t\t%s\n", LineNo, + S.c_str()); + return false; + } + } + return true; +} + +std::string Base64(const Unit &U) { + static const char Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + std::string Res; + size_t i; + for (i = 0; i + 2 < U.size(); i += 3) { + uint32_t x = (U[i] << 16) + (U[i + 1] << 8) + U[i + 2]; + Res += Table[(x >> 18) & 63]; + Res += Table[(x >> 12) & 63]; + Res += Table[(x >> 6) & 63]; + Res += Table[x & 63]; + } + if (i + 1 == U.size()) { + uint32_t x = (U[i] << 16); + Res += Table[(x >> 18) & 63]; + Res += Table[(x >> 12) & 63]; + Res += "=="; + } else if (i + 2 == U.size()) { + uint32_t x = (U[i] << 16) + (U[i + 1] << 8); + Res += Table[(x >> 18) & 63]; + Res += Table[(x >> 12) & 63]; + Res += Table[(x >> 6) & 63]; + Res += "="; + } + return Res; +} + +std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) { + if (!EF->__sanitizer_symbolize_pc) return ""; + char PcDescr[1024]; + EF->__sanitizer_symbolize_pc(reinterpret_cast(PC), + SymbolizedFMT, PcDescr, sizeof(PcDescr)); + PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case. + return PcDescr; +} + +void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) { + if (EF->__sanitizer_symbolize_pc) + Printf("%s", DescribePC(SymbolizedFMT, PC).c_str()); + else + Printf(FallbackFMT, PC); +} + +unsigned NumberOfCpuCores() { + unsigned N = std::thread::hardware_concurrency(); + if (!N) { + Printf("WARNING: std::thread::hardware_concurrency not well defined for " + "your platform. Assuming CPU count of 1.\n"); + N = 1; + } + return N; +} + +bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out) { + FILE *Pipe = OpenProcessPipe(Command.c_str(), "r"); + if (!Pipe) return false; + char Buff[1024]; + size_t N; + while ((N = fread(Buff, 1, sizeof(Buff), Pipe)) > 0) + Out->append(Buff, N); + return true; +} + +} // namespace fuzzer diff --git a/test/fuzz_test/Fuzzer/FuzzerUtil.h b/test/fuzz_test/Fuzzer/FuzzerUtil.h new file mode 100644 index 00000000..08058c56 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerUtil.h @@ -0,0 +1,72 @@ +//===- FuzzerUtil.h - Internal header for the Fuzzer Utils ------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Util functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_UTIL_H +#define LLVM_FUZZER_UTIL_H + +#include "FuzzerDefs.h" + +namespace fuzzer { + +void PrintHexArray(const Unit &U, const char *PrintAfter = ""); + +void PrintHexArray(const uint8_t *Data, size_t Size, + const char *PrintAfter = ""); + +void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter = ""); + +void PrintASCII(const Unit &U, const char *PrintAfter = ""); + +// Changes U to contain only ASCII (isprint+isspace) characters. +// Returns true iff U has been changed. +bool ToASCII(uint8_t *Data, size_t Size); + +bool IsASCII(const Unit &U); + +bool IsASCII(const uint8_t *Data, size_t Size); + +std::string Base64(const Unit &U); + +void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC); + +std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC); + +unsigned NumberOfCpuCores(); + +bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out); + +// Platform specific functions. +void SetSignalHandler(const FuzzingOptions& Options); + +void SleepSeconds(int Seconds); + +unsigned long GetPid(); + +size_t GetPeakRSSMb(); + +int ExecuteCommand(const std::string &Command); + +FILE *OpenProcessPipe(const char *Command, const char *Mode); + +const void *SearchMemory(const void *haystack, size_t haystacklen, + const void *needle, size_t needlelen); + +std::string CloneArgsWithoutX(const std::vector &Args, + const char *X1, const char *X2); + +inline std::string CloneArgsWithoutX(const std::vector &Args, + const char *X) { + return CloneArgsWithoutX(Args, X, X); +} + +} // namespace fuzzer + +#endif // LLVM_FUZZER_UTIL_H diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp b/test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp new file mode 100644 index 00000000..9674368c --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp @@ -0,0 +1,152 @@ +//===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Misc utils for Darwin. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_APPLE + +#include "FuzzerIO.h" +#include +#include +#include +#include + +// There is no header for this on macOS so declare here +extern "C" char **environ; + +namespace fuzzer { + +static std::mutex SignalMutex; +// Global variables used to keep track of how signal handling should be +// restored. They should **not** be accessed without holding `SignalMutex`. +static int ActiveThreadCount = 0; +static struct sigaction OldSigIntAction; +static struct sigaction OldSigQuitAction; +static sigset_t OldBlockedSignalsSet; + +// This is a reimplementation of Libc's `system()`. On Darwin the Libc +// implementation contains a mutex which prevents it from being used +// concurrently. This implementation **can** be used concurrently. It sets the +// signal handlers when the first thread enters and restores them when the last +// thread finishes execution of the function and ensures this is not racey by +// using a mutex. +int ExecuteCommand(const std::string &Command) { + posix_spawnattr_t SpawnAttributes; + if (posix_spawnattr_init(&SpawnAttributes)) + return -1; + // Block and ignore signals of the current process when the first thread + // enters. + { + std::lock_guard Lock(SignalMutex); + if (ActiveThreadCount == 0) { + static struct sigaction IgnoreSignalAction; + sigset_t BlockedSignalsSet; + memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction)); + IgnoreSignalAction.sa_handler = SIG_IGN; + + if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) { + Printf("Failed to ignore SIGINT\n"); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + } + if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) { + Printf("Failed to ignore SIGQUIT\n"); + // Try our best to restore the signal handlers. + (void)sigaction(SIGINT, &OldSigIntAction, NULL); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + } + + (void)sigemptyset(&BlockedSignalsSet); + (void)sigaddset(&BlockedSignalsSet, SIGCHLD); + if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) == + -1) { + Printf("Failed to block SIGCHLD\n"); + // Try our best to restore the signal handlers. + (void)sigaction(SIGQUIT, &OldSigQuitAction, NULL); + (void)sigaction(SIGINT, &OldSigIntAction, NULL); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + } + } + ++ActiveThreadCount; + } + + // NOTE: Do not introduce any new `return` statements past this + // point. It is important that `ActiveThreadCount` always be decremented + // when leaving this function. + + // Make sure the child process uses the default handlers for the + // following signals rather than inheriting what the parent has. + sigset_t DefaultSigSet; + (void)sigemptyset(&DefaultSigSet); + (void)sigaddset(&DefaultSigSet, SIGQUIT); + (void)sigaddset(&DefaultSigSet, SIGINT); + (void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet); + // Make sure the child process doesn't block SIGCHLD + (void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet); + short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + (void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags); + + pid_t Pid; + char **Environ = environ; // Read from global + const char *CommandCStr = Command.c_str(); + const char *Argv[] = {"sh", "-c", CommandCStr, NULL}; + int ErrorCode = 0, ProcessStatus = 0; + // FIXME: We probably shouldn't hardcode the shell path. + ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes, + (char *const *)Argv, Environ); + (void)posix_spawnattr_destroy(&SpawnAttributes); + if (!ErrorCode) { + pid_t SavedPid = Pid; + do { + // Repeat until call completes uninterrupted. + Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0); + } while (Pid == -1 && errno == EINTR); + if (Pid == -1) { + // Fail for some other reason. + ProcessStatus = -1; + } + } else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) { + // Fork failure. + ProcessStatus = -1; + } else { + // Shell execution failure. + ProcessStatus = W_EXITCODE(127, 0); + } + + // Restore the signal handlers of the current process when the last thread + // using this function finishes. + { + std::lock_guard Lock(SignalMutex); + --ActiveThreadCount; + if (ActiveThreadCount == 0) { + bool FailedRestore = false; + if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) { + Printf("Failed to restore SIGINT handling\n"); + FailedRestore = true; + } + if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) { + Printf("Failed to restore SIGQUIT handling\n"); + FailedRestore = true; + } + if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) { + Printf("Failed to unblock SIGCHLD\n"); + FailedRestore = true; + } + if (FailedRestore) + ProcessStatus = -1; + } + } + return ProcessStatus; +} + +} // namespace fuzzer + +#endif // LIBFUZZER_APPLE diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp b/test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp new file mode 100644 index 00000000..dfe7e6f4 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp @@ -0,0 +1,24 @@ +//===- FuzzerUtilLinux.cpp - Misc utils for Linux. ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Misc utils for Linux. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_LINUX + +#include + +namespace fuzzer { + +int ExecuteCommand(const std::string &Command) { + return system(Command.c_str()); +} + +} // namespace fuzzer + +#endif // LIBFUZZER_LINUX diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp b/test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp new file mode 100644 index 00000000..8b484b8e --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp @@ -0,0 +1,117 @@ +//===- FuzzerUtilPosix.cpp - Misc utils for Posix. ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Misc utils implementation using Posix API. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_POSIX +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +static void AlarmHandler(int, siginfo_t *, void *) { + Fuzzer::StaticAlarmCallback(); +} + +static void CrashHandler(int, siginfo_t *, void *) { + Fuzzer::StaticCrashSignalCallback(); +} + +static void InterruptHandler(int, siginfo_t *, void *) { + Fuzzer::StaticInterruptCallback(); +} + +static void SetSigaction(int signum, + void (*callback)(int, siginfo_t *, void *)) { + struct sigaction sigact; + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_sigaction = callback; + if (sigaction(signum, &sigact, 0)) { + Printf("libFuzzer: sigaction failed with %d\n", errno); + exit(1); + } +} + +void SetTimer(int Seconds) { + struct itimerval T { + {Seconds, 0}, { Seconds, 0 } + }; + if (setitimer(ITIMER_REAL, &T, nullptr)) { + Printf("libFuzzer: setitimer failed with %d\n", errno); + exit(1); + } + SetSigaction(SIGALRM, AlarmHandler); +} + +void SetSignalHandler(const FuzzingOptions& Options) { + if (Options.UnitTimeoutSec > 0) + SetTimer(Options.UnitTimeoutSec / 2 + 1); + if (Options.HandleInt) + SetSigaction(SIGINT, InterruptHandler); + if (Options.HandleTerm) + SetSigaction(SIGTERM, InterruptHandler); + if (Options.HandleSegv) + SetSigaction(SIGSEGV, CrashHandler); + if (Options.HandleBus) + SetSigaction(SIGBUS, CrashHandler); + if (Options.HandleAbrt) + SetSigaction(SIGABRT, CrashHandler); + if (Options.HandleIll) + SetSigaction(SIGILL, CrashHandler); + if (Options.HandleFpe) + SetSigaction(SIGFPE, CrashHandler); +} + +void SleepSeconds(int Seconds) { + sleep(Seconds); // Use C API to avoid coverage from instrumented libc++. +} + +unsigned long GetPid() { return (unsigned long)getpid(); } + +size_t GetPeakRSSMb() { + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage)) + return 0; + if (LIBFUZZER_LINUX) { + // ru_maxrss is in KiB + return usage.ru_maxrss >> 10; + } else if (LIBFUZZER_APPLE) { + // ru_maxrss is in bytes + return usage.ru_maxrss >> 20; + } + assert(0 && "GetPeakRSSMb() is not implemented for your platform"); + return 0; +} + +FILE *OpenProcessPipe(const char *Command, const char *Mode) { + return popen(Command, Mode); +} + +const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, + size_t PattLen) { + return memmem(Data, DataLen, Patt, PattLen); +} + +} // namespace fuzzer + +#endif // LIBFUZZER_POSIX diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp b/test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp new file mode 100644 index 00000000..64adb7cd --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp @@ -0,0 +1,182 @@ +//===- FuzzerUtilWindows.cpp - Misc utils for Windows. --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Misc utils implementation for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_WINDOWS +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +static const FuzzingOptions* HandlerOpt = nullptr; + +LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) { + switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { + case EXCEPTION_ACCESS_VIOLATION: + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + case EXCEPTION_STACK_OVERFLOW: + if (HandlerOpt->HandleSegv) + Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_DATATYPE_MISALIGNMENT: + case EXCEPTION_IN_PAGE_ERROR: + if (HandlerOpt->HandleBus) + Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + case EXCEPTION_PRIV_INSTRUCTION: + if (HandlerOpt->HandleIll) + Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_INVALID_OPERATION: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_STACK_CHECK: + case EXCEPTION_FLT_UNDERFLOW: + case EXCEPTION_INT_DIVIDE_BY_ZERO: + case EXCEPTION_INT_OVERFLOW: + if (HandlerOpt->HandleFpe) + Fuzzer::StaticCrashSignalCallback(); + break; + } + return EXCEPTION_CONTINUE_SEARCH; +} + +BOOL WINAPI CtrlHandler(DWORD dwCtrlType) { + switch (dwCtrlType) { + case CTRL_C_EVENT: + if (HandlerOpt->HandleInt) + Fuzzer::StaticInterruptCallback(); + return TRUE; + case CTRL_BREAK_EVENT: + if (HandlerOpt->HandleTerm) + Fuzzer::StaticInterruptCallback(); + return TRUE; + } + return FALSE; +} + +void CALLBACK AlarmHandler(PVOID, BOOLEAN) { + Fuzzer::StaticAlarmCallback(); +} + +class TimerQ { + HANDLE TimerQueue; + public: + TimerQ() : TimerQueue(NULL) {}; + ~TimerQ() { + if (TimerQueue) + DeleteTimerQueueEx(TimerQueue, NULL); + }; + void SetTimer(int Seconds) { + if (!TimerQueue) { + TimerQueue = CreateTimerQueue(); + if (!TimerQueue) { + Printf("libFuzzer: CreateTimerQueue failed.\n"); + exit(1); + } + } + HANDLE Timer; + if (!CreateTimerQueueTimer(&Timer, TimerQueue, AlarmHandler, NULL, + Seconds*1000, Seconds*1000, 0)) { + Printf("libFuzzer: CreateTimerQueueTimer failed.\n"); + exit(1); + } + }; +}; + +static TimerQ Timer; + +static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); } + +void SetSignalHandler(const FuzzingOptions& Options) { + HandlerOpt = &Options; + + if (Options.UnitTimeoutSec > 0) + Timer.SetTimer(Options.UnitTimeoutSec / 2 + 1); + + if (Options.HandleInt || Options.HandleTerm) + if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) { + DWORD LastError = GetLastError(); + Printf("libFuzzer: SetConsoleCtrlHandler failed (Error code: %lu).\n", + LastError); + exit(1); + } + + if (Options.HandleSegv || Options.HandleBus || Options.HandleIll || + Options.HandleFpe) + if (!AddVectoredExceptionHandler(1, ExceptionHandler)) { + Printf("libFuzzer: AddVectoredExceptionHandler failed.\n"); + exit(1); + } + + if (Options.HandleAbrt) + if (SIG_ERR == signal(SIGABRT, CrashHandler)) { + Printf("libFuzzer: signal failed with %d\n", errno); + exit(1); + } +} + +void SleepSeconds(int Seconds) { Sleep(Seconds * 1000); } + +unsigned long GetPid() { return GetCurrentProcessId(); } + +size_t GetPeakRSSMb() { + PROCESS_MEMORY_COUNTERS info; + if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info))) + return 0; + return info.PeakWorkingSetSize >> 20; +} + +FILE *OpenProcessPipe(const char *Command, const char *Mode) { + return _popen(Command, Mode); +} + +int ExecuteCommand(const std::string &Command) { + return system(Command.c_str()); +} + +const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, + size_t PattLen) { + // TODO: make this implementation more efficient. + const char *Cdata = (const char *)Data; + const char *Cpatt = (const char *)Patt; + + if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen) + return NULL; + + if (PattLen == 1) + return memchr(Data, *Cpatt, DataLen); + + const char *End = Cdata + DataLen - PattLen + 1; + + for (const char *It = Cdata; It < End; ++It) + if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0) + return It; + + return NULL; +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS diff --git a/test/fuzz_test/Fuzzer/FuzzerValueBitMap.h b/test/fuzz_test/Fuzzer/FuzzerValueBitMap.h new file mode 100644 index 00000000..0692acd1 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerValueBitMap.h @@ -0,0 +1,87 @@ +//===- FuzzerValueBitMap.h - INTERNAL - Bit map -----------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// ValueBitMap. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_VALUE_BIT_MAP_H +#define LLVM_FUZZER_VALUE_BIT_MAP_H + +#include "FuzzerDefs.h" + +namespace fuzzer { + +// A bit map containing kMapSizeInWords bits. +struct ValueBitMap { + static const size_t kMapSizeInBits = 65371; // Prime. + static const size_t kMapSizeInBitsAligned = 65536; // 2^16 + static const size_t kBitsInWord = (sizeof(uintptr_t) * 8); + static const size_t kMapSizeInWords = kMapSizeInBitsAligned / kBitsInWord; + public: + static const size_t kNumberOfItems = kMapSizeInBits; + // Clears all bits. + void Reset() { memset(Map, 0, sizeof(Map)); } + + // Computes a hash function of Value and sets the corresponding bit. + // Returns true if the bit was changed from 0 to 1. + inline bool AddValue(uintptr_t Value) { + uintptr_t Idx = Value < kMapSizeInBits ? Value : Value % kMapSizeInBits; + uintptr_t WordIdx = Idx / kBitsInWord; + uintptr_t BitIdx = Idx % kBitsInWord; + uintptr_t Old = Map[WordIdx]; + uintptr_t New = Old | (1UL << BitIdx); + Map[WordIdx] = New; + return New != Old; + } + + inline bool Get(uintptr_t Idx) { + assert(Idx < kMapSizeInBits); + uintptr_t WordIdx = Idx / kBitsInWord; + uintptr_t BitIdx = Idx % kBitsInWord; + return Map[WordIdx] & (1UL << BitIdx); + } + + size_t GetNumBitsSinceLastMerge() const { return NumBits; } + + // Merges 'Other' into 'this', clears 'Other', updates NumBits, + // returns true if new bits were added. + ATTRIBUTE_TARGET_POPCNT + bool MergeFrom(ValueBitMap &Other) { + uintptr_t Res = 0; + size_t OldNumBits = NumBits; + for (size_t i = 0; i < kMapSizeInWords; i++) { + auto O = Other.Map[i]; + auto M = Map[i]; + if (O) { + Map[i] = (M |= O); + Other.Map[i] = 0; + } + if (M) + Res += __builtin_popcountl(M); + } + NumBits = Res; + return OldNumBits < NumBits; + } + + template + void ForEach(Callback CB) { + for (size_t i = 0; i < kMapSizeInWords; i++) + if (uintptr_t M = Map[i]) + for (size_t j = 0; j < sizeof(M) * 8; j++) + if (M & ((uintptr_t)1 << j)) + CB(i * sizeof(M) * 8 + j); + } + + private: + size_t NumBits = 0; + uintptr_t Map[kMapSizeInWords] __attribute__((aligned(512))); +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_VALUE_BIT_MAP_H diff --git a/test/fuzz_test/Fuzzer/README.txt b/test/fuzz_test/Fuzzer/README.txt new file mode 100644 index 00000000..79f49b55 --- /dev/null +++ b/test/fuzz_test/Fuzzer/README.txt @@ -0,0 +1,2 @@ +Move to http://llvm.org/docs/LibFuzzer.html + diff --git a/test/fuzz_test/Fuzzer/afl/afl_driver.cpp b/test/fuzz_test/Fuzzer/afl/afl_driver.cpp new file mode 100644 index 00000000..fc958955 --- /dev/null +++ b/test/fuzz_test/Fuzzer/afl/afl_driver.cpp @@ -0,0 +1,295 @@ +//===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +//===----------------------------------------------------------------------===// + +/* This file allows to fuzz libFuzzer-style target functions + (LLVMFuzzerTestOneInput) with AFL using AFL's persistent (in-process) mode. + +Usage: +################################################################################ +cat << EOF > test_fuzzer.cc +#include +#include +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'H') + if (size > 1 && data[1] == 'I') + if (size > 2 && data[2] == '!') + __builtin_trap(); + return 0; +} +EOF +# Build your target with -fsanitize-coverage=trace-pc using fresh clang. +clang -g -fsanitize-coverage=trace-pc test_fuzzer.cc -c +# Build afl-llvm-rt.o.c from the AFL distribution. +clang -c -w $AFL_HOME/llvm_mode/afl-llvm-rt.o.c +# Build this file, link it with afl-llvm-rt.o.o and the target code. +clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o +# Run AFL: +rm -rf IN OUT; mkdir IN OUT; echo z > IN/z; +$AFL_HOME/afl-fuzz -i IN -o OUT ./a.out +################################################################################ +Environment Variables: +There are a few environment variables that can be set to use features that +afl-fuzz doesn't have. + +AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file +specified. If the file does not exist, it is created. This is useful for getting +stack traces (when using ASAN for example) or original error messages on hard to +reproduce bugs. + +AFL_DRIVER_EXTRA_STATS_FILENAME: Setting this causes afl_driver to write extra +statistics to the file specified. Currently these are peak_rss_mb +(the peak amount of virtual memory used in MB) and slowest_unit_time_secs. If +the file does not exist it is created. If the file does exist then +afl_driver assumes it was restarted by afl-fuzz and will try to read old +statistics from the file. If that fails then the process will quit. + +*/ +#include +#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 +#elif __APPLE__ +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_APPLE 1 +#else +#error "Support for your platform has not been implemented" +#endif + +// Used to avoid repeating error checking boilerplate. If cond is false, a +// fatal error has occured in the program. In this event print error_message +// to stderr and abort(). Otherwise do nothing. Note that setting +// AFL_DRIVER_STDERR_DUPLICATE_FILENAME may cause error_message to be appended +// to the file as well, if the error occurs after the duplication is performed. +#define CHECK_ERROR(cond, error_message) \ + if (!(cond)) { \ + fprintf(stderr, (error_message)); \ + abort(); \ + } + +// libFuzzer interface is thin, so we don't include any libFuzzer headers. +extern "C" { +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); +} + +// Notify AFL about persistent mode. +static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##"; +extern "C" int __afl_persistent_loop(unsigned int); +static volatile char suppress_warning2 = AFL_PERSISTENT[0]; + +// Notify AFL about deferred forkserver. +static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; +extern "C" void __afl_manual_init(); +static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0]; + +// Input buffer. +static const size_t kMaxAflInputSize = 1 << 20; +static uint8_t AflInputBuf[kMaxAflInputSize]; + +// Variables we need for writing to the extra stats file. +static FILE *extra_stats_file = NULL; +static uint32_t previous_peak_rss = 0; +static time_t slowest_unit_time_secs = 0; +static const int kNumExtraStats = 2; +static const char *kExtraStatsFormatString = "peak_rss_mb : %u\n" + "slowest_unit_time_sec : %u\n"; + +// Copied from FuzzerUtil.cpp. +size_t GetPeakRSSMb() { + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage)) + return 0; + if (LIBFUZZER_LINUX) { + // ru_maxrss is in KiB + return usage.ru_maxrss >> 10; + } else if (LIBFUZZER_APPLE) { + // ru_maxrss is in bytes + return usage.ru_maxrss >> 20; + } + assert(0 && "GetPeakRSSMb() is not implemented for your platform"); + return 0; +} + +// Based on SetSigaction in FuzzerUtil.cpp +static void SetSigaction(int signum, + void (*callback)(int, siginfo_t *, void *)) { + struct sigaction sigact; + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_sigaction = callback; + if (sigaction(signum, &sigact, 0)) { + fprintf(stderr, "libFuzzer: sigaction failed with %d\n", errno); + exit(1); + } +} + +// Write extra stats to the file specified by the user. If none is specified +// this function will never be called. +static void write_extra_stats() { + uint32_t peak_rss = GetPeakRSSMb(); + + if (peak_rss < previous_peak_rss) + peak_rss = previous_peak_rss; + + int chars_printed = fprintf(extra_stats_file, kExtraStatsFormatString, + peak_rss, slowest_unit_time_secs); + + CHECK_ERROR(chars_printed != 0, "Failed to write extra_stats_file"); + + CHECK_ERROR(fclose(extra_stats_file) == 0, + "Failed to close extra_stats_file"); +} + +// Call write_extra_stats before we exit. +static void crash_handler(int, siginfo_t *, void *) { + // Make sure we don't try calling write_extra_stats again if we crashed while + // trying to call it. + static bool first_crash = true; + CHECK_ERROR(first_crash, + "Crashed in crash signal handler. This is a bug in the fuzzer."); + + first_crash = false; + write_extra_stats(); +} + +// If the user has specified an extra_stats_file through the environment +// variable AFL_DRIVER_EXTRA_STATS_FILENAME, then perform necessary set up +// to write stats to it on exit. If no file is specified, do nothing. Otherwise +// install signal and exit handlers to write to the file when the process exits. +// Then if the file doesn't exist create it and set extra stats to 0. But if it +// does exist then read the initial values of the extra stats from the file +// and check that the file is writable. +static void maybe_initialize_extra_stats() { + // If AFL_DRIVER_EXTRA_STATS_FILENAME isn't set then we have nothing to do. + char *extra_stats_filename = getenv("AFL_DRIVER_EXTRA_STATS_FILENAME"); + if (!extra_stats_filename) + return; + + // Open the file and find the previous peak_rss_mb value. + // This is necessary because the fuzzing process is restarted after N + // iterations are completed. So we may need to get this value from a previous + // process to be accurate. + extra_stats_file = fopen(extra_stats_filename, "r"); + + // If extra_stats_file already exists: read old stats from it. + if (extra_stats_file) { + int matches = fscanf(extra_stats_file, kExtraStatsFormatString, + &previous_peak_rss, &slowest_unit_time_secs); + + // Make sure we have read a real extra stats file and that we have used it + // to set slowest_unit_time_secs and previous_peak_rss. + CHECK_ERROR(matches == kNumExtraStats, "Extra stats file is corrupt"); + + CHECK_ERROR(fclose(extra_stats_file) == 0, "Failed to close file"); + + // Now open the file for writing. + extra_stats_file = fopen(extra_stats_filename, "w"); + CHECK_ERROR(extra_stats_file, + "Failed to open extra stats file for writing"); + } else { + // Looks like this is the first time in a fuzzing job this is being called. + extra_stats_file = fopen(extra_stats_filename, "w+"); + CHECK_ERROR(extra_stats_file, "failed to create extra stats file"); + } + + // Make sure that crash_handler gets called on any kind of fatal error. + int crash_signals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGILL, SIGFPE, SIGINT, + SIGTERM}; + + const size_t num_signals = sizeof(crash_signals) / sizeof(crash_signals[0]); + + for (size_t idx = 0; idx < num_signals; idx++) + SetSigaction(crash_signals[idx], crash_handler); + + // Make sure it gets called on other kinds of exits. + atexit(write_extra_stats); +} + +// If the user asks us to duplicate stderr, then do it. +static void maybe_duplicate_stderr() { + char* stderr_duplicate_filename = + getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); + + if (!stderr_duplicate_filename) + return; + + FILE* stderr_duplicate_stream = + freopen(stderr_duplicate_filename, "a+", stderr); + + if (!stderr_duplicate_stream) { + fprintf( + stderr, + "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); + abort(); + } +} + +int main(int argc, char **argv) { + fprintf(stderr, "======================= INFO =========================\n" + "This binary is built for AFL-fuzz.\n" + "To run the target function on a single input execute this:\n" + " %s < INPUT_FILE\n" + "To run the fuzzing execute this:\n" + " afl-fuzz [afl-flags] %s [N] " + "-- run N fuzzing iterations before " + "re-spawning the process (default: 1000)\n" + "======================================================\n", + argv[0], argv[0]); + if (LLVMFuzzerInitialize) + LLVMFuzzerInitialize(&argc, &argv); + // Do any other expensive one-time initialization here. + + maybe_duplicate_stderr(); + maybe_initialize_extra_stats(); + + __afl_manual_init(); + + int N = 1000; + if (argc >= 2) + N = atoi(argv[1]); + assert(N > 0); + time_t unit_time_secs; + int num_runs = 0; + while (__afl_persistent_loop(N)) { + ssize_t n_read = read(0, AflInputBuf, kMaxAflInputSize); + if (n_read > 0) { + // Copy AflInputBuf into a separate buffer to let asan find buffer + // overflows. Don't use unique_ptr/etc to avoid extra dependencies. + uint8_t *copy = new uint8_t[n_read]; + memcpy(copy, AflInputBuf, n_read); + + struct timeval unit_start_time; + CHECK_ERROR(gettimeofday(&unit_start_time, NULL) == 0, + "Calling gettimeofday failed"); + + num_runs++; + LLVMFuzzerTestOneInput(copy, n_read); + + struct timeval unit_stop_time; + CHECK_ERROR(gettimeofday(&unit_stop_time, NULL) == 0, + "Calling gettimeofday failed"); + + // Update slowest_unit_time_secs if we see a new max. + unit_time_secs = unit_stop_time.tv_sec - unit_start_time.tv_sec; + if (slowest_unit_time_secs < unit_time_secs) + slowest_unit_time_secs = unit_time_secs; + + delete[] copy; + } + } + fprintf(stderr, "%s: successfully executed %d input(s)\n", argv[0], num_runs); +} diff --git a/test/fuzz_test/Fuzzer/build.sh b/test/fuzz_test/Fuzzer/build.sh new file mode 100755 index 00000000..27c148ad --- /dev/null +++ b/test/fuzz_test/Fuzzer/build.sh @@ -0,0 +1,10 @@ +#!/bin/bash +LIBFUZZER_SRC_DIR=$(dirname $0) +for f in $LIBFUZZER_SRC_DIR/*.cpp; do + clang -g -O2 -fno-omit-frame-pointer -std=c++11 $f -c & +done +wait +rm -f libFuzzer.a +ar ru libFuzzer.a Fuzzer*.o +rm -f Fuzzer*.o + diff --git a/test/fuzz_test/Fuzzer/cxx.dict b/test/fuzz_test/Fuzzer/cxx.dict new file mode 100644 index 00000000..41350f47 --- /dev/null +++ b/test/fuzz_test/Fuzzer/cxx.dict @@ -0,0 +1,122 @@ +"++" +"--" +"<<" +">>" +"+=" +"-=" +"*=" +"/=" +">>=" +"<<=" +"&=" +"|=" +"^=" +"%=" +"!=" +"&&" +"||" +"==" +">=" +"<=" +"->" +"alignas" +"alignof" +"and" +"and_eq" +"asm" +"auto" +"bitand" +"bitor" +"bool" +"break" +"case" +"catch" +"char" +"char16_t" +"char32_t" +"class" +"compl" +"concept" +"const" +"constexpr" +"const_cast" +"continue" +"decltype" +"default" +"delete" +"do" +"double" +"dynamic_cast" +"else" +"enum" +"explicit" +"export" +"extern" +"false" +"float" +"for" +"friend" +"goto" +"if" +"inline" +"int" +"long" +"mutable" +"namespace" +"new" +"noexcept" +"not" +"not_eq" +"nullptr" +"operator" +"or" +"or_eq" +"private" +"protected" +"public" +"register" +"reinterpret_cast" +"requires" +"return" +"short" +"signed" +"sizeof" +"static" +"static_assert" +"static_cast" +"struct" +"switch" +"template" +"this" +"thread_local" +"throw" +"true" +"try" +"typedef" +"typeid" +"typename" +"union" +"unsigned" +"using" +"virtual" +"void" +"volatile" +"wchar_t" +"while" +"xor" +"xor_eq" +"if" +"elif" +"else" +"endif" +"defined" +"ifdef" +"ifndef" +"define" +"undef" +"include" +"line" +"error" +"pragma" +"override" +"final" diff --git a/test/fuzz_test/Fuzzer/standalone/StandaloneFuzzTargetMain.c b/test/fuzz_test/Fuzzer/standalone/StandaloneFuzzTargetMain.c new file mode 100644 index 00000000..0d76ea49 --- /dev/null +++ b/test/fuzz_test/Fuzzer/standalone/StandaloneFuzzTargetMain.c @@ -0,0 +1,41 @@ +/*===- StandaloneFuzzTargetMain.c - standalone main() for fuzz targets. ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This main() function can be linked to a fuzz target (i.e. a library +// that exports LLVMFuzzerTestOneInput() and possibly LLVMFuzzerInitialize()) +// instead of libFuzzer. This main() function will not perform any fuzzing +// but will simply feed all input files one by one to the fuzz target. +// +// Use this file to provide reproducers for bugs when linking against libFuzzer +// or other fuzzing engine is undesirable. +//===----------------------------------------------------------------------===*/ +#include +#include +#include + +extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size); +__attribute__((weak)) extern int LLVMFuzzerInitialize(int *argc, char ***argv); +int main(int argc, char **argv) { + fprintf(stderr, "StandaloneFuzzTargetMain: running %d inputs\n", argc - 1); + if (LLVMFuzzerInitialize) + LLVMFuzzerInitialize(&argc, &argv); + for (int i = 1; i < argc; i++) { + fprintf(stderr, "Running: %s\n", argv[i]); + FILE *f = fopen(argv[i], "r"); + assert(f); + fseek(f, 0, SEEK_END); + size_t len = ftell(f); + fseek(f, 0, SEEK_SET); + unsigned char *buf = (unsigned char*)malloc(len); + size_t n_read = fread(buf, 1, len, f); + assert(n_read == len); + LLVMFuzzerTestOneInput(buf, len); + free(buf); + fprintf(stderr, "Done: %s: (%zd bytes)\n", argv[i], n_read); + } +} diff --git a/test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp b/test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp new file mode 100644 index 00000000..3dd0b611 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp @@ -0,0 +1,22 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Contains dummy functions used to avoid dependency on AFL. +#include +#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/test/fuzz_test/Fuzzer/test/AbsNegAndConstant64Test.cpp b/test/fuzz_test/Fuzzer/test/AbsNegAndConstant64Test.cpp new file mode 100644 index 00000000..57748143 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/AbsNegAndConstant64Test.cpp @@ -0,0 +1,23 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// abs(x) < 0 and y == Const puzzle, 64-bit variant. +#include +#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 (labs(x) < 0 && y == 0xbaddcafedeadbeefUL) { + printf("BINGO; Found the target, exiting; x = 0x%lx y 0x%lx\n", x, y); + exit(1); + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/AbsNegAndConstantTest.cpp b/test/fuzz_test/Fuzzer/test/AbsNegAndConstantTest.cpp new file mode 100644 index 00000000..69075a45 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/AbsNegAndConstantTest.cpp @@ -0,0 +1,23 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// abs(x) < 0 and y == Const puzzle. +#include +#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/test/fuzz_test/Fuzzer/test/AccumulateAllocationsTest.cpp b/test/fuzz_test/Fuzzer/test/AccumulateAllocationsTest.cpp new file mode 100644 index 00000000..604d8fa2 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/AccumulateAllocationsTest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test with a more mallocs than frees, but no leak. +#include +#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/test/fuzz_test/Fuzzer/test/BufferOverflowOnInput.cpp b/test/fuzz_test/Fuzzer/test/BufferOverflowOnInput.cpp new file mode 100644 index 00000000..b9d14052 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/BufferOverflowOnInput.cpp @@ -0,0 +1,23 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the string "Hi!". +#include +#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/test/fuzz_test/Fuzzer/test/CMakeLists.txt b/test/fuzz_test/Fuzzer/test/CMakeLists.txt new file mode 100644 index 00000000..c0457746 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/CMakeLists.txt @@ -0,0 +1,217 @@ +# Build all these tests with -O0, otherwise optimizations may merge some +# basic blocks and we'll fail to discover the targets. +# We change the flags for every build type because we might be doing +# a multi-configuration build (e.g. Xcode) where CMAKE_BUILD_TYPE doesn't +# mean anything. +set(variables_to_filter + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS_MINSIZEREL + LIBFUZZER_FLAGS_BASE + ) +foreach (VARNAME ${variables_to_filter}) + string(REPLACE " " ";" BUILD_FLAGS_AS_LIST "${${VARNAME}}") + set(new_flags "") + foreach (flag ${BUILD_FLAGS_AS_LIST}) + # NOTE: Use of XX here is to avoid a CMake warning due to CMP0054 + if (NOT ("XX${flag}" MATCHES "XX-O[0123s]")) + set(new_flags "${new_flags} ${flag}") + else() + set(new_flags "${new_flags} -O0") + endif() + endforeach() + set(${VARNAME} "${new_flags}") +endforeach() + +# Enable the coverage instrumentation (it is disabled for the Fuzzer lib). +set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp,trace-div,trace-gep -g") + +# add_libfuzzer_test( +# 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" + ) + set(TestBinaries ${TestBinaries} LLVMFuzzer-${name} PARENT_SCOPE) +endfunction() + +# Variable to keep track of all test targets +set(TestBinaries) + +############################################################################### +# Basic tests +############################################################################### + +set(Tests + AbsNegAndConstantTest + AbsNegAndConstant64Test + AccumulateAllocationsTest + BufferOverflowOnInput + CallerCalleeTest + CounterTest + CustomCrossOverTest + CustomMutatorTest + DivTest + EmptyTest + FourIndependentBranchesTest + FullCoverageSetTest + InitializeTest + MemcmpTest + LeakTest + LeakTimeoutTest + LoadTest + NullDerefTest + NullDerefOnEmptyTest + NthRunCrashTest + OneHugeAllocTest + OutOfMemoryTest + OutOfMemorySingleLargeMallocTest + RepeatedMemcmp + RepeatedBytesTest + SimpleCmpTest + SimpleDictionaryTest + SimpleHashTest + SimpleTest + SimpleThreadedTest + SingleMemcmpTest + SingleStrcmpTest + SingleStrncmpTest + SpamyTest + ShrinkControlFlowTest + ShrinkValueProfileTest + StrcmpTest + StrncmpOOBTest + StrncmpTest + StrstrTest + SwapCmpTest + SwitchTest + Switch2Test + ThreadedLeakTest + ThreadedTest + TimeoutTest + TimeoutEmptyTest + TraceMallocTest + ) + +if(APPLE) + # LeakSanitizer is not supported on OSX right now + set(HAS_LSAN 0) + message(WARNING "LeakSanitizer is not supported on Apple platforms." + " Building and running LibFuzzer LeakSanitizer tests is disabled." + ) +else() + set(HAS_LSAN 1) +endif() + +foreach(Test ${Tests}) + add_libfuzzer_test(${Test} SOURCES ${Test}.cpp) +endforeach() + + +############################################################################### +# Unit tests +############################################################################### + +add_executable(LLVMFuzzer-Unittest + FuzzerUnittest.cpp + ) + +add_executable(LLVMFuzzer-StandaloneInitializeTest + InitializeTest.cpp + ../standalone/StandaloneFuzzTargetMain.c + ) + +target_link_libraries(LLVMFuzzer-Unittest + gtest + gtest_main + LLVMFuzzerNoMain + ) + +target_include_directories(LLVMFuzzer-Unittest PRIVATE + "${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include" + ) + +set(TestBinaries ${TestBinaries} LLVMFuzzer-Unittest) +set_target_properties(LLVMFuzzer-Unittest + PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_CURRENT_BINARY_DIR}" +) + +set(TestBinaries ${TestBinaries} LLVMFuzzer-StandaloneInitializeTest) +set_target_properties(LLVMFuzzer-StandaloneInitializeTest + PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_CURRENT_BINARY_DIR}" +) + +############################################################################### +# Additional tests +############################################################################### + +include_directories(..) + +# add_subdirectory(uninstrumented) +add_subdirectory(no-coverage) +add_subdirectory(ubsan) + +add_library(LLVMFuzzer-DSO1 SHARED DSO1.cpp) +add_library(LLVMFuzzer-DSO2 SHARED DSO2.cpp) + +add_executable(LLVMFuzzer-DSOTest + DSOTestMain.cpp + DSOTestExtra.cpp) + +target_link_libraries(LLVMFuzzer-DSOTest + LLVMFuzzer-DSO1 + LLVMFuzzer-DSO2 + LLVMFuzzer + ) + +set_target_properties(LLVMFuzzer-DSOTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}/lib/Fuzzer/test") +set_target_properties(LLVMFuzzer-DSO1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}/lib/Fuzzer/lib") +set_target_properties(LLVMFuzzer-DSO2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}/lib/Fuzzer/lib") + +set(TestBinaries ${TestBinaries} LLVMFuzzer-DSOTest) + +############################################################################### +# Configure lit to run the tests +# +# Note this is done after declaring all tests so we can inform lit if any tests +# need to be disabled. +############################################################################### + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/unit/lit.site.cfg + ) + +add_lit_testsuite(check-fuzzer "Running Fuzzer tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${TestBinaries} FileCheck not + ) diff --git a/test/fuzz_test/Fuzzer/test/CallerCalleeTest.cpp b/test/fuzz_test/Fuzzer/test/CallerCalleeTest.cpp new file mode 100644 index 00000000..3ec025d0 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/CallerCalleeTest.cpp @@ -0,0 +1,59 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. +// Try to find the target using the indirect caller-callee pairs. +#include +#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/test/fuzz_test/Fuzzer/test/CounterTest.cpp b/test/fuzz_test/Fuzzer/test/CounterTest.cpp new file mode 100644 index 00000000..4917934c --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/CounterTest.cpp @@ -0,0 +1,18 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test for a fuzzer: must find the case where a particular basic block is +// executed many times. +#include + +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/test/fuzz_test/Fuzzer/test/CustomCrossOverTest.cpp b/test/fuzz_test/Fuzzer/test/CustomCrossOverTest.cpp new file mode 100644 index 00000000..b624088b --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/CustomCrossOverTest.cpp @@ -0,0 +1,63 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a cutom mutator. +#include +#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/test/fuzz_test/Fuzzer/test/CustomMutatorTest.cpp b/test/fuzz_test/Fuzzer/test/CustomMutatorTest.cpp new file mode 100644 index 00000000..4f84519a --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/CustomMutatorTest.cpp @@ -0,0 +1,38 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a cutom mutator. +#include +#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/test/fuzz_test/Fuzzer/test/DSO1.cpp b/test/fuzz_test/Fuzzer/test/DSO1.cpp new file mode 100644 index 00000000..4a293890 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/DSO1.cpp @@ -0,0 +1,12 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Source code for a simple DSO. + +int DSO1(int a) { + if (a < 123456) + return 0; + return 1; +} + +void Uncovered1() { } diff --git a/test/fuzz_test/Fuzzer/test/DSO2.cpp b/test/fuzz_test/Fuzzer/test/DSO2.cpp new file mode 100644 index 00000000..04b308d1 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/DSO2.cpp @@ -0,0 +1,12 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Source code for a simple DSO. + +int DSO2(int a) { + if (a < 3598235) + return 0; + return 1; +} + +void Uncovered2() {} diff --git a/test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp b/test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp new file mode 100644 index 00000000..a2274d07 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp @@ -0,0 +1,11 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Source code for a simple DSO. + +int DSOTestExtra(int a) { + if (a < 452345) + return 0; + return 1; +} + diff --git a/test/fuzz_test/Fuzzer/test/DSOTestMain.cpp b/test/fuzz_test/Fuzzer/test/DSOTestMain.cpp new file mode 100644 index 00000000..3e225d88 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/DSOTestMain.cpp @@ -0,0 +1,31 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Source code for a simple DSO. + +#include +#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/test/fuzz_test/Fuzzer/test/DivTest.cpp b/test/fuzz_test/Fuzzer/test/DivTest.cpp new file mode 100644 index 00000000..63f6960f --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/DivTest.cpp @@ -0,0 +1,20 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer: find the interesting argument for div. +#include +#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/test/fuzz_test/Fuzzer/test/EmptyTest.cpp b/test/fuzz_test/Fuzzer/test/EmptyTest.cpp new file mode 100644 index 00000000..5e843308 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/EmptyTest.cpp @@ -0,0 +1,11 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// A fuzzer with empty target function. + +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/FourIndependentBranchesTest.cpp b/test/fuzz_test/Fuzzer/test/FourIndependentBranchesTest.cpp new file mode 100644 index 00000000..62b3be76 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/FourIndependentBranchesTest.cpp @@ -0,0 +1,22 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the string "FUZZ". +#include +#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/test/fuzz_test/Fuzzer/test/FullCoverageSetTest.cpp b/test/fuzz_test/Fuzzer/test/FullCoverageSetTest.cpp new file mode 100644 index 00000000..415e0b47 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/FullCoverageSetTest.cpp @@ -0,0 +1,24 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the string "FUZZER". +#include +#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/test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp b/test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp new file mode 100644 index 00000000..4992ef57 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp @@ -0,0 +1,738 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Avoid ODR violations (LibFuzzer is built without ASan and this test is built +// with ASan) involving C++ standard library types when using libcxx. +#define _LIBCPP_HAS_NO_ASAN + +#include "FuzzerCorpus.h" +#include "FuzzerInternal.h" +#include "FuzzerDictionary.h" +#include "FuzzerMerge.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include "gtest/gtest.h" +#include +#include + +using namespace fuzzer; + +// For now, have LLVMFuzzerTestOneInput just to make it link. +// Later we may want to make unittests that actually call LLVMFuzzerTestOneInput. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + abort(); +} + +TEST(Fuzzer, CrossOver) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + Unit A({0, 1, 2}), B({5, 6, 7}); + Unit C; + Unit Expected[] = { + { 0 }, + { 0, 1 }, + { 0, 5 }, + { 0, 1, 2 }, + { 0, 1, 5 }, + { 0, 5, 1 }, + { 0, 5, 6 }, + { 0, 1, 2, 5 }, + { 0, 1, 5, 2 }, + { 0, 1, 5, 6 }, + { 0, 5, 1, 2 }, + { 0, 5, 1, 6 }, + { 0, 5, 6, 1 }, + { 0, 5, 6, 7 }, + { 0, 1, 2, 5, 6 }, + { 0, 1, 5, 2, 6 }, + { 0, 1, 5, 6, 2 }, + { 0, 1, 5, 6, 7 }, + { 0, 5, 1, 2, 6 }, + { 0, 5, 1, 6, 2 }, + { 0, 5, 1, 6, 7 }, + { 0, 5, 6, 1, 2 }, + { 0, 5, 6, 1, 7 }, + { 0, 5, 6, 7, 1 }, + { 0, 1, 2, 5, 6, 7 }, + { 0, 1, 5, 2, 6, 7 }, + { 0, 1, 5, 6, 2, 7 }, + { 0, 1, 5, 6, 7, 2 }, + { 0, 5, 1, 2, 6, 7 }, + { 0, 5, 1, 6, 2, 7 }, + { 0, 5, 1, 6, 7, 2 }, + { 0, 5, 6, 1, 2, 7 }, + { 0, 5, 6, 1, 7, 2 }, + { 0, 5, 6, 7, 1, 2 } + }; + for (size_t Len = 1; Len < 8; Len++) { + std::set FoundUnits, ExpectedUnitsWitThisLength; + for (int Iter = 0; Iter < 3000; Iter++) { + C.resize(Len); + size_t NewSize = MD.CrossOver(A.data(), A.size(), B.data(), B.size(), + C.data(), C.size()); + C.resize(NewSize); + FoundUnits.insert(C); + } + for (const Unit &U : Expected) + if (U.size() <= Len) + ExpectedUnitsWitThisLength.insert(U); + EXPECT_EQ(ExpectedUnitsWitThisLength, FoundUnits); + } +} + +TEST(Fuzzer, Hash) { + uint8_t A[] = {'a', 'b', 'c'}; + fuzzer::Unit U(A, A + sizeof(A)); + EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d", fuzzer::Hash(U)); + U.push_back('d'); + EXPECT_EQ("81fe8bfe87576c3ecb22426f8e57847382917acf", fuzzer::Hash(U)); +} + +typedef size_t (MutationDispatcher::*Mutator)(uint8_t *Data, size_t Size, + size_t MaxSize); + +void TestEraseBytes(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + uint8_t REM0[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t REM1[8] = {0x00, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t REM2[8] = {0x00, 0x11, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t REM3[8] = {0x00, 0x11, 0x22, 0x44, 0x55, 0x66, 0x77}; + uint8_t REM4[8] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x66, 0x77}; + uint8_t REM5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x66, 0x77}; + uint8_t REM6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x77}; + uint8_t REM7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + + uint8_t REM8[6] = {0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t REM9[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}; + uint8_t REM10[6] = {0x00, 0x11, 0x22, 0x55, 0x66, 0x77}; + + uint8_t REM11[5] = {0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t REM12[5] = {0x00, 0x11, 0x22, 0x33, 0x44}; + uint8_t REM13[5] = {0x00, 0x44, 0x55, 0x66, 0x77}; + + + Random Rand(0); + MutationDispatcher MD(Rand, {}); + int FoundMask = 0; + for (int i = 0; i < NumIter; i++) { + uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + size_t NewSize = (MD.*M)(T, sizeof(T), sizeof(T)); + if (NewSize == 7 && !memcmp(REM0, T, 7)) FoundMask |= 1 << 0; + if (NewSize == 7 && !memcmp(REM1, T, 7)) FoundMask |= 1 << 1; + if (NewSize == 7 && !memcmp(REM2, T, 7)) FoundMask |= 1 << 2; + if (NewSize == 7 && !memcmp(REM3, T, 7)) FoundMask |= 1 << 3; + if (NewSize == 7 && !memcmp(REM4, T, 7)) FoundMask |= 1 << 4; + if (NewSize == 7 && !memcmp(REM5, T, 7)) FoundMask |= 1 << 5; + if (NewSize == 7 && !memcmp(REM6, T, 7)) FoundMask |= 1 << 6; + if (NewSize == 7 && !memcmp(REM7, T, 7)) FoundMask |= 1 << 7; + + if (NewSize == 6 && !memcmp(REM8, T, 6)) FoundMask |= 1 << 8; + if (NewSize == 6 && !memcmp(REM9, T, 6)) FoundMask |= 1 << 9; + if (NewSize == 6 && !memcmp(REM10, T, 6)) FoundMask |= 1 << 10; + + if (NewSize == 5 && !memcmp(REM11, T, 5)) FoundMask |= 1 << 11; + if (NewSize == 5 && !memcmp(REM12, T, 5)) FoundMask |= 1 << 12; + if (NewSize == 5 && !memcmp(REM13, T, 5)) FoundMask |= 1 << 13; + } + EXPECT_EQ(FoundMask, (1 << 14) - 1); +} + +TEST(FuzzerMutate, EraseBytes1) { + TestEraseBytes(&MutationDispatcher::Mutate_EraseBytes, 200); +} +TEST(FuzzerMutate, EraseBytes2) { + TestEraseBytes(&MutationDispatcher::Mutate, 2000); +} + +void TestInsertByte(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + int FoundMask = 0; + uint8_t INS0[8] = {0xF1, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + uint8_t INS1[8] = {0x00, 0xF2, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + uint8_t INS2[8] = {0x00, 0x11, 0xF3, 0x22, 0x33, 0x44, 0x55, 0x66}; + uint8_t INS3[8] = {0x00, 0x11, 0x22, 0xF4, 0x33, 0x44, 0x55, 0x66}; + uint8_t INS4[8] = {0x00, 0x11, 0x22, 0x33, 0xF5, 0x44, 0x55, 0x66}; + uint8_t INS5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xF6, 0x55, 0x66}; + uint8_t INS6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xF7, 0x66}; + uint8_t INS7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF8}; + for (int i = 0; i < NumIter; i++) { + uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + size_t NewSize = (MD.*M)(T, 7, 8); + if (NewSize == 8 && !memcmp(INS0, T, 8)) FoundMask |= 1 << 0; + if (NewSize == 8 && !memcmp(INS1, T, 8)) FoundMask |= 1 << 1; + if (NewSize == 8 && !memcmp(INS2, T, 8)) FoundMask |= 1 << 2; + if (NewSize == 8 && !memcmp(INS3, T, 8)) FoundMask |= 1 << 3; + if (NewSize == 8 && !memcmp(INS4, T, 8)) FoundMask |= 1 << 4; + if (NewSize == 8 && !memcmp(INS5, T, 8)) FoundMask |= 1 << 5; + if (NewSize == 8 && !memcmp(INS6, T, 8)) FoundMask |= 1 << 6; + if (NewSize == 8 && !memcmp(INS7, T, 8)) FoundMask |= 1 << 7; + } + EXPECT_EQ(FoundMask, 255); +} + +TEST(FuzzerMutate, InsertByte1) { + TestInsertByte(&MutationDispatcher::Mutate_InsertByte, 1 << 15); +} +TEST(FuzzerMutate, InsertByte2) { + TestInsertByte(&MutationDispatcher::Mutate, 1 << 17); +} + +void TestInsertRepeatedBytes(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + int FoundMask = 0; + uint8_t INS0[7] = {0x00, 0x11, 0x22, 0x33, 'a', 'a', 'a'}; + uint8_t INS1[7] = {0x00, 0x11, 0x22, 'a', 'a', 'a', 0x33}; + uint8_t INS2[7] = {0x00, 0x11, 'a', 'a', 'a', 0x22, 0x33}; + uint8_t INS3[7] = {0x00, 'a', 'a', 'a', 0x11, 0x22, 0x33}; + uint8_t INS4[7] = {'a', 'a', 'a', 0x00, 0x11, 0x22, 0x33}; + + uint8_t INS5[8] = {0x00, 0x11, 0x22, 0x33, 'b', 'b', 'b', 'b'}; + uint8_t INS6[8] = {0x00, 0x11, 0x22, 'b', 'b', 'b', 'b', 0x33}; + uint8_t INS7[8] = {0x00, 0x11, 'b', 'b', 'b', 'b', 0x22, 0x33}; + uint8_t INS8[8] = {0x00, 'b', 'b', 'b', 'b', 0x11, 0x22, 0x33}; + uint8_t INS9[8] = {'b', 'b', 'b', 'b', 0x00, 0x11, 0x22, 0x33}; + + for (int i = 0; i < NumIter; i++) { + uint8_t T[8] = {0x00, 0x11, 0x22, 0x33}; + size_t NewSize = (MD.*M)(T, 4, 8); + if (NewSize == 7 && !memcmp(INS0, T, 7)) FoundMask |= 1 << 0; + if (NewSize == 7 && !memcmp(INS1, T, 7)) FoundMask |= 1 << 1; + if (NewSize == 7 && !memcmp(INS2, T, 7)) FoundMask |= 1 << 2; + if (NewSize == 7 && !memcmp(INS3, T, 7)) FoundMask |= 1 << 3; + if (NewSize == 7 && !memcmp(INS4, T, 7)) FoundMask |= 1 << 4; + + if (NewSize == 8 && !memcmp(INS5, T, 8)) FoundMask |= 1 << 5; + if (NewSize == 8 && !memcmp(INS6, T, 8)) FoundMask |= 1 << 6; + if (NewSize == 8 && !memcmp(INS7, T, 8)) FoundMask |= 1 << 7; + if (NewSize == 8 && !memcmp(INS8, T, 8)) FoundMask |= 1 << 8; + if (NewSize == 8 && !memcmp(INS9, T, 8)) FoundMask |= 1 << 9; + + } + EXPECT_EQ(FoundMask, (1 << 10) - 1); +} + +TEST(FuzzerMutate, InsertRepeatedBytes1) { + TestInsertRepeatedBytes(&MutationDispatcher::Mutate_InsertRepeatedBytes, 10000); +} +TEST(FuzzerMutate, InsertRepeatedBytes2) { + TestInsertRepeatedBytes(&MutationDispatcher::Mutate, 300000); +} + +void TestChangeByte(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + int FoundMask = 0; + uint8_t CH0[8] = {0xF0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH1[8] = {0x00, 0xF1, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH2[8] = {0x00, 0x11, 0xF2, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH3[8] = {0x00, 0x11, 0x22, 0xF3, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0xF4, 0x55, 0x66, 0x77}; + uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xF5, 0x66, 0x77}; + uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xF5, 0x77}; + uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7}; + for (int i = 0; i < NumIter; i++) { + uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + size_t NewSize = (MD.*M)(T, 8, 9); + if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0; + if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1; + if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2; + if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3; + if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4; + if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5; + if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6; + if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7; + } + EXPECT_EQ(FoundMask, 255); +} + +TEST(FuzzerMutate, ChangeByte1) { + TestChangeByte(&MutationDispatcher::Mutate_ChangeByte, 1 << 15); +} +TEST(FuzzerMutate, ChangeByte2) { + TestChangeByte(&MutationDispatcher::Mutate, 1 << 17); +} + +void TestChangeBit(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + int FoundMask = 0; + uint8_t CH0[8] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH1[8] = {0x00, 0x13, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH2[8] = {0x00, 0x11, 0x02, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH3[8] = {0x00, 0x11, 0x22, 0x37, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0x54, 0x55, 0x66, 0x77}; + uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x54, 0x66, 0x77}; + uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x76, 0x77}; + uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7}; + for (int i = 0; i < NumIter; i++) { + uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + size_t NewSize = (MD.*M)(T, 8, 9); + if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0; + if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1; + if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2; + if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3; + if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4; + if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5; + if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6; + if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7; + } + EXPECT_EQ(FoundMask, 255); +} + +TEST(FuzzerMutate, ChangeBit1) { + TestChangeBit(&MutationDispatcher::Mutate_ChangeBit, 1 << 16); +} +TEST(FuzzerMutate, ChangeBit2) { + TestChangeBit(&MutationDispatcher::Mutate, 1 << 18); +} + +void TestShuffleBytes(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + int FoundMask = 0; + uint8_t CH0[7] = {0x00, 0x22, 0x11, 0x33, 0x44, 0x55, 0x66}; + uint8_t CH1[7] = {0x11, 0x00, 0x33, 0x22, 0x44, 0x55, 0x66}; + uint8_t CH2[7] = {0x00, 0x33, 0x11, 0x22, 0x44, 0x55, 0x66}; + uint8_t CH3[7] = {0x00, 0x11, 0x22, 0x44, 0x55, 0x66, 0x33}; + uint8_t CH4[7] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x44, 0x66}; + for (int i = 0; i < NumIter; i++) { + uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + size_t NewSize = (MD.*M)(T, 7, 7); + if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0; + if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1; + if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2; + if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3; + if (NewSize == 7 && !memcmp(CH4, T, 7)) FoundMask |= 1 << 4; + } + EXPECT_EQ(FoundMask, 31); +} + +TEST(FuzzerMutate, ShuffleBytes1) { + TestShuffleBytes(&MutationDispatcher::Mutate_ShuffleBytes, 1 << 16); +} +TEST(FuzzerMutate, ShuffleBytes2) { + TestShuffleBytes(&MutationDispatcher::Mutate, 1 << 20); +} + +void TestCopyPart(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + int FoundMask = 0; + uint8_t CH0[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11}; + uint8_t CH1[7] = {0x55, 0x66, 0x22, 0x33, 0x44, 0x55, 0x66}; + uint8_t CH2[7] = {0x00, 0x55, 0x66, 0x33, 0x44, 0x55, 0x66}; + uint8_t CH3[7] = {0x00, 0x11, 0x22, 0x00, 0x11, 0x22, 0x66}; + uint8_t CH4[7] = {0x00, 0x11, 0x11, 0x22, 0x33, 0x55, 0x66}; + + for (int i = 0; i < NumIter; i++) { + uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + size_t NewSize = (MD.*M)(T, 7, 7); + if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0; + if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1; + if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2; + if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3; + if (NewSize == 7 && !memcmp(CH4, T, 7)) FoundMask |= 1 << 4; + } + + uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11, 0x22}; + uint8_t CH6[8] = {0x22, 0x33, 0x44, 0x00, 0x11, 0x22, 0x33, 0x44}; + uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x00, 0x11, 0x22, 0x33, 0x44}; + uint8_t CH8[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x22, 0x33, 0x44}; + uint8_t CH9[8] = {0x00, 0x11, 0x22, 0x22, 0x33, 0x44, 0x33, 0x44}; + + for (int i = 0; i < NumIter; i++) { + uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + size_t NewSize = (MD.*M)(T, 5, 8); + if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5; + if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6; + if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7; + if (NewSize == 8 && !memcmp(CH8, T, 8)) FoundMask |= 1 << 8; + if (NewSize == 8 && !memcmp(CH9, T, 8)) FoundMask |= 1 << 9; + } + + EXPECT_EQ(FoundMask, 1023); +} + +TEST(FuzzerMutate, CopyPart1) { + TestCopyPart(&MutationDispatcher::Mutate_CopyPart, 1 << 10); +} +TEST(FuzzerMutate, CopyPart2) { + TestCopyPart(&MutationDispatcher::Mutate, 1 << 13); +} + +void TestAddWordFromDictionary(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + uint8_t Word1[4] = {0xAA, 0xBB, 0xCC, 0xDD}; + uint8_t Word2[3] = {0xFF, 0xEE, 0xEF}; + MD.AddWordToManualDictionary(Word(Word1, sizeof(Word1))); + MD.AddWordToManualDictionary(Word(Word2, sizeof(Word2))); + int FoundMask = 0; + uint8_t CH0[7] = {0x00, 0x11, 0x22, 0xAA, 0xBB, 0xCC, 0xDD}; + uint8_t CH1[7] = {0x00, 0x11, 0xAA, 0xBB, 0xCC, 0xDD, 0x22}; + uint8_t CH2[7] = {0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22}; + uint8_t CH3[7] = {0xAA, 0xBB, 0xCC, 0xDD, 0x00, 0x11, 0x22}; + uint8_t CH4[6] = {0x00, 0x11, 0x22, 0xFF, 0xEE, 0xEF}; + uint8_t CH5[6] = {0x00, 0x11, 0xFF, 0xEE, 0xEF, 0x22}; + uint8_t CH6[6] = {0x00, 0xFF, 0xEE, 0xEF, 0x11, 0x22}; + uint8_t CH7[6] = {0xFF, 0xEE, 0xEF, 0x00, 0x11, 0x22}; + for (int i = 0; i < NumIter; i++) { + uint8_t T[7] = {0x00, 0x11, 0x22}; + size_t NewSize = (MD.*M)(T, 3, 7); + if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0; + if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1; + if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2; + if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3; + if (NewSize == 6 && !memcmp(CH4, T, 6)) FoundMask |= 1 << 4; + if (NewSize == 6 && !memcmp(CH5, T, 6)) FoundMask |= 1 << 5; + if (NewSize == 6 && !memcmp(CH6, T, 6)) FoundMask |= 1 << 6; + if (NewSize == 6 && !memcmp(CH7, T, 6)) FoundMask |= 1 << 7; + } + EXPECT_EQ(FoundMask, 255); +} + +TEST(FuzzerMutate, AddWordFromDictionary1) { + TestAddWordFromDictionary( + &MutationDispatcher::Mutate_AddWordFromManualDictionary, 1 << 15); +} + +TEST(FuzzerMutate, AddWordFromDictionary2) { + TestAddWordFromDictionary(&MutationDispatcher::Mutate, 1 << 15); +} + +void TestAddWordFromDictionaryWithHint(Mutator M, int NumIter) { + std::unique_ptr 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, {}); + + uint8_t CH0[8] = {'1', '2', '3', '4', '5', '6', '7', '7'}; + uint8_t CH1[8] = {'1', '2', '3', '4', '5', '6', '7', '9'}; + uint8_t CH2[8] = {'2', '4', '6', '9', '1', '3', '5', '6'}; + uint8_t CH3[8] = {'0', '6', '1', '7', '2', '8', '3', '9'}; + int FoundMask = 0; + for (int i = 0; i < NumIter; i++) { + uint8_t T[8] = {'1', '2', '3', '4', '5', '6', '7', '8'}; + size_t NewSize = (MD.*M)(T, 8, 8); + /**/ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0; + else if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1; + else if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2; + else if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3; + else if (NewSize == 8) FoundMask |= 1 << 4; + } + EXPECT_EQ(FoundMask, 31); +} + +TEST(FuzzerMutate, ChangeASCIIInteger1) { + TestChangeASCIIInteger(&MutationDispatcher::Mutate_ChangeASCIIInteger, + 1 << 15); +} + +TEST(FuzzerMutate, ChangeASCIIInteger2) { + TestChangeASCIIInteger(&MutationDispatcher::Mutate, 1 << 15); +} + +void TestChangeBinaryInteger(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + + uint8_t CH0[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x79}; + uint8_t CH1[8] = {0x00, 0x11, 0x22, 0x31, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH2[8] = {0xff, 0x10, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH3[8] = {0x00, 0x11, 0x2a, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x4f, 0x66, 0x77}; + uint8_t CH5[8] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88}; + uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x00, 0x00, 0x00, 0x08, 0x77}; // Size + uint8_t CH7[8] = {0x00, 0x08, 0x00, 0x33, 0x44, 0x55, 0x66, 0x77}; // Sw(Size) + + int FoundMask = 0; + for (int i = 0; i < NumIter; i++) { + uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + size_t NewSize = (MD.*M)(T, 8, 8); + /**/ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0; + else if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1; + else if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2; + else if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3; + else if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4; + else if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5; + else if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6; + else if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7; + } + EXPECT_EQ(FoundMask, 255); +} + +TEST(FuzzerMutate, ChangeBinaryInteger1) { + TestChangeBinaryInteger(&MutationDispatcher::Mutate_ChangeBinaryInteger, + 1 << 12); +} + +TEST(FuzzerMutate, ChangeBinaryInteger2) { + TestChangeBinaryInteger(&MutationDispatcher::Mutate, 1 << 15); +} + + +TEST(FuzzerDictionary, ParseOneDictionaryEntry) { + Unit U; + EXPECT_FALSE(ParseOneDictionaryEntry("", &U)); + EXPECT_FALSE(ParseOneDictionaryEntry(" ", &U)); + EXPECT_FALSE(ParseOneDictionaryEntry("\t ", &U)); + EXPECT_FALSE(ParseOneDictionaryEntry(" \" ", &U)); + EXPECT_FALSE(ParseOneDictionaryEntry(" zz\" ", &U)); + EXPECT_FALSE(ParseOneDictionaryEntry(" \"zz ", &U)); + EXPECT_FALSE(ParseOneDictionaryEntry(" \"\" ", &U)); + EXPECT_TRUE(ParseOneDictionaryEntry("\"a\"", &U)); + EXPECT_EQ(U, Unit({'a'})); + EXPECT_TRUE(ParseOneDictionaryEntry("\"abc\"", &U)); + EXPECT_EQ(U, Unit({'a', 'b', 'c'})); + EXPECT_TRUE(ParseOneDictionaryEntry("abc=\"abc\"", &U)); + EXPECT_EQ(U, Unit({'a', 'b', 'c'})); + EXPECT_FALSE(ParseOneDictionaryEntry("\"\\\"", &U)); + EXPECT_TRUE(ParseOneDictionaryEntry("\"\\\\\"", &U)); + EXPECT_EQ(U, Unit({'\\'})); + EXPECT_TRUE(ParseOneDictionaryEntry("\"\\xAB\"", &U)); + EXPECT_EQ(U, Unit({0xAB})); + EXPECT_TRUE(ParseOneDictionaryEntry("\"\\xABz\\xDE\"", &U)); + EXPECT_EQ(U, Unit({0xAB, 'z', 0xDE})); + EXPECT_TRUE(ParseOneDictionaryEntry("\"#\"", &U)); + EXPECT_EQ(U, Unit({'#'})); + EXPECT_TRUE(ParseOneDictionaryEntry("\"\\\"\"", &U)); + EXPECT_EQ(U, Unit({'"'})); +} + +TEST(FuzzerDictionary, ParseDictionaryFile) { + std::vector Units; + EXPECT_FALSE(ParseDictionaryFile("zzz\n", &Units)); + EXPECT_FALSE(ParseDictionaryFile("", &Units)); + EXPECT_TRUE(ParseDictionaryFile("\n", &Units)); + EXPECT_EQ(Units.size(), 0U); + EXPECT_TRUE(ParseDictionaryFile("#zzzz a b c d\n", &Units)); + EXPECT_EQ(Units.size(), 0U); + EXPECT_TRUE(ParseDictionaryFile(" #zzzz\n", &Units)); + EXPECT_EQ(Units.size(), 0U); + EXPECT_TRUE(ParseDictionaryFile(" #zzzz\n", &Units)); + EXPECT_EQ(Units.size(), 0U); + EXPECT_TRUE(ParseDictionaryFile(" #zzzz\naaa=\"aa\"", &Units)); + EXPECT_EQ(Units, std::vector({Unit({'a', 'a'})})); + EXPECT_TRUE( + ParseDictionaryFile(" #zzzz\naaa=\"aa\"\n\nabc=\"abc\"", &Units)); + EXPECT_EQ(Units, + std::vector({Unit({'a', 'a'}), Unit({'a', 'b', 'c'})})); +} + +TEST(FuzzerUtil, Base64) { + EXPECT_EQ("", Base64({})); + EXPECT_EQ("YQ==", Base64({'a'})); + EXPECT_EQ("eA==", Base64({'x'})); + EXPECT_EQ("YWI=", Base64({'a', 'b'})); + EXPECT_EQ("eHk=", Base64({'x', 'y'})); + EXPECT_EQ("YWJj", Base64({'a', 'b', 'c'})); + EXPECT_EQ("eHl6", Base64({'x', 'y', 'z'})); + EXPECT_EQ("YWJjeA==", Base64({'a', 'b', 'c', 'x'})); + EXPECT_EQ("YWJjeHk=", Base64({'a', 'b', 'c', 'x', 'y'})); + EXPECT_EQ("YWJjeHl6", Base64({'a', 'b', 'c', 'x', 'y', 'z'})); +} + +TEST(Corpus, Distribution) { + Random Rand(0); + InputCorpus C(""); + size_t N = 10; + size_t TriesPerUnit = 1<<16; + for (size_t i = 0; i < N; i++) + C.AddToCorpus(Unit{ static_cast(i) }, 0); + + std::vector Hist(N); + for (size_t i = 0; i < N * TriesPerUnit; i++) { + Hist[C.ChooseUnitIdxToMutate(Rand)]++; + } + for (size_t i = 0; i < N; i++) { + // A weak sanity check that every unit gets invoked. + EXPECT_GT(Hist[i], TriesPerUnit / N / 3); + } +} + +TEST(Merge, Bad) { + const char *kInvalidInputs[] = { + "", + "x", + "3\nx", + "2\n3", + "2\n2", + "2\n2\nA\n", + "2\n2\nA\nB\nC\n", + "0\n0\n", + "1\n1\nA\nDONE 0", + "1\n1\nA\nSTARTED 1", + }; + Merger M; + for (auto S : kInvalidInputs) { + // fprintf(stderr, "TESTING:\n%s\n", S); + EXPECT_FALSE(M.Parse(S, false)); + } +} + +void EQ(const std::vector &A, const std::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()); + EXPECT_EQ(a, b); +} + +static void Merge(const std::string &Input, + const std::vector Result, + size_t NumNewFeatures) { + Merger M; + std::vector NewFiles; + EXPECT_TRUE(M.Parse(Input, true)); + EXPECT_EQ(NumNewFeatures, M.Merge(&NewFiles)); + EQ(NewFiles, Result); +} + +TEST(Merge, Good) { + Merger M; + + EXPECT_TRUE(M.Parse("1\n0\nAA\n", false)); + EXPECT_EQ(M.Files.size(), 1U); + EXPECT_EQ(M.NumFilesInFirstCorpus, 0U); + EXPECT_EQ(M.Files[0].Name, "AA"); + EXPECT_TRUE(M.LastFailure.empty()); + EXPECT_EQ(M.FirstNotProcessedFile, 0U); + + EXPECT_TRUE(M.Parse("2\n1\nAA\nBB\nSTARTED 0 42\n", false)); + EXPECT_EQ(M.Files.size(), 2U); + EXPECT_EQ(M.NumFilesInFirstCorpus, 1U); + EXPECT_EQ(M.Files[0].Name, "AA"); + EXPECT_EQ(M.Files[1].Name, "BB"); + EXPECT_EQ(M.LastFailure, "AA"); + EXPECT_EQ(M.FirstNotProcessedFile, 1U); + + EXPECT_TRUE(M.Parse("3\n1\nAA\nBB\nC\n" + "STARTED 0 1000\n" + "DONE 0 1 2 3\n" + "STARTED 1 1001\n" + "DONE 1 4 5 6 \n" + "STARTED 2 1002\n" + "", true)); + EXPECT_EQ(M.Files.size(), 3U); + EXPECT_EQ(M.NumFilesInFirstCorpus, 1U); + EXPECT_EQ(M.Files[0].Name, "AA"); + EXPECT_EQ(M.Files[0].Size, 1000U); + EXPECT_EQ(M.Files[1].Name, "BB"); + EXPECT_EQ(M.Files[1].Size, 1001U); + EXPECT_EQ(M.Files[2].Name, "C"); + EXPECT_EQ(M.Files[2].Size, 1002U); + EXPECT_EQ(M.LastFailure, "C"); + EXPECT_EQ(M.FirstNotProcessedFile, 3U); + EQ(M.Files[0].Features, {1, 2, 3}); + EQ(M.Files[1].Features, {4, 5, 6}); + + + std::vector NewFiles; + + EXPECT_TRUE(M.Parse("3\n2\nAA\nBB\nC\n" + "STARTED 0 1000\nDONE 0 1 2 3\n" + "STARTED 1 1001\nDONE 1 4 5 6 \n" + "STARTED 2 1002\nDONE 2 6 1 3 \n" + "", true)); + EXPECT_EQ(M.Files.size(), 3U); + EXPECT_EQ(M.NumFilesInFirstCorpus, 2U); + EXPECT_TRUE(M.LastFailure.empty()); + EXPECT_EQ(M.FirstNotProcessedFile, 3U); + EQ(M.Files[0].Features, {1, 2, 3}); + EQ(M.Files[1].Features, {4, 5, 6}); + EQ(M.Files[2].Features, {1, 3, 6}); + EXPECT_EQ(0U, M.Merge(&NewFiles)); + EQ(NewFiles, {}); + + EXPECT_TRUE(M.Parse("3\n1\nA\nB\nC\n" + "STARTED 0 1000\nDONE 0 1 2 3\n" + "STARTED 1 1001\nDONE 1 4 5 6 \n" + "STARTED 2 1002\nDONE 2 6 1 3\n" + "", true)); + EQ(M.Files[0].Features, {1, 2, 3}); + EQ(M.Files[1].Features, {4, 5, 6}); + EQ(M.Files[2].Features, {1, 3, 6}); + EXPECT_EQ(3U, M.Merge(&NewFiles)); + EQ(NewFiles, {"B"}); +} + +TEST(Merge, Merge) { + + Merge("3\n1\nA\nB\nC\n" + "STARTED 0 1000\nDONE 0 1 2 3\n" + "STARTED 1 1001\nDONE 1 4 5 6 \n" + "STARTED 2 1002\nDONE 2 6 1 3 \n", + {"B"}, 3); + + Merge("3\n0\nA\nB\nC\n" + "STARTED 0 2000\nDONE 0 1 2 3\n" + "STARTED 1 1001\nDONE 1 4 5 6 \n" + "STARTED 2 1002\nDONE 2 6 1 3 \n", + {"A", "B", "C"}, 6); + + Merge("4\n0\nA\nB\nC\nD\n" + "STARTED 0 2000\nDONE 0 1 2 3\n" + "STARTED 1 1101\nDONE 1 4 5 6 \n" + "STARTED 2 1102\nDONE 2 6 1 3 100 \n" + "STARTED 3 1000\nDONE 3 1 \n", + {"A", "B", "C", "D"}, 7); + + Merge("4\n1\nA\nB\nC\nD\n" + "STARTED 0 2000\nDONE 0 4 5 6 7 8\n" + "STARTED 1 1100\nDONE 1 1 2 3 \n" + "STARTED 2 1100\nDONE 2 2 3 \n" + "STARTED 3 1000\nDONE 3 1 \n", + {"B", "D"}, 3); +} diff --git a/test/fuzz_test/Fuzzer/test/InitializeTest.cpp b/test/fuzz_test/Fuzzer/test/InitializeTest.cpp new file mode 100644 index 00000000..0d6a0fda --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/InitializeTest.cpp @@ -0,0 +1,28 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Make sure LLVMFuzzerInitialize is called. +#include +#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/test/fuzz_test/Fuzzer/test/LeakTest.cpp b/test/fuzz_test/Fuzzer/test/LeakTest.cpp new file mode 100644 index 00000000..22e51640 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/LeakTest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test with a leak. +#include +#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/test/fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp b/test/fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp new file mode 100644 index 00000000..4f31b3e5 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test with a leak. +#include +#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/test/fuzz_test/Fuzzer/test/LoadTest.cpp b/test/fuzz_test/Fuzzer/test/LoadTest.cpp new file mode 100644 index 00000000..c1780d5c --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/LoadTest.cpp @@ -0,0 +1,22 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer: find interesting value of array index. +#include +#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; + size_t a = 0; + memcpy(&a, Data, 8); + Sink = array[a % (kArraySize + 1)]; + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/MemcmpTest.cpp b/test/fuzz_test/Fuzzer/test/MemcmpTest.cpp new file mode 100644 index 00000000..fdbf9468 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/MemcmpTest.cpp @@ -0,0 +1,31 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find a particular string. +#include +#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/test/fuzz_test/Fuzzer/test/NthRunCrashTest.cpp b/test/fuzz_test/Fuzzer/test/NthRunCrashTest.cpp new file mode 100644 index 00000000..b43e69e5 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/NthRunCrashTest.cpp @@ -0,0 +1,18 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Crash on the N-th execution. +#include +#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/test/fuzz_test/Fuzzer/test/NullDerefOnEmptyTest.cpp b/test/fuzz_test/Fuzzer/test/NullDerefOnEmptyTest.cpp new file mode 100644 index 00000000..15371092 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/NullDerefOnEmptyTest.cpp @@ -0,0 +1,19 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the empty string. +#include +#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/test/fuzz_test/Fuzzer/test/NullDerefTest.cpp b/test/fuzz_test/Fuzzer/test/NullDerefTest.cpp new file mode 100644 index 00000000..3f03d249 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/NullDerefTest.cpp @@ -0,0 +1,26 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the string "Hi!". +#include +#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/test/fuzz_test/Fuzzer/test/OneHugeAllocTest.cpp b/test/fuzz_test/Fuzzer/test/OneHugeAllocTest.cpp new file mode 100644 index 00000000..8d3d1d6d --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/OneHugeAllocTest.cpp @@ -0,0 +1,28 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Tests OOM handling when there is a single large allocation. +#include +#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/test/fuzz_test/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp b/test/fuzz_test/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp new file mode 100644 index 00000000..ea23a601 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp @@ -0,0 +1,27 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Tests OOM handling. +#include +#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 = 0xff000000U; + char *p = new char[kSize]; + SinkPtr = p; + delete [] p; + } + } + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp b/test/fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp new file mode 100644 index 00000000..078a39ee --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp @@ -0,0 +1,31 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Tests OOM handling. +#include +#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/test/fuzz_test/Fuzzer/test/RepeatedBytesTest.cpp b/test/fuzz_test/Fuzzer/test/RepeatedBytesTest.cpp new file mode 100644 index 00000000..2fa6c78c --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/RepeatedBytesTest.cpp @@ -0,0 +1,29 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find repeated bytes. +#include +#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/test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp b/test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp new file mode 100644 index 00000000..a327bbee --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp @@ -0,0 +1,22 @@ +// 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 Matches = 0; + for (size_t i = 0; i + 2 < Size; i += 3) { + const char *Pat = i % 2 ? "foo" : "bar"; + if (!memcmp(Data + i, Pat, 3)) + Matches++; + } + if (Matches > 20) { + fprintf(stderr, "BINGO!\n"); + exit(1); + } + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/ShrinkControlFlowTest.cpp b/test/fuzz_test/Fuzzer/test/ShrinkControlFlowTest.cpp new file mode 100644 index 00000000..0fd7c5e9 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/ShrinkControlFlowTest.cpp @@ -0,0 +1,28 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test that we can find the minimal item in the corpus (3 bytes: "FUZ"). +#include +#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/test/fuzz_test/Fuzzer/test/ShrinkValueProfileTest.cpp b/test/fuzz_test/Fuzzer/test/ShrinkValueProfileTest.cpp new file mode 100644 index 00000000..026b8ce2 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/ShrinkValueProfileTest.cpp @@ -0,0 +1,22 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test that we can find the minimal item in the corpus (3 bytes: "FUZ"). +#include +#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/test/fuzz_test/Fuzzer/test/SignedIntOverflowTest.cpp b/test/fuzz_test/Fuzzer/test/SignedIntOverflowTest.cpp new file mode 100644 index 00000000..7df32ad5 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SignedIntOverflowTest.cpp @@ -0,0 +1,28 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test for signed-integer-overflow. +#include +#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/test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp b/test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp new file mode 100644 index 00000000..0220c30f --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp @@ -0,0 +1,46 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find several narrow ranges. +#include +#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/test/fuzz_test/Fuzzer/test/SimpleDictionaryTest.cpp b/test/fuzz_test/Fuzzer/test/SimpleDictionaryTest.cpp new file mode 100644 index 00000000..cd7292bd --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SimpleDictionaryTest.cpp @@ -0,0 +1,29 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. +// The fuzzer must find a string based on dictionary words: +// "Elvis" +// "Presley" +#include +#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/test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp b/test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp new file mode 100644 index 00000000..00599de7 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp @@ -0,0 +1,40 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// This test computes a checksum of the data (all but the last 4 bytes), +// and then compares the last 4 bytes with the computed value. +// A fuzzer with cmp traces is expected to defeat this check. +#include +#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/test/fuzz_test/Fuzzer/test/SimpleTest.cpp b/test/fuzz_test/Fuzzer/test/SimpleTest.cpp new file mode 100644 index 00000000..e53ea160 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SimpleTest.cpp @@ -0,0 +1,27 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the string "Hi!". +#include +#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/test/fuzz_test/Fuzzer/test/SimpleThreadedTest.cpp b/test/fuzz_test/Fuzzer/test/SimpleThreadedTest.cpp new file mode 100644 index 00000000..5f02d3f8 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SimpleThreadedTest.cpp @@ -0,0 +1,25 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Threaded test for a fuzzer. The fuzzer should find "H" +#include +#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/test/fuzz_test/Fuzzer/test/SingleMemcmpTest.cpp b/test/fuzz_test/Fuzzer/test/SingleMemcmpTest.cpp new file mode 100644 index 00000000..c73f68a7 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SingleMemcmpTest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find a particular string. +#include +#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/test/fuzz_test/Fuzzer/test/SingleStrcmpTest.cpp b/test/fuzz_test/Fuzzer/test/SingleStrcmpTest.cpp new file mode 100644 index 00000000..73470b52 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SingleStrcmpTest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find a particular string. +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + char *S = (char*)Data; + if (Size >= 7 && !strcmp(S, "qwerty")) { + fprintf(stderr, "BINGO\n"); + exit(1); + } + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/SingleStrncmpTest.cpp b/test/fuzz_test/Fuzzer/test/SingleStrncmpTest.cpp new file mode 100644 index 00000000..dbcc464b --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SingleStrncmpTest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find a particular string. +#include +#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/test/fuzz_test/Fuzzer/test/SpamyTest.cpp b/test/fuzz_test/Fuzzer/test/SpamyTest.cpp new file mode 100644 index 00000000..d294d4dc --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SpamyTest.cpp @@ -0,0 +1,21 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// The test spams to stderr and stdout. +#include +#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/test/fuzz_test/Fuzzer/test/StrcmpTest.cpp b/test/fuzz_test/Fuzzer/test/StrcmpTest.cpp new file mode 100644 index 00000000..cd91dda7 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/StrcmpTest.cpp @@ -0,0 +1,32 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Break through a series of strcmp. +#include +#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/test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp b/test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp new file mode 100644 index 00000000..f70b003a --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp @@ -0,0 +1,21 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test that libFuzzer itself does not read out of bounds. +#include +#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/test/fuzz_test/Fuzzer/test/StrncmpTest.cpp b/test/fuzz_test/Fuzzer/test/StrncmpTest.cpp new file mode 100644 index 00000000..5ffd011d --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/StrncmpTest.cpp @@ -0,0 +1,28 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find a particular string. +#include +#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/test/fuzz_test/Fuzzer/test/StrstrTest.cpp b/test/fuzz_test/Fuzzer/test/StrstrTest.cpp new file mode 100644 index 00000000..f021e75e --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/StrstrTest.cpp @@ -0,0 +1,28 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test strstr and strcasestr hooks. +#include +#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/test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp b/test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp new file mode 100644 index 00000000..f79db4cc --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp @@ -0,0 +1,34 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// The fuzzer must find several constants with swapped bytes. +#include +#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/test/fuzz_test/Fuzzer/test/Switch2Test.cpp b/test/fuzz_test/Fuzzer/test/Switch2Test.cpp new file mode 100644 index 00000000..3c6a3004 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/Switch2Test.cpp @@ -0,0 +1,35 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the interesting switch value. +#include +#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/test/fuzz_test/Fuzzer/test/SwitchTest.cpp b/test/fuzz_test/Fuzzer/test/SwitchTest.cpp new file mode 100644 index 00000000..74e86c06 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SwitchTest.cpp @@ -0,0 +1,58 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the interesting switch value. +#include +#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/test/fuzz_test/Fuzzer/test/ThreadedLeakTest.cpp b/test/fuzz_test/Fuzzer/test/ThreadedLeakTest.cpp new file mode 100644 index 00000000..75110711 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/ThreadedLeakTest.cpp @@ -0,0 +1,18 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// The fuzzer should find a leak in a non-main thread. +#include +#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/test/fuzz_test/Fuzzer/test/ThreadedTest.cpp b/test/fuzz_test/Fuzzer/test/ThreadedTest.cpp new file mode 100644 index 00000000..09137a9a --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/ThreadedTest.cpp @@ -0,0 +1,26 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Threaded test for a fuzzer. The fuzzer should not crash. +#include +#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/test/fuzz_test/Fuzzer/test/TimeoutEmptyTest.cpp b/test/fuzz_test/Fuzzer/test/TimeoutEmptyTest.cpp new file mode 100644 index 00000000..8066f480 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/TimeoutEmptyTest.cpp @@ -0,0 +1,14 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the empty string. +#include +#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/test/fuzz_test/Fuzzer/test/TimeoutTest.cpp b/test/fuzz_test/Fuzzer/test/TimeoutTest.cpp new file mode 100644 index 00000000..f8107012 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/TimeoutTest.cpp @@ -0,0 +1,26 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the string "Hi!". +#include +#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/test/fuzz_test/Fuzzer/test/TraceMallocTest.cpp b/test/fuzz_test/Fuzzer/test/TraceMallocTest.cpp new file mode 100644 index 00000000..43e6950e --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/TraceMallocTest.cpp @@ -0,0 +1,27 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Tests -trace_malloc +#include +#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/test/fuzz_test/Fuzzer/test/UninstrumentedTest.cpp b/test/fuzz_test/Fuzzer/test/UninstrumentedTest.cpp new file mode 100644 index 00000000..ffe952c7 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/UninstrumentedTest.cpp @@ -0,0 +1,11 @@ +// 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/test/fuzz_test/Fuzzer/test/afl-driver-extra-stats.test b/test/fuzz_test/Fuzzer/test/afl-driver-extra-stats.test new file mode 100644 index 00000000..81e384e7 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/afl-driver-extra-stats.test @@ -0,0 +1,28 @@ +; 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/test/fuzz_test/Fuzzer/test/afl-driver-stderr.test b/test/fuzz_test/Fuzzer/test/afl-driver-stderr.test new file mode 100644 index 00000000..c0f9c839 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/afl-driver-stderr.test @@ -0,0 +1,10 @@ +; 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/test/fuzz_test/Fuzzer/test/caller-callee.test b/test/fuzz_test/Fuzzer/test/caller-callee.test new file mode 100644 index 00000000..76a951c5 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/caller-callee.test @@ -0,0 +1,2 @@ +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/test/fuzz_test/Fuzzer/test/coverage.test b/test/fuzz_test/Fuzzer/test/coverage.test new file mode 100644 index 00000000..fa11be50 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/coverage.test @@ -0,0 +1,19 @@ +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/test/fuzz_test/Fuzzer/test/dict1.txt b/test/fuzz_test/Fuzzer/test/dict1.txt new file mode 100644 index 00000000..520d0cc7 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/dict1.txt @@ -0,0 +1,4 @@ +# Dictionary for SimpleDictionaryTest + +a="Elvis" +b="Presley" diff --git a/test/fuzz_test/Fuzzer/test/dump_coverage.test b/test/fuzz_test/Fuzzer/test/dump_coverage.test new file mode 100644 index 00000000..9bd98daa --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/dump_coverage.test @@ -0,0 +1,16 @@ +RUN: DIR=%t_workdir +RUN: BUILD_DIR=$(pwd) +RUN: rm -rf $DIR && mkdir -p $DIR && cd $DIR +RUN: not $BUILD_DIR/LLVMFuzzer-NullDerefTest -dump_coverage=1 2>&1 | FileCheck %s +RUN: $BUILD_DIR/LLVMFuzzer-DSOTest -dump_coverage=1 -runs=0 2>&1 | FileCheck %s --check-prefix=DSO +RUN: not $BUILD_DIR/LLVMFuzzer-NullDerefTest -dump_coverage=0 2>&1 | FileCheck %s --check-prefix=NOCOV +RUN: rm -rf $DIR + + +CHECK: SanitizerCoverage: ./LLVMFuzzer-NullDerefTest.{{.*}}.sancov {{.*}} PCs written + +DSO: SanitizerCoverage: ./LLVMFuzzer-DSOTest.{{.*}}.sancov {{.*}} PCs written +DSO-DAG: SanitizerCoverage: ./libLLVMFuzzer-DSO1.{{.*}}.sancov {{.*}} PCs written +DSO-DAG: SanitizerCoverage: ./libLLVMFuzzer-DSO2.{{.*}}.sancov {{.*}} PCs written + +NOCOV-NOT: SanitizerCoverage: {{.*}} PCs written diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-customcrossover.test b/test/fuzz_test/Fuzzer/test/fuzzer-customcrossover.test new file mode 100644 index 00000000..28d39ce3 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-customcrossover.test @@ -0,0 +1,10 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer-custommutator.test b/test/fuzz_test/Fuzzer/test/fuzzer-custommutator.test new file mode 100644 index 00000000..fcd740bf --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-custommutator.test @@ -0,0 +1,4 @@ +RUN: not LLVMFuzzer-CustomMutatorTest 2>&1 | FileCheck %s --check-prefix=LLVMFuzzerCustomMutator +LLVMFuzzerCustomMutator: In LLVMFuzzerCustomMutator +LLVMFuzzerCustomMutator: BINGO + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-dict.test b/test/fuzz_test/Fuzzer/test/fuzzer-dict.test new file mode 100644 index 00000000..dec002f6 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-dict.test @@ -0,0 +1,6 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer-dirs.test b/test/fuzz_test/Fuzzer/test/fuzzer-dirs.test new file mode 100644 index 00000000..63afe8df --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-dirs.test @@ -0,0 +1,15 @@ +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 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 94 +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/test/fuzz_test/Fuzzer/test/fuzzer-fdmask.test b/test/fuzz_test/Fuzzer/test/fuzzer-fdmask.test new file mode 100644 index 00000000..abbc4bd6 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-fdmask.test @@ -0,0 +1,30 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer-finalstats.test b/test/fuzz_test/Fuzzer/test/fuzzer-finalstats.test new file mode 100644 index 00000000..1cbcd10f --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-finalstats.test @@ -0,0 +1,11 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer-flags.test b/test/fuzz_test/Fuzzer/test/fuzzer-flags.test new file mode 100644 index 00000000..76ea2770 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-flags.test @@ -0,0 +1,10 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer-jobs.test b/test/fuzz_test/Fuzzer/test/fuzzer-jobs.test new file mode 100644 index 00000000..5bf8cfad --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-jobs.test @@ -0,0 +1,29 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer-leak.test b/test/fuzz_test/Fuzzer/test/fuzzer-leak.test new file mode 100644 index 00000000..9cf5c743 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-leak.test @@ -0,0 +1,35 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer-oom-with-profile.test b/test/fuzz_test/Fuzzer/test/fuzzer-oom-with-profile.test new file mode 100644 index 00000000..2b2b0b9d --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-oom-with-profile.test @@ -0,0 +1,6 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer-oom.test b/test/fuzz_test/Fuzzer/test/fuzzer-oom.test new file mode 100644 index 00000000..8caf649e --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-oom.test @@ -0,0 +1,11 @@ +RUN: not LLVMFuzzer-OutOfMemoryTest -rss_limit_mb=300 2>&1 | FileCheck %s +CHECK: ERROR: libFuzzer: out-of-memory (used: {{.*}}; limit: 300Mb) +CHECK: Test unit written to ./oom- +SUMMARY: libFuzzer: out-of-memory + +RUN: not LLVMFuzzer-OutOfMemorySingleLargeMallocTest 2>&1 | FileCheck %s --check-prefix=SINGLE_LARGE_MALLOC +SINGLE_LARGE_MALLOC: libFuzzer: out-of-memory (malloc(42{{.*}})) +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/test/fuzz_test/Fuzzer/test/fuzzer-printcovpcs.test b/test/fuzz_test/Fuzzer/test/fuzzer-printcovpcs.test new file mode 100644 index 00000000..e4c6f0ed --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-printcovpcs.test @@ -0,0 +1,8 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer-runs.test b/test/fuzz_test/Fuzzer/test/fuzzer-runs.test new file mode 100644 index 00000000..056c4478 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-runs.test @@ -0,0 +1,8 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer-seed.test b/test/fuzz_test/Fuzzer/test/fuzzer-seed.test new file mode 100644 index 00000000..f1bdf9e4 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-seed.test @@ -0,0 +1,3 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer-segv.test b/test/fuzz_test/Fuzzer/test/fuzzer-segv.test new file mode 100644 index 00000000..330f03bc --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-segv.test @@ -0,0 +1,5 @@ +RUN: 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/test/fuzz_test/Fuzzer/test/fuzzer-singleinputs.test b/test/fuzz_test/Fuzzer/test/fuzzer-singleinputs.test new file mode 100644 index 00000000..ca8403bf --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-singleinputs.test @@ -0,0 +1,16 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer-threaded.test b/test/fuzz_test/Fuzzer/test/fuzzer-threaded.test new file mode 100644 index 00000000..c58a3345 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-threaded.test @@ -0,0 +1,7 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer-timeout.test b/test/fuzz_test/Fuzzer/test/fuzzer-timeout.test new file mode 100644 index 00000000..beb08671 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-timeout.test @@ -0,0 +1,19 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer-traces-hooks.test b/test/fuzz_test/Fuzzer/test/fuzzer-traces-hooks.test new file mode 100644 index 00000000..71fe6f2d --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-traces-hooks.test @@ -0,0 +1,25 @@ +// FIXME: Support sanitizer hooks for memcmp and strcmp need +// to be implemented in the sanitizer runtime for platforms other +// than linux +REQUIRES: linux +CHECK: BINGO +Done1000000: Done 1000000 runs in + +RUN: not LLVMFuzzer-MemcmpTest -seed=4294967295 -runs=100000 2>&1 | FileCheck %s +RUN: LLVMFuzzer-MemcmpTest -use_memcmp=0 -seed=4294967295 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 + +RUN: not LLVMFuzzer-StrncmpTest -seed=2 -runs=100000 2>&1 | FileCheck %s +RUN: LLVMFuzzer-StrncmpTest -use_memcmp=0 -seed=3 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 + +RUN: not LLVMFuzzer-StrcmpTest -seed=4 -runs=200000 2>&1 | FileCheck %s +RUN: LLVMFuzzer-StrcmpTest -use_memcmp=0 -seed=5 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 + +RUN: not LLVMFuzzer-StrstrTest -seed=6 -runs=200000 2>&1 | FileCheck %s +RUN: LLVMFuzzer-StrstrTest -use_memmem=0 -seed=7 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 + +RUN: LLVMFuzzer-RepeatedMemcmp -seed=10 -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/test/fuzz_test/Fuzzer/test/fuzzer-ubsan.test b/test/fuzz_test/Fuzzer/test/fuzzer-ubsan.test new file mode 100644 index 00000000..0e8ad6c9 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-ubsan.test @@ -0,0 +1,4 @@ +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/test/fuzz_test/Fuzzer/test/fuzzer.test b/test/fuzz_test/Fuzzer/test/fuzzer.test new file mode 100644 index 00000000..2f91c219 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer.test @@ -0,0 +1,57 @@ +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 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-UninstrumentedTest-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: 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 diff --git a/test/fuzz_test/Fuzzer/test/hi.txt b/test/fuzz_test/Fuzzer/test/hi.txt new file mode 100644 index 00000000..2f9031f0 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/hi.txt @@ -0,0 +1 @@ +Hi! \ No newline at end of file diff --git a/test/fuzz_test/Fuzzer/test/lit.cfg b/test/fuzz_test/Fuzzer/test/lit.cfg new file mode 100644 index 00000000..745af0c3 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/lit.cfg @@ -0,0 +1,29 @@ +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__) + +# 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('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/test/fuzz_test/Fuzzer/test/lit.site.cfg.in b/test/fuzz_test/Fuzzer/test/lit.site.cfg.in new file mode 100644 index 00000000..03e86c48 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/lit.site.cfg.in @@ -0,0 +1,4 @@ +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 +lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") diff --git a/test/fuzz_test/Fuzzer/test/merge.test b/test/fuzz_test/Fuzzer/test/merge.test new file mode 100644 index 00000000..1f1810eb --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/merge.test @@ -0,0 +1,46 @@ +CHECK: BINGO + +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 + +# T1 has 3 elements, T2 is empty. +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: 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: 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) diff --git a/test/fuzz_test/Fuzzer/test/minimize_crash.test b/test/fuzz_test/Fuzzer/test/minimize_crash.test new file mode 100644 index 00000000..7e540659 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/minimize_crash.test @@ -0,0 +1,6 @@ +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 diff --git a/test/fuzz_test/Fuzzer/test/no-coverage/CMakeLists.txt b/test/fuzz_test/Fuzzer/test/no-coverage/CMakeLists.txt new file mode 100644 index 00000000..d2f6f438 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/no-coverage/CMakeLists.txt @@ -0,0 +1,29 @@ +# 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 + UninstrumentedTest + ) + +foreach(Test ${NoCoverageTests}) + add_libfuzzer_test(${Test}-NoCoverage SOURCES ../${Test}.cpp) +endforeach() + + +############################################################################### +# AFL Driver test +############################################################################### + +add_executable(AFLDriverTest + ../AFLDriverTest.cpp ../../afl/afl_driver.cpp) + +set_target_properties(AFLDriverTest + PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}/lib/Fuzzer/test" + ) + +# Propagate value into parent directory +set(TestBinaries ${TestBinaries} AFLDriverTest PARENT_SCOPE) diff --git a/test/fuzz_test/Fuzzer/test/repeated-bytes.test b/test/fuzz_test/Fuzzer/test/repeated-bytes.test new file mode 100644 index 00000000..71394087 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/repeated-bytes.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: LLVMFuzzer-RepeatedBytesTest -seed=1 -runs=1000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/shrink.test b/test/fuzz_test/Fuzzer/test/shrink.test new file mode 100644 index 00000000..edb86cb1 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/shrink.test @@ -0,0 +1,7 @@ +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/test/fuzz_test/Fuzzer/test/simple-cmp.test b/test/fuzz_test/Fuzzer/test/simple-cmp.test new file mode 100644 index 00000000..145a0366 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/simple-cmp.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SimpleCmpTest -seed=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/standalone.test b/test/fuzz_test/Fuzzer/test/standalone.test new file mode 100644 index 00000000..3097b3ff --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/standalone.test @@ -0,0 +1,4 @@ +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/test/fuzz_test/Fuzzer/test/swap-cmp.test b/test/fuzz_test/Fuzzer/test/swap-cmp.test new file mode 100644 index 00000000..908b7986 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/swap-cmp.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SwapCmpTest -seed=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/trace-malloc.test b/test/fuzz_test/Fuzzer/test/trace-malloc.test new file mode 100644 index 00000000..c9514790 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/trace-malloc.test @@ -0,0 +1,10 @@ +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) + +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/test/fuzz_test/Fuzzer/test/ubsan/CMakeLists.txt b/test/fuzz_test/Fuzzer/test/ubsan/CMakeLists.txt new file mode 100644 index 00000000..7a9eacdb --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/ubsan/CMakeLists.txt @@ -0,0 +1,15 @@ +# 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() + +# Propagate value into parent directory +set(TestBinaries ${TestBinaries} PARENT_SCOPE) diff --git a/test/fuzz_test/Fuzzer/test/ulimit.test b/test/fuzz_test/Fuzzer/test/ulimit.test new file mode 100644 index 00000000..a60636c3 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/ulimit.test @@ -0,0 +1,2 @@ +RUN: ulimit -s 1000 +RUN: LLVMFuzzer-SimpleTest diff --git a/test/fuzz_test/Fuzzer/test/uninstrumented/CMakeLists.txt b/test/fuzz_test/Fuzzer/test/uninstrumented/CMakeLists.txt new file mode 100644 index 00000000..29b66e6e --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/uninstrumented/CMakeLists.txt @@ -0,0 +1,16 @@ +# 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() + +# Propagate value into parent directory +set(TestBinaries ${TestBinaries} PARENT_SCOPE) diff --git a/test/fuzz_test/Fuzzer/test/unit/lit.cfg b/test/fuzz_test/Fuzzer/test/unit/lit.cfg new file mode 100644 index 00000000..0cc31939 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/unit/lit.cfg @@ -0,0 +1,7 @@ +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/test/fuzz_test/Fuzzer/test/unit/lit.site.cfg.in b/test/fuzz_test/Fuzzer/test/unit/lit.site.cfg.in new file mode 100644 index 00000000..114daf47 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/unit/lit.site.cfg.in @@ -0,0 +1,2 @@ +config.test_exec_root = "@CMAKE_CURRENT_BINARY_DIR@" +lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/unit/lit.cfg") diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp.test b/test/fuzz_test/Fuzzer/test/value-profile-cmp.test new file mode 100644 index 00000000..48edba4f --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-cmp.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SimpleCmpTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp2.test b/test/fuzz_test/Fuzzer/test/value-profile-cmp2.test new file mode 100644 index 00000000..43d62400 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-cmp2.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SimpleHashTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp3.test b/test/fuzz_test/Fuzzer/test/value-profile-cmp3.test new file mode 100644 index 00000000..8a962763 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-cmp3.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-AbsNegAndConstantTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp4.test b/test/fuzz_test/Fuzzer/test/value-profile-cmp4.test new file mode 100644 index 00000000..1e7131e5 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-cmp4.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-AbsNegAndConstant64Test -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/value-profile-div.test b/test/fuzz_test/Fuzzer/test/value-profile-div.test new file mode 100644 index 00000000..ba45e412 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-div.test @@ -0,0 +1,3 @@ +CHECK: AddressSanitizer: FPE +RUN: not LLVMFuzzer-DivTest -seed=1 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s + diff --git a/test/fuzz_test/Fuzzer/test/value-profile-load.test b/test/fuzz_test/Fuzzer/test/value-profile-load.test new file mode 100644 index 00000000..14d3109a --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-load.test @@ -0,0 +1,3 @@ +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/test/fuzz_test/Fuzzer/test/value-profile-mem.test b/test/fuzz_test/Fuzzer/test/value-profile-mem.test new file mode 100644 index 00000000..09d737db --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-mem.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SingleMemcmpTest -seed=1 -use_cmp=0 -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/value-profile-set.test b/test/fuzz_test/Fuzzer/test/value-profile-set.test new file mode 100644 index 00000000..9d06c365 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-set.test @@ -0,0 +1,3 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-FourIndependentBranchesTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s + diff --git a/test/fuzz_test/Fuzzer/test/value-profile-strcmp.test b/test/fuzz_test/Fuzzer/test/value-profile-strcmp.test new file mode 100644 index 00000000..1e7ef9b4 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-strcmp.test @@ -0,0 +1,2 @@ +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/test/fuzz_test/Fuzzer/test/value-profile-strncmp.test b/test/fuzz_test/Fuzzer/test/value-profile-strncmp.test new file mode 100644 index 00000000..65097318 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-strncmp.test @@ -0,0 +1,2 @@ +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/test/fuzz_test/Fuzzer/test/value-profile-switch.test b/test/fuzz_test/Fuzzer/test/value-profile-switch.test new file mode 100644 index 00000000..1947f568 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-switch.test @@ -0,0 +1,3 @@ +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 From 6e8791912ff4b3480acaa350c628404f04aefa47 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 09:09:40 +0100 Subject: [PATCH 03/35] :construction: added fuzzer from OSS-Fuzz --- test/fuzz_test/parse_fuzzer.cc | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 test/fuzz_test/parse_fuzzer.cc diff --git a/test/fuzz_test/parse_fuzzer.cc b/test/fuzz_test/parse_fuzzer.cc new file mode 100644 index 00000000..bb8b3d37 --- /dev/null +++ b/test/fuzz_test/parse_fuzzer.cc @@ -0,0 +1,36 @@ +// Copyright 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +using json = nlohmann::json; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + try { + std::stringstream s; + s << json::parse(data, data + size); + try { + auto j = json::parse(s.str()); + std::stringstream s2; + s2 << j; + assert(s.str() == s2.str()); + assert(j == json::parse(s.str())); + } catch (const std::invalid_argument&) { + assert(0); + } + } catch (const std::invalid_argument&) { } + return 0; +} From ad241a2260ed33fa0b0aa40ec65e5a68650eb5f7 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 09:35:53 +0100 Subject: [PATCH 04/35] :hammer: moved third-party code into separate folder --- .gitignore | 2 +- test/CMakeLists.txt | 4 +-- test/Makefile | 8 ++--- .../fuzzer-parse_json.cpp} | 34 +++++++++++-------- .../Fuzzer/CMakeLists.txt | 0 .../Fuzzer/FuzzerCorpus.h | 0 .../Fuzzer/FuzzerCrossOver.cpp | 0 .../Fuzzer/FuzzerDefs.h | 0 .../Fuzzer/FuzzerDictionary.h | 0 .../Fuzzer/FuzzerDriver.cpp | 0 .../Fuzzer/FuzzerExtFunctions.def | 0 .../Fuzzer/FuzzerExtFunctions.h | 0 .../Fuzzer/FuzzerExtFunctionsDlsym.cpp | 0 .../Fuzzer/FuzzerExtFunctionsWeak.cpp | 0 .../Fuzzer/FuzzerExtFunctionsWeakAlias.cpp | 0 .../Fuzzer/FuzzerFlags.def | 0 .../Fuzzer/FuzzerIO.cpp | 0 .../Fuzzer/FuzzerIO.h | 0 .../Fuzzer/FuzzerIOPosix.cpp | 0 .../Fuzzer/FuzzerIOWindows.cpp | 0 .../Fuzzer/FuzzerInterface.h | 0 .../Fuzzer/FuzzerInternal.h | 0 .../Fuzzer/FuzzerLoop.cpp | 0 .../Fuzzer/FuzzerMain.cpp | 0 .../Fuzzer/FuzzerMerge.cpp | 0 .../Fuzzer/FuzzerMerge.h | 0 .../Fuzzer/FuzzerMutate.cpp | 0 .../Fuzzer/FuzzerMutate.h | 0 .../Fuzzer/FuzzerOptions.h | 0 .../Fuzzer/FuzzerRandom.h | 0 .../Fuzzer/FuzzerSHA1.cpp | 0 .../Fuzzer/FuzzerSHA1.h | 0 .../Fuzzer/FuzzerTracePC.cpp | 0 .../Fuzzer/FuzzerTracePC.h | 0 .../Fuzzer/FuzzerTraceState.cpp | 0 .../Fuzzer/FuzzerUtil.cpp | 0 .../Fuzzer/FuzzerUtil.h | 0 .../Fuzzer/FuzzerUtilDarwin.cpp | 0 .../Fuzzer/FuzzerUtilLinux.cpp | 0 .../Fuzzer/FuzzerUtilPosix.cpp | 0 .../Fuzzer/FuzzerUtilWindows.cpp | 0 .../Fuzzer/FuzzerValueBitMap.h | 0 .../Fuzzer/README.txt | 0 .../Fuzzer/afl/afl_driver.cpp | 0 .../{fuzz_test => thirdparty}/Fuzzer/build.sh | 0 .../{fuzz_test => thirdparty}/Fuzzer/cxx.dict | 0 .../standalone/StandaloneFuzzTargetMain.c | 0 .../Fuzzer/test/AFLDriverTest.cpp | 0 .../Fuzzer/test/AbsNegAndConstant64Test.cpp | 0 .../Fuzzer/test/AbsNegAndConstantTest.cpp | 0 .../Fuzzer/test/AccumulateAllocationsTest.cpp | 0 .../Fuzzer/test/BufferOverflowOnInput.cpp | 0 .../Fuzzer/test/CMakeLists.txt | 0 .../Fuzzer/test/CallerCalleeTest.cpp | 0 .../Fuzzer/test/CounterTest.cpp | 0 .../Fuzzer/test/CustomCrossOverTest.cpp | 0 .../Fuzzer/test/CustomMutatorTest.cpp | 0 .../Fuzzer/test/DSO1.cpp | 0 .../Fuzzer/test/DSO2.cpp | 0 .../Fuzzer/test/DSOTestExtra.cpp | 0 .../Fuzzer/test/DSOTestMain.cpp | 0 .../Fuzzer/test/DivTest.cpp | 0 .../Fuzzer/test/EmptyTest.cpp | 0 .../test/FourIndependentBranchesTest.cpp | 0 .../Fuzzer/test/FullCoverageSetTest.cpp | 0 .../Fuzzer/test/FuzzerUnittest.cpp | 0 .../Fuzzer/test/InitializeTest.cpp | 0 .../Fuzzer/test/LeakTest.cpp | 0 .../Fuzzer/test/LeakTimeoutTest.cpp | 0 .../Fuzzer/test/LoadTest.cpp | 0 .../Fuzzer/test/MemcmpTest.cpp | 0 .../Fuzzer/test/NthRunCrashTest.cpp | 0 .../Fuzzer/test/NullDerefOnEmptyTest.cpp | 0 .../Fuzzer/test/NullDerefTest.cpp | 0 .../Fuzzer/test/OneHugeAllocTest.cpp | 0 .../test/OutOfMemorySingleLargeMallocTest.cpp | 0 .../Fuzzer/test/OutOfMemoryTest.cpp | 0 .../Fuzzer/test/RepeatedBytesTest.cpp | 0 .../Fuzzer/test/RepeatedMemcmp.cpp | 0 .../Fuzzer/test/ShrinkControlFlowTest.cpp | 0 .../Fuzzer/test/ShrinkValueProfileTest.cpp | 0 .../Fuzzer/test/SignedIntOverflowTest.cpp | 0 .../Fuzzer/test/SimpleCmpTest.cpp | 0 .../Fuzzer/test/SimpleDictionaryTest.cpp | 0 .../Fuzzer/test/SimpleHashTest.cpp | 0 .../Fuzzer/test/SimpleTest.cpp | 0 .../Fuzzer/test/SimpleThreadedTest.cpp | 0 .../Fuzzer/test/SingleMemcmpTest.cpp | 0 .../Fuzzer/test/SingleStrcmpTest.cpp | 0 .../Fuzzer/test/SingleStrncmpTest.cpp | 0 .../Fuzzer/test/SpamyTest.cpp | 0 .../Fuzzer/test/StrcmpTest.cpp | 0 .../Fuzzer/test/StrncmpOOBTest.cpp | 0 .../Fuzzer/test/StrncmpTest.cpp | 0 .../Fuzzer/test/StrstrTest.cpp | 0 .../Fuzzer/test/SwapCmpTest.cpp | 0 .../Fuzzer/test/Switch2Test.cpp | 0 .../Fuzzer/test/SwitchTest.cpp | 0 .../Fuzzer/test/ThreadedLeakTest.cpp | 0 .../Fuzzer/test/ThreadedTest.cpp | 0 .../Fuzzer/test/TimeoutEmptyTest.cpp | 0 .../Fuzzer/test/TimeoutTest.cpp | 0 .../Fuzzer/test/TraceMallocTest.cpp | 0 .../Fuzzer/test/UninstrumentedTest.cpp | 0 .../Fuzzer/test/afl-driver-extra-stats.test | 0 .../Fuzzer/test/afl-driver-stderr.test | 0 .../Fuzzer/test/caller-callee.test | 0 .../Fuzzer/test/coverage.test | 0 .../Fuzzer/test/dict1.txt | 0 .../Fuzzer/test/dump_coverage.test | 0 .../Fuzzer/test/fuzzer-customcrossover.test | 0 .../Fuzzer/test/fuzzer-custommutator.test | 0 .../Fuzzer/test/fuzzer-dict.test | 0 .../Fuzzer/test/fuzzer-dirs.test | 0 .../Fuzzer/test/fuzzer-fdmask.test | 0 .../Fuzzer/test/fuzzer-finalstats.test | 0 .../Fuzzer/test/fuzzer-flags.test | 0 .../Fuzzer/test/fuzzer-jobs.test | 0 .../Fuzzer/test/fuzzer-leak.test | 0 .../Fuzzer/test/fuzzer-oom-with-profile.test | 0 .../Fuzzer/test/fuzzer-oom.test | 0 .../Fuzzer/test/fuzzer-printcovpcs.test | 0 .../Fuzzer/test/fuzzer-runs.test | 0 .../Fuzzer/test/fuzzer-seed.test | 0 .../Fuzzer/test/fuzzer-segv.test | 0 .../Fuzzer/test/fuzzer-singleinputs.test | 0 .../Fuzzer/test/fuzzer-threaded.test | 0 .../Fuzzer/test/fuzzer-timeout.test | 0 .../Fuzzer/test/fuzzer-traces-hooks.test | 0 .../Fuzzer/test/fuzzer-ubsan.test | 0 .../Fuzzer/test/fuzzer.test | 0 .../Fuzzer/test/hi.txt | 0 .../Fuzzer/test/lit.cfg | 0 .../Fuzzer/test/lit.site.cfg.in | 0 .../Fuzzer/test/merge.test | 0 .../Fuzzer/test/minimize_crash.test | 0 .../Fuzzer/test/no-coverage/CMakeLists.txt | 0 .../Fuzzer/test/repeated-bytes.test | 0 .../Fuzzer/test/shrink.test | 0 .../Fuzzer/test/simple-cmp.test | 0 .../Fuzzer/test/standalone.test | 0 .../Fuzzer/test/swap-cmp.test | 0 .../Fuzzer/test/trace-malloc.test | 0 .../Fuzzer/test/ubsan/CMakeLists.txt | 0 .../Fuzzer/test/ulimit.test | 0 .../Fuzzer/test/uninstrumented/CMakeLists.txt | 0 .../Fuzzer/test/unit/lit.cfg | 0 .../Fuzzer/test/unit/lit.site.cfg.in | 0 .../Fuzzer/test/value-profile-cmp.test | 0 .../Fuzzer/test/value-profile-cmp2.test | 0 .../Fuzzer/test/value-profile-cmp3.test | 0 .../Fuzzer/test/value-profile-cmp4.test | 0 .../Fuzzer/test/value-profile-div.test | 0 .../Fuzzer/test/value-profile-load.test | 0 .../Fuzzer/test/value-profile-mem.test | 0 .../Fuzzer/test/value-profile-set.test | 0 .../Fuzzer/test/value-profile-strcmp.test | 0 .../Fuzzer/test/value-profile-strncmp.test | 0 .../Fuzzer/test/value-profile-switch.test | 0 test/{src => thirdparty/catch}/catch.hpp | 0 160 files changed, 27 insertions(+), 21 deletions(-) rename test/{fuzz_test/parse_fuzzer.cc => src/fuzzer-parse_json.cpp} (61%) rename test/{fuzz_test => thirdparty}/Fuzzer/CMakeLists.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerCorpus.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerCrossOver.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerDefs.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerDictionary.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerDriver.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerExtFunctions.def (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerExtFunctions.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerExtFunctionsDlsym.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerExtFunctionsWeak.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerFlags.def (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerIO.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerIO.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerIOPosix.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerIOWindows.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerInterface.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerInternal.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerLoop.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerMain.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerMerge.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerMerge.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerMutate.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerMutate.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerOptions.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerRandom.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerSHA1.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerSHA1.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerTracePC.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerTracePC.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerTraceState.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerUtil.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerUtil.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerUtilDarwin.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerUtilLinux.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerUtilPosix.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerUtilWindows.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerValueBitMap.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/README.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/afl/afl_driver.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/build.sh (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/cxx.dict (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/standalone/StandaloneFuzzTargetMain.c (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/AFLDriverTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/AbsNegAndConstant64Test.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/AbsNegAndConstantTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/AccumulateAllocationsTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/BufferOverflowOnInput.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/CMakeLists.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/CallerCalleeTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/CounterTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/CustomCrossOverTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/CustomMutatorTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/DSO1.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/DSO2.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/DSOTestExtra.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/DSOTestMain.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/DivTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/EmptyTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/FourIndependentBranchesTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/FullCoverageSetTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/FuzzerUnittest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/InitializeTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/LeakTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/LeakTimeoutTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/LoadTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/MemcmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/NthRunCrashTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/NullDerefOnEmptyTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/NullDerefTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/OneHugeAllocTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/OutOfMemoryTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/RepeatedBytesTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/RepeatedMemcmp.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/ShrinkControlFlowTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/ShrinkValueProfileTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SignedIntOverflowTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SimpleCmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SimpleDictionaryTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SimpleHashTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SimpleTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SimpleThreadedTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SingleMemcmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SingleStrcmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SingleStrncmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SpamyTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/StrcmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/StrncmpOOBTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/StrncmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/StrstrTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SwapCmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/Switch2Test.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SwitchTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/ThreadedLeakTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/ThreadedTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/TimeoutEmptyTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/TimeoutTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/TraceMallocTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/UninstrumentedTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/afl-driver-extra-stats.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/afl-driver-stderr.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/caller-callee.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/coverage.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/dict1.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/dump_coverage.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-customcrossover.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-custommutator.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-dict.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-dirs.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-fdmask.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-finalstats.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-flags.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-jobs.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-leak.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-oom-with-profile.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-oom.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-printcovpcs.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-runs.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-seed.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-segv.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-singleinputs.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-threaded.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-timeout.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-traces-hooks.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-ubsan.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/hi.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/lit.cfg (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/lit.site.cfg.in (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/merge.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/minimize_crash.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/no-coverage/CMakeLists.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/repeated-bytes.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/shrink.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/simple-cmp.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/standalone.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/swap-cmp.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/trace-malloc.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/ubsan/CMakeLists.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/ulimit.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/uninstrumented/CMakeLists.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/unit/lit.cfg (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/unit/lit.site.cfg.in (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-cmp.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-cmp2.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-cmp3.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-cmp4.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-div.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-load.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-mem.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-set.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-strcmp.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-strncmp.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-switch.test (100%) rename test/{src => thirdparty/catch}/catch.hpp (100%) diff --git a/.gitignore b/.gitignore index 099cad5c..104befbe 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,4 @@ cmake-build-debug test/test-* -test/fuzz_test/Fuzzer/.svn +.svn diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f57ddf71..47785aee 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,7 +1,7 @@ # The unit test executable. set(JSON_UNITTEST_TARGET_NAME "json_unit") add_executable(${JSON_UNITTEST_TARGET_NAME} - "src/catch.hpp" + "thirdparty/catch/catch.hpp" "src/unit.cpp" "src/unit-algorithms.cpp" "src/unit-allocator.cpp" @@ -44,7 +44,7 @@ set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES COMPILE_OPTIONS "$<$:/EHsc;$<$:/Od>>" ) -target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src") +target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src" "thirdparty/catch") target_link_libraries(${JSON_UNITTEST_TARGET_NAME} ${JSON_TARGET_NAME}) add_test(NAME "${JSON_UNITTEST_TARGET_NAME}_default" diff --git a/test/Makefile b/test/Makefile index 488cec89..c1fb33f4 100644 --- a/test/Makefile +++ b/test/Makefile @@ -4,7 +4,7 @@ # additional flags CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wno-ctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wno-float-equal -CPPFLAGS += -I ../src -I . +CPPFLAGS += -I ../src -I . -I thirdparty/catch SOURCES = src/unit.cpp \ src/unit-algorithms.cpp \ @@ -57,11 +57,11 @@ clean: # single test file ############################################################################## -json_unit: $(OBJECTS) ../src/json.hpp src/catch.hpp +json_unit: $(OBJECTS) ../src/json.hpp thirdparty/catch/catch.hpp @echo "[CXXLD] $@" @$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) -o $@ -%.o: %.cpp ../src/json.hpp src/catch.hpp +%.o: %.cpp ../src/json.hpp thirdparty/catch/catch.hpp @echo "[CXX] $@" @$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ @@ -70,7 +70,7 @@ json_unit: $(OBJECTS) ../src/json.hpp src/catch.hpp # individual test cases ############################################################################## -test-%: src/unit-%.cpp ../src/json.hpp src/catch.hpp +test-%: src/unit-%.cpp ../src/json.hpp thirdparty/catch/catch.hpp @echo "[CXXLD] $@" @$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) -DCATCH_CONFIG_MAIN $< -o $@ diff --git a/test/fuzz_test/parse_fuzzer.cc b/test/src/fuzzer-parse_json.cpp similarity index 61% rename from test/fuzz_test/parse_fuzzer.cc rename to test/src/fuzzer-parse_json.cpp index bb8b3d37..20a824db 100644 --- a/test/fuzz_test/parse_fuzzer.cc +++ b/test/src/fuzzer-parse_json.cpp @@ -18,19 +18,25 @@ using json = nlohmann::json; -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - try { - std::stringstream s; - s << json::parse(data, data + size); - try { - auto j = json::parse(s.str()); - std::stringstream s2; - s2 << j; - assert(s.str() == s2.str()); - assert(j == json::parse(s.str())); - } catch (const std::invalid_argument&) { - assert(0); +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + try + { + std::stringstream s; + s << json::parse(data, data + size); + try + { + auto j = json::parse(s.str()); + std::stringstream s2; + s2 << j; + assert(s.str() == s2.str()); + assert(j == json::parse(s.str())); + } + catch (const std::invalid_argument&) + { + assert(0); + } } - } catch (const std::invalid_argument&) { } - return 0; + catch (const std::invalid_argument&) { } + return 0; } diff --git a/test/fuzz_test/Fuzzer/CMakeLists.txt b/test/thirdparty/Fuzzer/CMakeLists.txt similarity index 100% rename from test/fuzz_test/Fuzzer/CMakeLists.txt rename to test/thirdparty/Fuzzer/CMakeLists.txt diff --git a/test/fuzz_test/Fuzzer/FuzzerCorpus.h b/test/thirdparty/Fuzzer/FuzzerCorpus.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerCorpus.h rename to test/thirdparty/Fuzzer/FuzzerCorpus.h diff --git a/test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp b/test/thirdparty/Fuzzer/FuzzerCrossOver.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp rename to test/thirdparty/Fuzzer/FuzzerCrossOver.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerDefs.h b/test/thirdparty/Fuzzer/FuzzerDefs.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerDefs.h rename to test/thirdparty/Fuzzer/FuzzerDefs.h diff --git a/test/fuzz_test/Fuzzer/FuzzerDictionary.h b/test/thirdparty/Fuzzer/FuzzerDictionary.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerDictionary.h rename to test/thirdparty/Fuzzer/FuzzerDictionary.h diff --git a/test/fuzz_test/Fuzzer/FuzzerDriver.cpp b/test/thirdparty/Fuzzer/FuzzerDriver.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerDriver.cpp rename to test/thirdparty/Fuzzer/FuzzerDriver.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctions.def b/test/thirdparty/Fuzzer/FuzzerExtFunctions.def similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerExtFunctions.def rename to test/thirdparty/Fuzzer/FuzzerExtFunctions.def diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctions.h b/test/thirdparty/Fuzzer/FuzzerExtFunctions.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerExtFunctions.h rename to test/thirdparty/Fuzzer/FuzzerExtFunctions.h diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctionsDlsym.cpp b/test/thirdparty/Fuzzer/FuzzerExtFunctionsDlsym.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerExtFunctionsDlsym.cpp rename to test/thirdparty/Fuzzer/FuzzerExtFunctionsDlsym.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeak.cpp b/test/thirdparty/Fuzzer/FuzzerExtFunctionsWeak.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeak.cpp rename to test/thirdparty/Fuzzer/FuzzerExtFunctionsWeak.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp b/test/thirdparty/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp rename to test/thirdparty/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerFlags.def b/test/thirdparty/Fuzzer/FuzzerFlags.def similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerFlags.def rename to test/thirdparty/Fuzzer/FuzzerFlags.def diff --git a/test/fuzz_test/Fuzzer/FuzzerIO.cpp b/test/thirdparty/Fuzzer/FuzzerIO.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerIO.cpp rename to test/thirdparty/Fuzzer/FuzzerIO.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerIO.h b/test/thirdparty/Fuzzer/FuzzerIO.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerIO.h rename to test/thirdparty/Fuzzer/FuzzerIO.h diff --git a/test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp b/test/thirdparty/Fuzzer/FuzzerIOPosix.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp rename to test/thirdparty/Fuzzer/FuzzerIOPosix.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp b/test/thirdparty/Fuzzer/FuzzerIOWindows.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp rename to test/thirdparty/Fuzzer/FuzzerIOWindows.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerInterface.h b/test/thirdparty/Fuzzer/FuzzerInterface.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerInterface.h rename to test/thirdparty/Fuzzer/FuzzerInterface.h diff --git a/test/fuzz_test/Fuzzer/FuzzerInternal.h b/test/thirdparty/Fuzzer/FuzzerInternal.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerInternal.h rename to test/thirdparty/Fuzzer/FuzzerInternal.h diff --git a/test/fuzz_test/Fuzzer/FuzzerLoop.cpp b/test/thirdparty/Fuzzer/FuzzerLoop.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerLoop.cpp rename to test/thirdparty/Fuzzer/FuzzerLoop.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerMain.cpp b/test/thirdparty/Fuzzer/FuzzerMain.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerMain.cpp rename to test/thirdparty/Fuzzer/FuzzerMain.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerMerge.cpp b/test/thirdparty/Fuzzer/FuzzerMerge.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerMerge.cpp rename to test/thirdparty/Fuzzer/FuzzerMerge.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerMerge.h b/test/thirdparty/Fuzzer/FuzzerMerge.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerMerge.h rename to test/thirdparty/Fuzzer/FuzzerMerge.h diff --git a/test/fuzz_test/Fuzzer/FuzzerMutate.cpp b/test/thirdparty/Fuzzer/FuzzerMutate.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerMutate.cpp rename to test/thirdparty/Fuzzer/FuzzerMutate.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerMutate.h b/test/thirdparty/Fuzzer/FuzzerMutate.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerMutate.h rename to test/thirdparty/Fuzzer/FuzzerMutate.h diff --git a/test/fuzz_test/Fuzzer/FuzzerOptions.h b/test/thirdparty/Fuzzer/FuzzerOptions.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerOptions.h rename to test/thirdparty/Fuzzer/FuzzerOptions.h diff --git a/test/fuzz_test/Fuzzer/FuzzerRandom.h b/test/thirdparty/Fuzzer/FuzzerRandom.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerRandom.h rename to test/thirdparty/Fuzzer/FuzzerRandom.h diff --git a/test/fuzz_test/Fuzzer/FuzzerSHA1.cpp b/test/thirdparty/Fuzzer/FuzzerSHA1.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerSHA1.cpp rename to test/thirdparty/Fuzzer/FuzzerSHA1.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerSHA1.h b/test/thirdparty/Fuzzer/FuzzerSHA1.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerSHA1.h rename to test/thirdparty/Fuzzer/FuzzerSHA1.h diff --git a/test/fuzz_test/Fuzzer/FuzzerTracePC.cpp b/test/thirdparty/Fuzzer/FuzzerTracePC.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerTracePC.cpp rename to test/thirdparty/Fuzzer/FuzzerTracePC.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerTracePC.h b/test/thirdparty/Fuzzer/FuzzerTracePC.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerTracePC.h rename to test/thirdparty/Fuzzer/FuzzerTracePC.h diff --git a/test/fuzz_test/Fuzzer/FuzzerTraceState.cpp b/test/thirdparty/Fuzzer/FuzzerTraceState.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerTraceState.cpp rename to test/thirdparty/Fuzzer/FuzzerTraceState.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerUtil.cpp b/test/thirdparty/Fuzzer/FuzzerUtil.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerUtil.cpp rename to test/thirdparty/Fuzzer/FuzzerUtil.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerUtil.h b/test/thirdparty/Fuzzer/FuzzerUtil.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerUtil.h rename to test/thirdparty/Fuzzer/FuzzerUtil.h diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp b/test/thirdparty/Fuzzer/FuzzerUtilDarwin.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp rename to test/thirdparty/Fuzzer/FuzzerUtilDarwin.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp b/test/thirdparty/Fuzzer/FuzzerUtilLinux.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp rename to test/thirdparty/Fuzzer/FuzzerUtilLinux.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp b/test/thirdparty/Fuzzer/FuzzerUtilPosix.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp rename to test/thirdparty/Fuzzer/FuzzerUtilPosix.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp b/test/thirdparty/Fuzzer/FuzzerUtilWindows.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp rename to test/thirdparty/Fuzzer/FuzzerUtilWindows.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerValueBitMap.h b/test/thirdparty/Fuzzer/FuzzerValueBitMap.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerValueBitMap.h rename to test/thirdparty/Fuzzer/FuzzerValueBitMap.h diff --git a/test/fuzz_test/Fuzzer/README.txt b/test/thirdparty/Fuzzer/README.txt similarity index 100% rename from test/fuzz_test/Fuzzer/README.txt rename to test/thirdparty/Fuzzer/README.txt diff --git a/test/fuzz_test/Fuzzer/afl/afl_driver.cpp b/test/thirdparty/Fuzzer/afl/afl_driver.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/afl/afl_driver.cpp rename to test/thirdparty/Fuzzer/afl/afl_driver.cpp diff --git a/test/fuzz_test/Fuzzer/build.sh b/test/thirdparty/Fuzzer/build.sh similarity index 100% rename from test/fuzz_test/Fuzzer/build.sh rename to test/thirdparty/Fuzzer/build.sh diff --git a/test/fuzz_test/Fuzzer/cxx.dict b/test/thirdparty/Fuzzer/cxx.dict similarity index 100% rename from test/fuzz_test/Fuzzer/cxx.dict rename to test/thirdparty/Fuzzer/cxx.dict diff --git a/test/fuzz_test/Fuzzer/standalone/StandaloneFuzzTargetMain.c b/test/thirdparty/Fuzzer/standalone/StandaloneFuzzTargetMain.c similarity index 100% rename from test/fuzz_test/Fuzzer/standalone/StandaloneFuzzTargetMain.c rename to test/thirdparty/Fuzzer/standalone/StandaloneFuzzTargetMain.c diff --git a/test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp b/test/thirdparty/Fuzzer/test/AFLDriverTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp rename to test/thirdparty/Fuzzer/test/AFLDriverTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/AbsNegAndConstant64Test.cpp b/test/thirdparty/Fuzzer/test/AbsNegAndConstant64Test.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/AbsNegAndConstant64Test.cpp rename to test/thirdparty/Fuzzer/test/AbsNegAndConstant64Test.cpp diff --git a/test/fuzz_test/Fuzzer/test/AbsNegAndConstantTest.cpp b/test/thirdparty/Fuzzer/test/AbsNegAndConstantTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/AbsNegAndConstantTest.cpp rename to test/thirdparty/Fuzzer/test/AbsNegAndConstantTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/AccumulateAllocationsTest.cpp b/test/thirdparty/Fuzzer/test/AccumulateAllocationsTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/AccumulateAllocationsTest.cpp rename to test/thirdparty/Fuzzer/test/AccumulateAllocationsTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/BufferOverflowOnInput.cpp b/test/thirdparty/Fuzzer/test/BufferOverflowOnInput.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/BufferOverflowOnInput.cpp rename to test/thirdparty/Fuzzer/test/BufferOverflowOnInput.cpp diff --git a/test/fuzz_test/Fuzzer/test/CMakeLists.txt b/test/thirdparty/Fuzzer/test/CMakeLists.txt similarity index 100% rename from test/fuzz_test/Fuzzer/test/CMakeLists.txt rename to test/thirdparty/Fuzzer/test/CMakeLists.txt diff --git a/test/fuzz_test/Fuzzer/test/CallerCalleeTest.cpp b/test/thirdparty/Fuzzer/test/CallerCalleeTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/CallerCalleeTest.cpp rename to test/thirdparty/Fuzzer/test/CallerCalleeTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/CounterTest.cpp b/test/thirdparty/Fuzzer/test/CounterTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/CounterTest.cpp rename to test/thirdparty/Fuzzer/test/CounterTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/CustomCrossOverTest.cpp b/test/thirdparty/Fuzzer/test/CustomCrossOverTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/CustomCrossOverTest.cpp rename to test/thirdparty/Fuzzer/test/CustomCrossOverTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/CustomMutatorTest.cpp b/test/thirdparty/Fuzzer/test/CustomMutatorTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/CustomMutatorTest.cpp rename to test/thirdparty/Fuzzer/test/CustomMutatorTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/DSO1.cpp b/test/thirdparty/Fuzzer/test/DSO1.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/DSO1.cpp rename to test/thirdparty/Fuzzer/test/DSO1.cpp diff --git a/test/fuzz_test/Fuzzer/test/DSO2.cpp b/test/thirdparty/Fuzzer/test/DSO2.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/DSO2.cpp rename to test/thirdparty/Fuzzer/test/DSO2.cpp diff --git a/test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp b/test/thirdparty/Fuzzer/test/DSOTestExtra.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp rename to test/thirdparty/Fuzzer/test/DSOTestExtra.cpp diff --git a/test/fuzz_test/Fuzzer/test/DSOTestMain.cpp b/test/thirdparty/Fuzzer/test/DSOTestMain.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/DSOTestMain.cpp rename to test/thirdparty/Fuzzer/test/DSOTestMain.cpp diff --git a/test/fuzz_test/Fuzzer/test/DivTest.cpp b/test/thirdparty/Fuzzer/test/DivTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/DivTest.cpp rename to test/thirdparty/Fuzzer/test/DivTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/EmptyTest.cpp b/test/thirdparty/Fuzzer/test/EmptyTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/EmptyTest.cpp rename to test/thirdparty/Fuzzer/test/EmptyTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/FourIndependentBranchesTest.cpp b/test/thirdparty/Fuzzer/test/FourIndependentBranchesTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/FourIndependentBranchesTest.cpp rename to test/thirdparty/Fuzzer/test/FourIndependentBranchesTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/FullCoverageSetTest.cpp b/test/thirdparty/Fuzzer/test/FullCoverageSetTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/FullCoverageSetTest.cpp rename to test/thirdparty/Fuzzer/test/FullCoverageSetTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp b/test/thirdparty/Fuzzer/test/FuzzerUnittest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp rename to test/thirdparty/Fuzzer/test/FuzzerUnittest.cpp diff --git a/test/fuzz_test/Fuzzer/test/InitializeTest.cpp b/test/thirdparty/Fuzzer/test/InitializeTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/InitializeTest.cpp rename to test/thirdparty/Fuzzer/test/InitializeTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/LeakTest.cpp b/test/thirdparty/Fuzzer/test/LeakTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/LeakTest.cpp rename to test/thirdparty/Fuzzer/test/LeakTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp b/test/thirdparty/Fuzzer/test/LeakTimeoutTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp rename to test/thirdparty/Fuzzer/test/LeakTimeoutTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/LoadTest.cpp b/test/thirdparty/Fuzzer/test/LoadTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/LoadTest.cpp rename to test/thirdparty/Fuzzer/test/LoadTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/MemcmpTest.cpp b/test/thirdparty/Fuzzer/test/MemcmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/MemcmpTest.cpp rename to test/thirdparty/Fuzzer/test/MemcmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/NthRunCrashTest.cpp b/test/thirdparty/Fuzzer/test/NthRunCrashTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/NthRunCrashTest.cpp rename to test/thirdparty/Fuzzer/test/NthRunCrashTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/NullDerefOnEmptyTest.cpp b/test/thirdparty/Fuzzer/test/NullDerefOnEmptyTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/NullDerefOnEmptyTest.cpp rename to test/thirdparty/Fuzzer/test/NullDerefOnEmptyTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/NullDerefTest.cpp b/test/thirdparty/Fuzzer/test/NullDerefTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/NullDerefTest.cpp rename to test/thirdparty/Fuzzer/test/NullDerefTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/OneHugeAllocTest.cpp b/test/thirdparty/Fuzzer/test/OneHugeAllocTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/OneHugeAllocTest.cpp rename to test/thirdparty/Fuzzer/test/OneHugeAllocTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp b/test/thirdparty/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp rename to test/thirdparty/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp b/test/thirdparty/Fuzzer/test/OutOfMemoryTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp rename to test/thirdparty/Fuzzer/test/OutOfMemoryTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/RepeatedBytesTest.cpp b/test/thirdparty/Fuzzer/test/RepeatedBytesTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/RepeatedBytesTest.cpp rename to test/thirdparty/Fuzzer/test/RepeatedBytesTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp b/test/thirdparty/Fuzzer/test/RepeatedMemcmp.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp rename to test/thirdparty/Fuzzer/test/RepeatedMemcmp.cpp diff --git a/test/fuzz_test/Fuzzer/test/ShrinkControlFlowTest.cpp b/test/thirdparty/Fuzzer/test/ShrinkControlFlowTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/ShrinkControlFlowTest.cpp rename to test/thirdparty/Fuzzer/test/ShrinkControlFlowTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/ShrinkValueProfileTest.cpp b/test/thirdparty/Fuzzer/test/ShrinkValueProfileTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/ShrinkValueProfileTest.cpp rename to test/thirdparty/Fuzzer/test/ShrinkValueProfileTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SignedIntOverflowTest.cpp b/test/thirdparty/Fuzzer/test/SignedIntOverflowTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SignedIntOverflowTest.cpp rename to test/thirdparty/Fuzzer/test/SignedIntOverflowTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp b/test/thirdparty/Fuzzer/test/SimpleCmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp rename to test/thirdparty/Fuzzer/test/SimpleCmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SimpleDictionaryTest.cpp b/test/thirdparty/Fuzzer/test/SimpleDictionaryTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SimpleDictionaryTest.cpp rename to test/thirdparty/Fuzzer/test/SimpleDictionaryTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp b/test/thirdparty/Fuzzer/test/SimpleHashTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp rename to test/thirdparty/Fuzzer/test/SimpleHashTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SimpleTest.cpp b/test/thirdparty/Fuzzer/test/SimpleTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SimpleTest.cpp rename to test/thirdparty/Fuzzer/test/SimpleTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SimpleThreadedTest.cpp b/test/thirdparty/Fuzzer/test/SimpleThreadedTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SimpleThreadedTest.cpp rename to test/thirdparty/Fuzzer/test/SimpleThreadedTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SingleMemcmpTest.cpp b/test/thirdparty/Fuzzer/test/SingleMemcmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SingleMemcmpTest.cpp rename to test/thirdparty/Fuzzer/test/SingleMemcmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SingleStrcmpTest.cpp b/test/thirdparty/Fuzzer/test/SingleStrcmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SingleStrcmpTest.cpp rename to test/thirdparty/Fuzzer/test/SingleStrcmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SingleStrncmpTest.cpp b/test/thirdparty/Fuzzer/test/SingleStrncmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SingleStrncmpTest.cpp rename to test/thirdparty/Fuzzer/test/SingleStrncmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SpamyTest.cpp b/test/thirdparty/Fuzzer/test/SpamyTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SpamyTest.cpp rename to test/thirdparty/Fuzzer/test/SpamyTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/StrcmpTest.cpp b/test/thirdparty/Fuzzer/test/StrcmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/StrcmpTest.cpp rename to test/thirdparty/Fuzzer/test/StrcmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp b/test/thirdparty/Fuzzer/test/StrncmpOOBTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp rename to test/thirdparty/Fuzzer/test/StrncmpOOBTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/StrncmpTest.cpp b/test/thirdparty/Fuzzer/test/StrncmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/StrncmpTest.cpp rename to test/thirdparty/Fuzzer/test/StrncmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/StrstrTest.cpp b/test/thirdparty/Fuzzer/test/StrstrTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/StrstrTest.cpp rename to test/thirdparty/Fuzzer/test/StrstrTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp b/test/thirdparty/Fuzzer/test/SwapCmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp rename to test/thirdparty/Fuzzer/test/SwapCmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/Switch2Test.cpp b/test/thirdparty/Fuzzer/test/Switch2Test.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/Switch2Test.cpp rename to test/thirdparty/Fuzzer/test/Switch2Test.cpp diff --git a/test/fuzz_test/Fuzzer/test/SwitchTest.cpp b/test/thirdparty/Fuzzer/test/SwitchTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SwitchTest.cpp rename to test/thirdparty/Fuzzer/test/SwitchTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/ThreadedLeakTest.cpp b/test/thirdparty/Fuzzer/test/ThreadedLeakTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/ThreadedLeakTest.cpp rename to test/thirdparty/Fuzzer/test/ThreadedLeakTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/ThreadedTest.cpp b/test/thirdparty/Fuzzer/test/ThreadedTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/ThreadedTest.cpp rename to test/thirdparty/Fuzzer/test/ThreadedTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/TimeoutEmptyTest.cpp b/test/thirdparty/Fuzzer/test/TimeoutEmptyTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/TimeoutEmptyTest.cpp rename to test/thirdparty/Fuzzer/test/TimeoutEmptyTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/TimeoutTest.cpp b/test/thirdparty/Fuzzer/test/TimeoutTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/TimeoutTest.cpp rename to test/thirdparty/Fuzzer/test/TimeoutTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/TraceMallocTest.cpp b/test/thirdparty/Fuzzer/test/TraceMallocTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/TraceMallocTest.cpp rename to test/thirdparty/Fuzzer/test/TraceMallocTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/UninstrumentedTest.cpp b/test/thirdparty/Fuzzer/test/UninstrumentedTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/UninstrumentedTest.cpp rename to test/thirdparty/Fuzzer/test/UninstrumentedTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/afl-driver-extra-stats.test b/test/thirdparty/Fuzzer/test/afl-driver-extra-stats.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/afl-driver-extra-stats.test rename to test/thirdparty/Fuzzer/test/afl-driver-extra-stats.test diff --git a/test/fuzz_test/Fuzzer/test/afl-driver-stderr.test b/test/thirdparty/Fuzzer/test/afl-driver-stderr.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/afl-driver-stderr.test rename to test/thirdparty/Fuzzer/test/afl-driver-stderr.test diff --git a/test/fuzz_test/Fuzzer/test/caller-callee.test b/test/thirdparty/Fuzzer/test/caller-callee.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/caller-callee.test rename to test/thirdparty/Fuzzer/test/caller-callee.test diff --git a/test/fuzz_test/Fuzzer/test/coverage.test b/test/thirdparty/Fuzzer/test/coverage.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/coverage.test rename to test/thirdparty/Fuzzer/test/coverage.test diff --git a/test/fuzz_test/Fuzzer/test/dict1.txt b/test/thirdparty/Fuzzer/test/dict1.txt similarity index 100% rename from test/fuzz_test/Fuzzer/test/dict1.txt rename to test/thirdparty/Fuzzer/test/dict1.txt diff --git a/test/fuzz_test/Fuzzer/test/dump_coverage.test b/test/thirdparty/Fuzzer/test/dump_coverage.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/dump_coverage.test rename to test/thirdparty/Fuzzer/test/dump_coverage.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-customcrossover.test b/test/thirdparty/Fuzzer/test/fuzzer-customcrossover.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-customcrossover.test rename to test/thirdparty/Fuzzer/test/fuzzer-customcrossover.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-custommutator.test b/test/thirdparty/Fuzzer/test/fuzzer-custommutator.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-custommutator.test rename to test/thirdparty/Fuzzer/test/fuzzer-custommutator.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-dict.test b/test/thirdparty/Fuzzer/test/fuzzer-dict.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-dict.test rename to test/thirdparty/Fuzzer/test/fuzzer-dict.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-dirs.test b/test/thirdparty/Fuzzer/test/fuzzer-dirs.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-dirs.test rename to test/thirdparty/Fuzzer/test/fuzzer-dirs.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-fdmask.test b/test/thirdparty/Fuzzer/test/fuzzer-fdmask.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-fdmask.test rename to test/thirdparty/Fuzzer/test/fuzzer-fdmask.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-finalstats.test b/test/thirdparty/Fuzzer/test/fuzzer-finalstats.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-finalstats.test rename to test/thirdparty/Fuzzer/test/fuzzer-finalstats.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-flags.test b/test/thirdparty/Fuzzer/test/fuzzer-flags.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-flags.test rename to test/thirdparty/Fuzzer/test/fuzzer-flags.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-jobs.test b/test/thirdparty/Fuzzer/test/fuzzer-jobs.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-jobs.test rename to test/thirdparty/Fuzzer/test/fuzzer-jobs.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-leak.test b/test/thirdparty/Fuzzer/test/fuzzer-leak.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-leak.test rename to test/thirdparty/Fuzzer/test/fuzzer-leak.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-oom-with-profile.test b/test/thirdparty/Fuzzer/test/fuzzer-oom-with-profile.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-oom-with-profile.test rename to test/thirdparty/Fuzzer/test/fuzzer-oom-with-profile.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-oom.test b/test/thirdparty/Fuzzer/test/fuzzer-oom.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-oom.test rename to test/thirdparty/Fuzzer/test/fuzzer-oom.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-printcovpcs.test b/test/thirdparty/Fuzzer/test/fuzzer-printcovpcs.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-printcovpcs.test rename to test/thirdparty/Fuzzer/test/fuzzer-printcovpcs.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-runs.test b/test/thirdparty/Fuzzer/test/fuzzer-runs.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-runs.test rename to test/thirdparty/Fuzzer/test/fuzzer-runs.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-seed.test b/test/thirdparty/Fuzzer/test/fuzzer-seed.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-seed.test rename to test/thirdparty/Fuzzer/test/fuzzer-seed.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-segv.test b/test/thirdparty/Fuzzer/test/fuzzer-segv.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-segv.test rename to test/thirdparty/Fuzzer/test/fuzzer-segv.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-singleinputs.test b/test/thirdparty/Fuzzer/test/fuzzer-singleinputs.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-singleinputs.test rename to test/thirdparty/Fuzzer/test/fuzzer-singleinputs.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-threaded.test b/test/thirdparty/Fuzzer/test/fuzzer-threaded.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-threaded.test rename to test/thirdparty/Fuzzer/test/fuzzer-threaded.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-timeout.test b/test/thirdparty/Fuzzer/test/fuzzer-timeout.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-timeout.test rename to test/thirdparty/Fuzzer/test/fuzzer-timeout.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-traces-hooks.test b/test/thirdparty/Fuzzer/test/fuzzer-traces-hooks.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-traces-hooks.test rename to test/thirdparty/Fuzzer/test/fuzzer-traces-hooks.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-ubsan.test b/test/thirdparty/Fuzzer/test/fuzzer-ubsan.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-ubsan.test rename to test/thirdparty/Fuzzer/test/fuzzer-ubsan.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer.test b/test/thirdparty/Fuzzer/test/fuzzer.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer.test rename to test/thirdparty/Fuzzer/test/fuzzer.test diff --git a/test/fuzz_test/Fuzzer/test/hi.txt b/test/thirdparty/Fuzzer/test/hi.txt similarity index 100% rename from test/fuzz_test/Fuzzer/test/hi.txt rename to test/thirdparty/Fuzzer/test/hi.txt diff --git a/test/fuzz_test/Fuzzer/test/lit.cfg b/test/thirdparty/Fuzzer/test/lit.cfg similarity index 100% rename from test/fuzz_test/Fuzzer/test/lit.cfg rename to test/thirdparty/Fuzzer/test/lit.cfg diff --git a/test/fuzz_test/Fuzzer/test/lit.site.cfg.in b/test/thirdparty/Fuzzer/test/lit.site.cfg.in similarity index 100% rename from test/fuzz_test/Fuzzer/test/lit.site.cfg.in rename to test/thirdparty/Fuzzer/test/lit.site.cfg.in diff --git a/test/fuzz_test/Fuzzer/test/merge.test b/test/thirdparty/Fuzzer/test/merge.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/merge.test rename to test/thirdparty/Fuzzer/test/merge.test diff --git a/test/fuzz_test/Fuzzer/test/minimize_crash.test b/test/thirdparty/Fuzzer/test/minimize_crash.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/minimize_crash.test rename to test/thirdparty/Fuzzer/test/minimize_crash.test diff --git a/test/fuzz_test/Fuzzer/test/no-coverage/CMakeLists.txt b/test/thirdparty/Fuzzer/test/no-coverage/CMakeLists.txt similarity index 100% rename from test/fuzz_test/Fuzzer/test/no-coverage/CMakeLists.txt rename to test/thirdparty/Fuzzer/test/no-coverage/CMakeLists.txt diff --git a/test/fuzz_test/Fuzzer/test/repeated-bytes.test b/test/thirdparty/Fuzzer/test/repeated-bytes.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/repeated-bytes.test rename to test/thirdparty/Fuzzer/test/repeated-bytes.test diff --git a/test/fuzz_test/Fuzzer/test/shrink.test b/test/thirdparty/Fuzzer/test/shrink.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/shrink.test rename to test/thirdparty/Fuzzer/test/shrink.test diff --git a/test/fuzz_test/Fuzzer/test/simple-cmp.test b/test/thirdparty/Fuzzer/test/simple-cmp.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/simple-cmp.test rename to test/thirdparty/Fuzzer/test/simple-cmp.test diff --git a/test/fuzz_test/Fuzzer/test/standalone.test b/test/thirdparty/Fuzzer/test/standalone.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/standalone.test rename to test/thirdparty/Fuzzer/test/standalone.test diff --git a/test/fuzz_test/Fuzzer/test/swap-cmp.test b/test/thirdparty/Fuzzer/test/swap-cmp.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/swap-cmp.test rename to test/thirdparty/Fuzzer/test/swap-cmp.test diff --git a/test/fuzz_test/Fuzzer/test/trace-malloc.test b/test/thirdparty/Fuzzer/test/trace-malloc.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/trace-malloc.test rename to test/thirdparty/Fuzzer/test/trace-malloc.test diff --git a/test/fuzz_test/Fuzzer/test/ubsan/CMakeLists.txt b/test/thirdparty/Fuzzer/test/ubsan/CMakeLists.txt similarity index 100% rename from test/fuzz_test/Fuzzer/test/ubsan/CMakeLists.txt rename to test/thirdparty/Fuzzer/test/ubsan/CMakeLists.txt diff --git a/test/fuzz_test/Fuzzer/test/ulimit.test b/test/thirdparty/Fuzzer/test/ulimit.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/ulimit.test rename to test/thirdparty/Fuzzer/test/ulimit.test diff --git a/test/fuzz_test/Fuzzer/test/uninstrumented/CMakeLists.txt b/test/thirdparty/Fuzzer/test/uninstrumented/CMakeLists.txt similarity index 100% rename from test/fuzz_test/Fuzzer/test/uninstrumented/CMakeLists.txt rename to test/thirdparty/Fuzzer/test/uninstrumented/CMakeLists.txt diff --git a/test/fuzz_test/Fuzzer/test/unit/lit.cfg b/test/thirdparty/Fuzzer/test/unit/lit.cfg similarity index 100% rename from test/fuzz_test/Fuzzer/test/unit/lit.cfg rename to test/thirdparty/Fuzzer/test/unit/lit.cfg diff --git a/test/fuzz_test/Fuzzer/test/unit/lit.site.cfg.in b/test/thirdparty/Fuzzer/test/unit/lit.site.cfg.in similarity index 100% rename from test/fuzz_test/Fuzzer/test/unit/lit.site.cfg.in rename to test/thirdparty/Fuzzer/test/unit/lit.site.cfg.in diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp.test b/test/thirdparty/Fuzzer/test/value-profile-cmp.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-cmp.test rename to test/thirdparty/Fuzzer/test/value-profile-cmp.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp2.test b/test/thirdparty/Fuzzer/test/value-profile-cmp2.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-cmp2.test rename to test/thirdparty/Fuzzer/test/value-profile-cmp2.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp3.test b/test/thirdparty/Fuzzer/test/value-profile-cmp3.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-cmp3.test rename to test/thirdparty/Fuzzer/test/value-profile-cmp3.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp4.test b/test/thirdparty/Fuzzer/test/value-profile-cmp4.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-cmp4.test rename to test/thirdparty/Fuzzer/test/value-profile-cmp4.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-div.test b/test/thirdparty/Fuzzer/test/value-profile-div.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-div.test rename to test/thirdparty/Fuzzer/test/value-profile-div.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-load.test b/test/thirdparty/Fuzzer/test/value-profile-load.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-load.test rename to test/thirdparty/Fuzzer/test/value-profile-load.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-mem.test b/test/thirdparty/Fuzzer/test/value-profile-mem.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-mem.test rename to test/thirdparty/Fuzzer/test/value-profile-mem.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-set.test b/test/thirdparty/Fuzzer/test/value-profile-set.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-set.test rename to test/thirdparty/Fuzzer/test/value-profile-set.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-strcmp.test b/test/thirdparty/Fuzzer/test/value-profile-strcmp.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-strcmp.test rename to test/thirdparty/Fuzzer/test/value-profile-strcmp.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-strncmp.test b/test/thirdparty/Fuzzer/test/value-profile-strncmp.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-strncmp.test rename to test/thirdparty/Fuzzer/test/value-profile-strncmp.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-switch.test b/test/thirdparty/Fuzzer/test/value-profile-switch.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-switch.test rename to test/thirdparty/Fuzzer/test/value-profile-switch.test diff --git a/test/src/catch.hpp b/test/thirdparty/catch/catch.hpp similarity index 100% rename from test/src/catch.hpp rename to test/thirdparty/catch/catch.hpp From 22a6b956e89d3468b28a46b11ad242ebee0c4954 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 09:36:34 +0100 Subject: [PATCH 05/35] :page_facing_up: added license for Catch --- test/thirdparty/catch/LICENSE_1_0.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100755 test/thirdparty/catch/LICENSE_1_0.txt diff --git a/test/thirdparty/catch/LICENSE_1_0.txt b/test/thirdparty/catch/LICENSE_1_0.txt new file mode 100755 index 00000000..36b7cd93 --- /dev/null +++ b/test/thirdparty/catch/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. From 048330b14b019d93e4f05437915fdd01d9861c81 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 09:37:15 +0100 Subject: [PATCH 06/35] :arrow_up: Catch v1.5.9 --- test/thirdparty/catch/catch.hpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) mode change 100644 => 100755 test/thirdparty/catch/catch.hpp diff --git a/test/thirdparty/catch/catch.hpp b/test/thirdparty/catch/catch.hpp old mode 100644 new mode 100755 index 2e6fe8d3..3d18eadb --- a/test/thirdparty/catch/catch.hpp +++ b/test/thirdparty/catch/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v1.5.8 - * Generated: 2016-10-26 12:07:30.938259 + * Catch v1.5.9 + * Generated: 2016-11-29 12:14:38.049276 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -3428,6 +3428,7 @@ namespace Catch { #include #include #include +#include namespace Catch { @@ -3995,9 +3996,12 @@ namespace Clara { inline void convertInto( std::string const& _source, std::string& _dest ) { _dest = _source; } + char toLowerCh(char c) { + return static_cast( ::tolower( c ) ); + } inline void convertInto( std::string const& _source, bool& _dest ) { std::string sourceLC = _source; - std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower ); + std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh ); if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) _dest = true; else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) @@ -7578,7 +7582,7 @@ namespace Catch { return os; } - Version libraryVersion( 1, 5, 8, "", 0 ); + Version libraryVersion( 1, 5, 9, "", 0 ); } @@ -9166,6 +9170,7 @@ namespace Catch { public: XmlReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ), + m_xml(_config.stream()), m_sectionDepth( 0 ) { m_reporterPrefs.shouldRedirectStdOut = true; @@ -9185,7 +9190,6 @@ namespace Catch { virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testRunStarting( testInfo ); - m_xml.setStream( stream ); m_xml.startElement( "Catch" ); if( !m_config->name().empty() ) m_xml.writeAttribute( "name", m_config->name() ); From a084e90f392b6cbc4facb2bc993d21a88fe25ec9 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 11:09:26 +0100 Subject: [PATCH 07/35] :hammer: split AFL test in driver and test file --- .gitignore | 2 + Makefile | 10 ++--- test/Makefile | 8 ++++ test/src/fuzz.cpp | 34 ----------------- test/src/fuzzer-driver_afl.cpp | 33 +++++++++++++++++ test/src/fuzzer-parse_json.cpp | 67 +++++++++++++++++++++++----------- 6 files changed, 91 insertions(+), 63 deletions(-) delete mode 100644 test/src/fuzz.cpp create mode 100644 test/src/fuzzer-driver_afl.cpp diff --git a/.gitignore b/.gitignore index 104befbe..eedc9641 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ cmake-build-debug test/test-* .svn + +test/thirdparty/Fuzzer/libFuzzer.a diff --git a/Makefile b/Makefile index fcce453e..8278dbf2 100644 --- a/Makefile +++ b/Makefile @@ -49,14 +49,10 @@ doctest: fuzz_testing: rm -fr fuzz-testing mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out - $(MAKE) fuzz CXX=afl-clang++ - mv fuzz fuzz-testing + $(MAKE) parse_afl_fuzzer -C test CXX=afl-clang++ + mv test/fuzzer parse_afl_fuzzer find test/data/json_tests -size -5k -name *json | xargs -I{} cp "{}" fuzz-testing/testcases - @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzz" - -# the fuzzer binary -fuzz: test/src/fuzz.cpp src/json.hpp - $(CXX) -std=c++11 $(CXXFLAGS) $(FLAGS) $(CPPFLAGS) -I src $< $(LDFLAGS) -o $@ + @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" ########################################################################## diff --git a/test/Makefile b/test/Makefile index c1fb33f4..da679998 100644 --- a/test/Makefile +++ b/test/Makefile @@ -78,3 +78,11 @@ TEST_PATTERN = "*" TEST_PREFIX = "" check: $(TESTCASES) @cd .. ; for testcase in $(TESTCASES); do echo "Executing $$testcase..."; $(TEST_PREFIX)test/$$testcase $(TEST_PATTERN) || exit 1; done + + +############################################################################## +# fuzzer +############################################################################## + +parse_afl_fuzzer: + $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_json.cpp -o $@ diff --git a/test/src/fuzz.cpp b/test/src/fuzz.cpp deleted file mode 100644 index ef403ea8..00000000 --- a/test/src/fuzz.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - __ _____ _____ _____ - __| | __| | | | JSON for Modern C++ (fuzz test support) -| | |__ | | | | | | version 2.0.9 -|_____|_____|_____|_|___| https://github.com/nlohmann/json - -Run "make fuzz_testing" and follow the instructions. - -Licensed under the MIT License . -*/ - -#include - -using json = nlohmann::json; - -int main() -{ -#ifdef __AFL_HAVE_MANUAL_CONTROL - while (__AFL_LOOP(1000)) - { -#endif - try - { - json j(std::cin); - std::cout << j << std::endl; - } - catch (std::invalid_argument& e) - { - std::cout << "Invalid argument in parsing" << e.what() << '\n'; - } -#ifdef __AFL_HAVE_MANUAL_CONTROL - } -#endif -} diff --git a/test/src/fuzzer-driver_afl.cpp b/test/src/fuzzer-driver_afl.cpp new file mode 100644 index 00000000..e386033a --- /dev/null +++ b/test/src/fuzzer-driver_afl.cpp @@ -0,0 +1,33 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (fuzz test support) +| | |__ | | | | | | version 2.0.9 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +This file implements a driver for American Fuzzy Lop (afl-fuzz). It relies on +an implementation of the `LLVMFuzzerTestOneInput` function which processes a +passed byte array. + +Licensed under the MIT License . +*/ + +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); + +int main() +{ +#ifdef __AFL_HAVE_MANUAL_CONTROL + while (__AFL_LOOP(1000)) + { +#endif + // copy stdin to stringstream to pass it to fuzzer as byte array + std::stringstream ss; + ss << std::cin.rdbuf(); + LLVMFuzzerTestOneInput(reinterpret_cast(ss.str().c_str()), ss.str().size()); +#ifdef __AFL_HAVE_MANUAL_CONTROL + } +#endif +} diff --git a/test/src/fuzzer-parse_json.cpp b/test/src/fuzzer-parse_json.cpp index 20a824db..51ac440d 100644 --- a/test/src/fuzzer-parse_json.cpp +++ b/test/src/fuzzer-parse_json.cpp @@ -1,16 +1,23 @@ -// Copyright 2016 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (fuzz test support) +| | |__ | | | | | | version 2.0.9 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +This file implements a parser test suitable for fuzz testing. Given a byte +array data, it performs the following steps: + +- j1 = parse(data) +- s1 = serialize(j1) +- j2 = parse(s1) +- s2 = serialize(j2) +- assert(s1 == s2) + +The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer +drivers. + +Licensed under the MIT License . +*/ #include #include @@ -18,25 +25,41 @@ using json = nlohmann::json; +// see http://llvm.org/docs/LibFuzzer.html extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { try { - std::stringstream s; - s << json::parse(data, data + size); + // step 1: parse input + json j1 = json::parse(data, data + size); + try { - auto j = json::parse(s.str()); - std::stringstream s2; - s2 << j; - assert(s.str() == s2.str()); - assert(j == json::parse(s.str())); + // step 2: round trip + + // first serialization + std::string s1 = j1.dump(); + + // parse serialization + json j2 = json::parse(s1); + + // second serialization + std::string s2 = j2.dump(); + + // serializations must match + assert(s1 == s2); } catch (const std::invalid_argument&) { - assert(0); + // parsing a JSON serialization must not fail + assert(false); } } - catch (const std::invalid_argument&) { } + catch (const std::invalid_argument&) + { + // parse errors are ok, because input may be random bytes + } + + // return 0 - non-zero return values are reserved for future use return 0; } From 33be4e7f1f19fea00279aa1733a3c72f475b1a12 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 12:08:36 +0100 Subject: [PATCH 08/35] :construction: cleanup --- Makefile | 10 ++++- test/Makefile | 3 ++ test/src/fuzzer-parse_cbor.cpp | 68 ++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 test/src/fuzzer-parse_cbor.cpp diff --git a/Makefile b/Makefile index 8278dbf2..3e873cbc 100644 --- a/Makefile +++ b/Makefile @@ -50,10 +50,18 @@ fuzz_testing: rm -fr fuzz-testing mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out $(MAKE) parse_afl_fuzzer -C test CXX=afl-clang++ - mv test/fuzzer parse_afl_fuzzer + mv test/parse_afl_fuzzer fuzz-testing/fuzzer find test/data/json_tests -size -5k -name *json | xargs -I{} cp "{}" fuzz-testing/testcases @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" +fuzz_testing_cbor: + rm -fr fuzz-testing + mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out + $(MAKE) parse_cbor_fuzzer -C test CXX=afl-clang++ + mv test/parse_cbor_fuzzer fuzz-testing/fuzzer + find test/data -size -5k -name *cbor | xargs -I{} cp "{}" fuzz-testing/testcases + @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" + ########################################################################## # static analyzer diff --git a/test/Makefile b/test/Makefile index da679998..a063221a 100644 --- a/test/Makefile +++ b/test/Makefile @@ -86,3 +86,6 @@ check: $(TESTCASES) parse_afl_fuzzer: $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_json.cpp -o $@ + +parse_cbor_fuzzer: + $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_cbor.cpp -o $@ diff --git a/test/src/fuzzer-parse_cbor.cpp b/test/src/fuzzer-parse_cbor.cpp new file mode 100644 index 00000000..42ce679c --- /dev/null +++ b/test/src/fuzzer-parse_cbor.cpp @@ -0,0 +1,68 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (fuzz test support) +| | |__ | | | | | | version 2.0.9 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +This file implements a parser test suitable for fuzz testing. Given a byte +array data, it performs the following steps: + +- j1 = from_cbor(data) +- vec = to_cbor(j1) +- j2 = from_cbor(vec) +- assert(j1 == j2) + +The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer +drivers. + +Licensed under the MIT License . +*/ + +#include +#include +#include + +using json = nlohmann::json; + +// see http://llvm.org/docs/LibFuzzer.html +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + try + { + // step 1: parse input + std::vector vec1(data, data + size); + json j1 = json::from_cbor(vec1); + + try + { + // step 2: round trip + std::vector vec2 = json::to_cbor(j1); + + // parse serialization + json j2 = json::from_cbor(vec2); + + // deserializations must match + assert(j1 == j2); + } + catch (const std::invalid_argument&) + { + // parsing a CBOR serialization must not fail + assert(false); + } + } + catch (const std::invalid_argument&) + { + // parse errors are ok, because input may be random bytes + } + catch (const std::out_of_range&) + { + // parse errors are ok, because input may be random bytes + } + catch (const std::domain_error&) + { + // parse errors are ok, because input may be random bytes + } + + // return 0 - non-zero return values are reserved for future use + return 0; +} From f87f4c06f9dc3b3813fa859479d321508675a5c0 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 12:10:52 +0100 Subject: [PATCH 09/35] :construction: cleanup --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3e873cbc..c99778af 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ all: # clean up clean: - rm -fr json_unit json_benchmarks fuzz fuzz-testing *.dSYM + rm -fr json_unit json_benchmarks fuzz fuzz-testing *.dSYM test/*.dSYM rm -fr benchmarks/files/numbers/*.json $(MAKE) clean -Cdoc $(MAKE) clean -Ctest From 6e129fbfb7a09856659763a13193d6ae1ee6ae0f Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 25 Dec 2016 14:39:08 +0100 Subject: [PATCH 10/35] :memo: some small documentation fixes --- doc/Makefile | 1 + src/json.hpp | 16 ++++++++++------ src/json.hpp.re2c | 16 ++++++++++------ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/doc/Makefile b/doc/Makefile index 56198923..04a5cc7b 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -57,6 +57,7 @@ doxygen: create_output create_links $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, AllocatorType >@@g' html/*.html $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html + $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html upload: clean doxygen check_output cd html ; ../scripts/git-update-ghpages nlohmann/json diff --git a/src/json.hpp b/src/json.hpp index 23058bee..cb301643 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -3793,7 +3793,7 @@ class basic_json container `c`, the expression `c.front()` is equivalent to `*c.begin()`. @return In case of a structured type (array or object), a reference to the - first element is returned. In cast of number, string, or boolean values, a + first element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @@ -3836,7 +3836,7 @@ class basic_json @endcode @return In case of a structured type (array or object), a reference to the - last element is returned. In cast of number, string, or boolean values, a + last element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @@ -4187,10 +4187,14 @@ class basic_json element is not found or the JSON value is not an object, end() is returned. + @note This method always returns @ref end() when executed on a JSON type + that is not an object. + @param[in] key key value of the element to search for @return Iterator to an element with key equivalent to @a key. If no such - element is found, past-the-end (see end()) iterator is returned. + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. @complexity Logarithmic in the size of the JSON object. @@ -4233,6 +4237,9 @@ class basic_json default `std::map` type, the return value will always be `0` (@a key was not found) or `1` (@a key was found). + @note This method always returns `0` when executed on a JSON type that is + not an object. + @param[in] key key value of the element to count @return Number of elements with key @a key. If the JSON value is not an @@ -4792,9 +4799,6 @@ class basic_json object | `{}` array | `[]` - @note Floating-point numbers are set to `0.0` which will be serialized to - `0`. The vale type remains @ref number_float_t. - @complexity Linear in the size of the JSON value. @liveexample{The example below shows the effect of `clear()` to different diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index ac9a3315..6723e75a 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -3793,7 +3793,7 @@ class basic_json container `c`, the expression `c.front()` is equivalent to `*c.begin()`. @return In case of a structured type (array or object), a reference to the - first element is returned. In cast of number, string, or boolean values, a + first element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @@ -3836,7 +3836,7 @@ class basic_json @endcode @return In case of a structured type (array or object), a reference to the - last element is returned. In cast of number, string, or boolean values, a + last element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @@ -4187,10 +4187,14 @@ class basic_json element is not found or the JSON value is not an object, end() is returned. + @note This method always returns @ref end() when executed on a JSON type + that is not an object. + @param[in] key key value of the element to search for @return Iterator to an element with key equivalent to @a key. If no such - element is found, past-the-end (see end()) iterator is returned. + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. @complexity Logarithmic in the size of the JSON object. @@ -4233,6 +4237,9 @@ class basic_json default `std::map` type, the return value will always be `0` (@a key was not found) or `1` (@a key was found). + @note This method always returns `0` when executed on a JSON type that is + not an object. + @param[in] key key value of the element to count @return Number of elements with key @a key. If the JSON value is not an @@ -4792,9 +4799,6 @@ class basic_json object | `{}` array | `[]` - @note Floating-point numbers are set to `0.0` which will be serialized to - `0`. The vale type remains @ref number_float_t. - @complexity Linear in the size of the JSON value. @liveexample{The example below shows the effect of `clear()` to different From dbdcb3fec8f4ac47d99ab283a5c60034b3297539 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 25 Dec 2016 16:06:36 +0100 Subject: [PATCH 11/35] :bug: fixed bug in CBOR/MessagePack deserialization --- src/json.hpp | 30 ++++++++++++++++++++++++++++++ src/json.hpp.re2c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/json.hpp b/src/json.hpp index 23058bee..1d92b51a 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6831,6 +6831,27 @@ class basic_json } } + /*! + @brief checks if a given length does not exceed the size of a given vector + + To secure the access to the byte vector during CBOR/MessagePack + deserialization, bytes are copied from the vector into buffers. This + function checks if the number of bytes to copy (@a len) does not exceed the + size of the given vector @a vec. + + @param[in] vec byte vector + @param[in] len length + + @throws out_of_range if `len > v.size()` + */ + static void check_length(const std::vector& vec, const size_t& len) + { + if (len > vec.size()) + { + throw std::out_of_range("len out of range"); + } + } + /*! @brief create a JSON value from a given MessagePack vector @@ -6882,6 +6903,7 @@ class basic_json const size_t len = v[current_idx] & 0x1f; const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } } @@ -6985,6 +7007,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -6993,6 +7016,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7001,6 +7025,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7219,6 +7244,7 @@ class basic_json const auto len = static_cast(v[current_idx] - 0x60); const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7227,6 +7253,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7235,6 +7262,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7243,6 +7271,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7251,6 +7280,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 9; idx += len + 8; // skip 8 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index ac9a3315..d5b05c01 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6831,6 +6831,27 @@ class basic_json } } + /*! + @brief checks if a given length does not exceed the size of a given vector + + To secure the access to the byte vector during CBOR/MessagePack + deserialization, bytes are copied from the vector into buffers. This + function checks if the number of bytes to copy (@a len) does not exceed the + size of the given vector @a vec. + + @param[in] vec byte vector + @param[in] len length + + @throws out_of_range if `len > v.size()` + */ + static void check_length(const std::vector& vec, const size_t& len) + { + if (len > vec.size()) + { + throw std::out_of_range("len out of range"); + } + } + /*! @brief create a JSON value from a given MessagePack vector @@ -6882,6 +6903,7 @@ class basic_json const size_t len = v[current_idx] & 0x1f; const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } } @@ -6985,6 +7007,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -6993,6 +7016,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7001,6 +7025,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7219,6 +7244,7 @@ class basic_json const auto len = static_cast(v[current_idx] - 0x60); const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7227,6 +7253,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7235,6 +7262,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7243,6 +7271,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7251,6 +7280,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 9; idx += len + 8; // skip 8 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } From 6de9d4035a3becaa7d92812ca3ededfaf9a0cb29 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 25 Dec 2016 16:06:56 +0100 Subject: [PATCH 12/35] :bug: fixed AFL driver to also read binary data --- test/src/fuzzer-driver_afl.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/test/src/fuzzer-driver_afl.cpp b/test/src/fuzzer-driver_afl.cpp index e386033a..eddeae41 100644 --- a/test/src/fuzzer-driver_afl.cpp +++ b/test/src/fuzzer-driver_afl.cpp @@ -11,9 +11,9 @@ passed byte array. Licensed under the MIT License . */ -#include -#include -#include +#include // for vector +#include // for uint8_t +#include // for cin extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); @@ -23,10 +23,15 @@ int main() while (__AFL_LOOP(1000)) { #endif - // copy stdin to stringstream to pass it to fuzzer as byte array - std::stringstream ss; - ss << std::cin.rdbuf(); - LLVMFuzzerTestOneInput(reinterpret_cast(ss.str().c_str()), ss.str().size()); + // copy stdin to byte vector + std::vector vec; + char c; + while (std::cin.get(c)) + { + vec.push_back(static_cast(c)); + } + + LLVMFuzzerTestOneInput(vec.data(), vec.size()); #ifdef __AFL_HAVE_MANUAL_CONTROL } #endif From e4cc62e6538662e0526eb81689a0d55334c1ac33 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 25 Dec 2016 16:07:10 +0100 Subject: [PATCH 13/35] :bug: fixed Makefile to work with proper CBOR files --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c99778af..76e60170 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ fuzz_testing_cbor: mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out $(MAKE) parse_cbor_fuzzer -C test CXX=afl-clang++ mv test/parse_cbor_fuzzer fuzz-testing/fuzzer - find test/data -size -5k -name *cbor | xargs -I{} cp "{}" fuzz-testing/testcases + find test/data -size -5k -name *.cbor | xargs -I{} cp "{}" fuzz-testing/testcases @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" From 1399abc58371978814e62b5eadc281ccbeb8dc96 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 25 Dec 2016 16:18:56 +0100 Subject: [PATCH 14/35] :construction: added MessagePack fuzz target --- Makefile | 8 ++++ test/Makefile | 3 ++ test/src/fuzzer-parse_msgpack.cpp | 68 +++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 test/src/fuzzer-parse_msgpack.cpp diff --git a/Makefile b/Makefile index 76e60170..8f196950 100644 --- a/Makefile +++ b/Makefile @@ -62,6 +62,14 @@ fuzz_testing_cbor: find test/data -size -5k -name *.cbor | xargs -I{} cp "{}" fuzz-testing/testcases @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" +fuzz_testing_msgpack: + rm -fr fuzz-testing + mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out + $(MAKE) parse_msgpack_fuzzer -C test CXX=afl-clang++ + mv test/parse_msgpack_fuzzer fuzz-testing/fuzzer + find test/data -size -5k -name *.msgpack | xargs -I{} cp "{}" fuzz-testing/testcases + @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" + ########################################################################## # static analyzer diff --git a/test/Makefile b/test/Makefile index a063221a..556ab0d7 100644 --- a/test/Makefile +++ b/test/Makefile @@ -89,3 +89,6 @@ parse_afl_fuzzer: parse_cbor_fuzzer: $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_cbor.cpp -o $@ + +parse_msgpack_fuzzer: + $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_msgpack.cpp -o $@ diff --git a/test/src/fuzzer-parse_msgpack.cpp b/test/src/fuzzer-parse_msgpack.cpp new file mode 100644 index 00000000..992697c2 --- /dev/null +++ b/test/src/fuzzer-parse_msgpack.cpp @@ -0,0 +1,68 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (fuzz test support) +| | |__ | | | | | | version 2.0.9 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +This file implements a parser test suitable for fuzz testing. Given a byte +array data, it performs the following steps: + +- j1 = from_msgpack(data) +- vec = to_msgpack(j1) +- j2 = from_msgpack(vec) +- assert(j1 == j2) + +The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer +drivers. + +Licensed under the MIT License . +*/ + +#include +#include +#include + +using json = nlohmann::json; + +// see http://llvm.org/docs/LibFuzzer.html +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + try + { + // step 1: parse input + std::vector vec1(data, data + size); + json j1 = json::from_msgpack(vec1); + + try + { + // step 2: round trip + std::vector vec2 = json::to_msgpack(j1); + + // parse serialization + json j2 = json::from_msgpack(vec2); + + // deserializations must match + assert(j1 == j2); + } + catch (const std::invalid_argument&) + { + // parsing a MessagePack serialization must not fail + assert(false); + } + } + catch (const std::invalid_argument&) + { + // parse errors are ok, because input may be random bytes + } + catch (const std::out_of_range&) + { + // parse errors are ok, because input may be random bytes + } + catch (const std::domain_error&) + { + // parse errors are ok, because input may be random bytes + } + + // return 0 - non-zero return values are reserved for future use + return 0; +} From e3b036348b16c1cbfb41e0d746160a99cfee989c Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 25 Dec 2016 16:36:43 +0100 Subject: [PATCH 15/35] :construction: added targets for parallel AFL fuzzing --- Makefile | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Makefile b/Makefile index 8f196950..e2c120db 100644 --- a/Makefile +++ b/Makefile @@ -70,6 +70,19 @@ fuzz_testing_msgpack: find test/data -size -5k -name *.msgpack | xargs -I{} cp "{}" fuzz-testing/testcases @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" +fuzzing-start: + afl-fuzz -S fuzzer1 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer2 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer3 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer4 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer5 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer6 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer7 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -M fuzzer0 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer + +fuzzing-stop: + -killall fuzzer + -killall afl-fuzz ########################################################################## # static analyzer From acb7e0558d2337ad42261ed81e67d37385679285 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 28 Dec 2016 12:09:15 +0100 Subject: [PATCH 16/35] :ambulance: fixed bugs detected by AFL-Fuzz --- src/json.hpp | 57 +++++++++++++ src/json.hpp.re2c | 57 +++++++++++++ ...0,sig:06,src:000223+000677,op:splice,rep:2 | 1 + ...id:000000,sig:06,src:000787,op:havoc,rep:8 | Bin 0 -> 19 bytes ...d:000000,sig:06,src:000833,op:havoc,rep:32 | Bin 0 -> 38 bytes ...d:000000,sig:06,src:000838,op:havoc,rep:64 | Bin 0 -> 842 bytes ...,sig:06,src:000846+001064,op:splice,rep:16 | Bin 0 -> 201 bytes ...id:000000,sig:06,src:000848,op:flip1,pos:0 | Bin 0 -> 263 bytes ...d:000000,sig:06,src:001435,op:havoc,rep:32 | Bin 0 -> 2488 bytes ...id:000000,sig:06,src:001436,op:havoc,rep:4 | Bin 0 -> 2579 bytes ...1,sig:06,src:000864+000903,op:splice,rep:2 | Bin 0 -> 33 bytes ...,sig:06,src:001310+001138,op:splice,rep:32 | 1 + ...,sig:06,src:001330+000569,op:splice,rep:64 | Bin 0 -> 1932 bytes ...d:000001,sig:06,src:001413,op:havoc,rep:32 | Bin 0 -> 1396 bytes ...id:000001,sig:06,src:001447,op:havoc,rep:4 | Bin 0 -> 1839 bytes ...1,sig:06,src:001465+000325,op:splice,rep:4 | Bin 0 -> 475 bytes ...id:000002,sig:06,src:000539,op:havoc,rep:8 | Bin 0 -> 140 bytes ...d:000002,sig:06,src:001301,op:havoc,rep:16 | Bin 0 -> 16 bytes ...2,sig:06,src:001317+000850,op:splice,rep:8 | Bin 0 -> 349 bytes ...:000002,sig:06,src:001382,op:havoc,rep:128 | Bin 0 -> 3116 bytes ...2,sig:06,src:001413+001036,op:splice,rep:4 | Bin 0 -> 395 bytes ...,sig:06,src:000846+000155,op:splice,rep:16 | Bin 0 -> 222 bytes ...:000004,sig:06,src:001445,op:havoc,rep:128 | Bin 0 -> 4382 bytes test/src/unit-cbor.cpp | 78 ++++++++++++++++++ 24 files changed, 194 insertions(+) create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2 create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:000787,op:havoc,rep:8 create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:000833,op:havoc,rep:32 create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:000838,op:havoc,rep:64 create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:000846+001064,op:splice,rep:16 create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:000848,op:flip1,pos:0 create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:001435,op:havoc,rep:32 create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:001436,op:havoc,rep:4 create mode 100644 test/data/cbor_regression/id:000001,sig:06,src:000864+000903,op:splice,rep:2 create mode 100644 test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32 create mode 100644 test/data/cbor_regression/id:000001,sig:06,src:001330+000569,op:splice,rep:64 create mode 100644 test/data/cbor_regression/id:000001,sig:06,src:001413,op:havoc,rep:32 create mode 100644 test/data/cbor_regression/id:000001,sig:06,src:001447,op:havoc,rep:4 create mode 100644 test/data/cbor_regression/id:000001,sig:06,src:001465+000325,op:splice,rep:4 create mode 100644 test/data/cbor_regression/id:000002,sig:06,src:000539,op:havoc,rep:8 create mode 100644 test/data/cbor_regression/id:000002,sig:06,src:001301,op:havoc,rep:16 create mode 100644 test/data/cbor_regression/id:000002,sig:06,src:001317+000850,op:splice,rep:8 create mode 100644 test/data/cbor_regression/id:000002,sig:06,src:001382,op:havoc,rep:128 create mode 100644 test/data/cbor_regression/id:000002,sig:06,src:001413+001036,op:splice,rep:4 create mode 100644 test/data/cbor_regression/id:000003,sig:06,src:000846+000155,op:splice,rep:16 create mode 100644 test/data/cbor_regression/id:000004,sig:06,src:001445,op:havoc,rep:128 diff --git a/src/json.hpp b/src/json.hpp index cb301643..913f1966 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6835,6 +6835,44 @@ class basic_json } } + + /* + @brief checks if given lengths do not exceed the size of a given vector + + To secure the access to the byte vector during CBOR/MessagePack + deserialization, bytes are copied from the vector into buffers. This + function checks if the number of bytes to copy (@a len) does not exceed the + size @s size of the vector. Additionally, an @a offset is given from where + to start reading the bytes. + + This function checks whether reading the bytes is safe; that is, offset is a + valid index in the vector, offset+len + + @param[in] size size of the byte vector + @param[in] len number of bytes to read + @param[in] offset offset where to start reading + + vec: x x x x x X X X X X + ^ ^ ^ + 0 offset len + + @throws out_of_range if `len > v.size()` + */ + static void check_length(const size_t size, const size_t len, const size_t offset) + { + // simple case: requested length is greater than the vector's length + if (len > size or offset > size) + { + throw std::out_of_range("len out of range"); + } + + // second case: adding offset would result in overflow + if ((size > (std::numeric_limits::max() - offset))) + { + throw std::out_of_range("len+offset out of range"); + } + } + /*! @brief create a JSON value from a given MessagePack vector @@ -6886,6 +6924,7 @@ class basic_json const size_t len = v[current_idx] & 0x1f; const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } } @@ -6989,6 +7028,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -6997,6 +7037,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7005,6 +7046,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7223,6 +7265,7 @@ class basic_json const auto len = static_cast(v[current_idx] - 0x60); const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7231,6 +7274,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7239,6 +7283,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7247,6 +7292,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7255,6 +7301,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 9; idx += len + 8; // skip 8 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7595,6 +7642,11 @@ class basic_json */ static basic_json from_msgpack(const std::vector& v) { + if (v.empty()) + { + throw std::invalid_argument("empty vector"); + } + size_t i = 0; return from_msgpack_internal(v, i); } @@ -7652,6 +7704,11 @@ class basic_json */ static basic_json from_cbor(const std::vector& v) { + if (v.empty()) + { + throw std::invalid_argument("empty vector"); + } + size_t i = 0; return from_cbor_internal(v, i); } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 6723e75a..3b598542 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6835,6 +6835,44 @@ class basic_json } } + + /* + @brief checks if given lengths do not exceed the size of a given vector + + To secure the access to the byte vector during CBOR/MessagePack + deserialization, bytes are copied from the vector into buffers. This + function checks if the number of bytes to copy (@a len) does not exceed the + size @s size of the vector. Additionally, an @a offset is given from where + to start reading the bytes. + + This function checks whether reading the bytes is safe; that is, offset is a + valid index in the vector, offset+len + + @param[in] size size of the byte vector + @param[in] len number of bytes to read + @param[in] offset offset where to start reading + + vec: x x x x x X X X X X + ^ ^ ^ + 0 offset len + + @throws out_of_range if `len > v.size()` + */ + static void check_length(const size_t size, const size_t len, const size_t offset) + { + // simple case: requested length is greater than the vector's length + if (len > size or offset > size) + { + throw std::out_of_range("len out of range"); + } + + // second case: adding offset would result in overflow + if ((size > (std::numeric_limits::max() - offset))) + { + throw std::out_of_range("len+offset out of range"); + } + } + /*! @brief create a JSON value from a given MessagePack vector @@ -6886,6 +6924,7 @@ class basic_json const size_t len = v[current_idx] & 0x1f; const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } } @@ -6989,6 +7028,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -6997,6 +7037,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7005,6 +7046,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7223,6 +7265,7 @@ class basic_json const auto len = static_cast(v[current_idx] - 0x60); const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7231,6 +7274,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7239,6 +7283,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7247,6 +7292,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7255,6 +7301,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 9; idx += len + 8; // skip 8 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7595,6 +7642,11 @@ class basic_json */ static basic_json from_msgpack(const std::vector& v) { + if (v.empty()) + { + throw std::invalid_argument("empty vector"); + } + size_t i = 0; return from_msgpack_internal(v, i); } @@ -7652,6 +7704,11 @@ class basic_json */ static basic_json from_cbor(const std::vector& v) { + if (v.empty()) + { + throw std::invalid_argument("empty vector"); + } + size_t i = 0; return from_cbor_internal(v, i); } diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2 b/test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2 new file mode 100644 index 00000000..e30ed176 --- /dev/null +++ b/test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2 @@ -0,0 +1 @@ +{ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000787,op:havoc,rep:8 b/test/data/cbor_regression/id:000000,sig:06,src:000787,op:havoc,rep:8 new file mode 100644 index 0000000000000000000000000000000000000000..8de7c9e50d18d64f7807ba158718d1f71895dcbb GIT binary patch literal 19 Ucmb>a4*~zI8JHLtY#4w50DwOT>;M1& literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000833,op:havoc,rep:32 b/test/data/cbor_regression/id:000000,sig:06,src:000833,op:havoc,rep:32 new file mode 100644 index 0000000000000000000000000000000000000000..d2ae80c771595f1dd59d700664577524eb5b6c78 GIT binary patch literal 38 ncmb>a4+Yz{ZCko+8v~FsY*hH0R#}`{B9NAmvAB3qiNr4e3C$Nr literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000838,op:havoc,rep:64 b/test/data/cbor_regression/id:000000,sig:06,src:000838,op:havoc,rep:64 new file mode 100644 index 0000000000000000000000000000000000000000..d1ef19b507738ecf9c8e939347afc9087aad6876 GIT binary patch literal 842 zcmb>a4+Yz{$)Db~EwwbYcH+c|^OvrjxCRUqz~oj20SI@^|MwFoPMEl8B2ZVup>5l? zIcx@MO4*SF)C2^Ltq}Tri3G#{2FAoJ=bXmcYz2?hq6UUU28Hyzs?@xav}6VbCWay_ zl7JCb)j?QAQ3Z>D76NTeElSBt%uP)xF3p3=V>c32GkLs{%#ywm?bHf|U`fU=_3}V# zvvU(O^Ww`h8Gsa4+Y!6fB{6-9|Do5w{1(!O|6{>0&6F(K>}OX#DIW3n4CBfs(v$2eM)K)SpCFC u5Oo#;oa4+Y!6fB{6-9|Do5w{1%;O|6{>0&6F(K>}OhY_KGVfN9zc)RdB%1QBn9N}n&0 z_?41al9=V3lbM{Y;E`I?z>vtGke-*DnpcvR3?x#EQi?OHQYBOpbMsS*OY;mHGyKz9 zi&IM^(lRniBpJWd%O~chX6Ghm=Eaw1=B31xr}G8RB&QwU2&>{H!go0b6(hUX^LPGdafk>dNZ4(a0gz3!N-GSMi?aY+!s*&o| zo6*D@#Df?A0sjR42Tz_fF~JLHjL|x~Eu}PqH3ISAoaTG;_RV|W+q`d{)#i&!d9y^2 zLJQpOGYzRs8z}`MFheBe@Rs7`x`CDX!mLr{nZqV9@L@-7)kg>Sc0a!{SH3OX*tsx9 zc`&I$+oKE-VBuqmu9^0fl7IyEW&)Q6P~c^*_)UZ?J4^p@?Z=W(VN{3G1=R`d7c+(bM^11&Rp@+(O?DYiAyH7DcOGCx$~*tstnsV-&uSY<#-Ut3~9=uty?K?Qla7k zAktqsD6ZLDufIz^lbY8uq(*4nFbplFI$VZCm(xV@bF5EW5B;C5zoavemSvDA%3@z; Gb3Xwc-m0Vk literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:001436,op:havoc,rep:4 b/test/data/cbor_regression/id:000000,sig:06,src:001436,op:havoc,rep:4 new file mode 100644 index 0000000000000000000000000000000000000000..bb32f698895c0584b1476a95e8db9b6272bef2f5 GIT binary patch literal 2579 zcmdT`&uZFeee0V(Mq?y>hw4> ztfj)L0yIvC83Q_TQieGH&?v7DzHvs*NSMf(cR)f6&&|W_i)UWG`1?<1{onS5=ifGj zN=}-z6A8&U$TSk{2dA@W5SaF(Ye^^)SW})dc-kA>ymn>or`07_=Z-q}QppT-wDJK_ z>89M;eeUlzr`Du6cV?G8pa1Oe?3ZDOFWqtf>eqXp9l@_<#^3)~=nmu>aG|NI)HLe# z#k~COP0(6!p!P@W=rccp9>n=q*fmN+?X0vsxn=J^Ynff@bI)(Y!q*B_U>Z1(2%54q zT({M3F1CJbcYBTb^XJZ8x=L{#TN1KNX#%CYJGg=M$iyoD)eZ|`*wf74s681L9)E+J zJr)*7p2QYOj%w9c0m}wc8}Pt5p2uy$t~h#8huA(+n(H!ikHm0e2zne05mvgL7sw%C zLupzOnptU5Dc&0P4Oz`sNg%>ysSueSH#G1oV`y5GQ`CHTzPc={l}d$>i-crJ6j}UeU2mF#L8Z2zN~&}Aozh|5SAT6! z>Nm$IM&r=d^DdigO(vFj9cq5j@ac2kmJoBm;?Y z_eS}YtQuPWwCpUMUMkh9Me{7lPRw%7&{V29nj*kLq1hRWWH~!i{z~p}HNwF&rp2~W`Zh_GW^aNuJ{{+F_u*Bc&3FyiVZ_75HVtih%R!_3 NefxuDbw9>>^9`wVbX@=d literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:000864+000903,op:splice,rep:2 b/test/data/cbor_regression/id:000001,sig:06,src:000864+000903,op:splice,rep:2 new file mode 100644 index 0000000000000000000000000000000000000000..e94858106169ac59d0330ea1f59fcc42f09d2f2e GIT binary patch literal 33 ecmZ3OQa^bX!-NUe4Q2jmtlVHwUteGS9|i#A<`C`x literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32 b/test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32 new file mode 100644 index 00000000..92079a80 --- /dev/null +++ b/test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32 @@ -0,0 +1 @@ +”{ÿÿÿÿÿÿÿÿÿ’ÿÿÿÿÿÿÿÿúúúúúúúúúúúúetú \ No newline at end of file diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001330+000569,op:splice,rep:64 b/test/data/cbor_regression/id:000001,sig:06,src:001330+000569,op:splice,rep:64 new file mode 100644 index 0000000000000000000000000000000000000000..0e3f141f863bb079b063807cf5accd2f27fadc2f GIT binary patch literal 1932 zcmeHIy-ve05VlYsfHz=bth4X}s8Xg@0yR4v$tAe8!z&SihA-D}NXYksfh zZ^r%gVUQ~6EERDR(#aTem2`T8LYV`trRBMlnnK3|1F4PC_*zD2vZJ4(dxIA*e_#<1 z40SSsY%Ff^ob`~Pm<*bCNvLM>l<)-TQd7?NEh#cUXua0h4s`Y7D9fuGmQ(%$hfzmd literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001413,op:havoc,rep:32 b/test/data/cbor_regression/id:000001,sig:06,src:001413,op:havoc,rep:32 new file mode 100644 index 0000000000000000000000000000000000000000..0ce68701c4269554399e96dd393c063e3493f186 GIT binary patch literal 1396 zcmc(fy>1gh5XaBBA{C_@q^ppSz$i!{WQ0UwLcj|4VH=@9LDsi(zBPBZ=bf?dtRQSU zBpM1T8dA~l0z3;(Kt&ah5QW)W8!P^Z3kuk3=Qp!Eqn-cGyQI>nRqZBc#P(!6CjrTJ z7!fGzcFYhrk4UyU;F6u#i6GzJv>|{HzR}U>#>MZ)KR(+BziQXMoGl3%ZBgv_B48YX z*ca@Q?Q9VOQ?9==Xp0EyvLy*Gw7QRXwlCkW-o2s>DDqYuFak$O7Z9Z?2gCXGYTh)~ z;dpKt_IgF!49$g4jh`prEKqOWo;0R=SY&6cMRL0RY&h(%TbbkdnSfFsTvb8M41?{=&yT_zxKUYdGh*7`tRU}EY<$Hd{5Rn`?h6SYXkm^ zlbOC(UFos3oAutBABi#cnZGByVr{{HTo~Z5yIZbsT XBGS3D8Q8;xiw>uMgr!9{#QfLaIWB|R literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001447,op:havoc,rep:4 b/test/data/cbor_regression/id:000001,sig:06,src:001447,op:havoc,rep:4 new file mode 100644 index 0000000000000000000000000000000000000000..35f3cb597438e5ac05686abe6588f769b8e233a4 GIT binary patch literal 1839 zcmcJQ&ubGw6vq>LvLJZOMO*|61)HU|LP;r7(;pgQ5|fCCm(K3I%}zSIlgvz-je?DM z^y0yj7kly|UOf3H^l$Jl@F1RAK`hp{n@vp8h+1?mnfJ}in>X)$-p-45u2L)*RZ29; zi&|2E(ng7B4bnFnM5*~bEv!1&+GrbXrd?y+01h5})_dKh*~5b`?~Sc*#cLnVW|;6- zb))1mPAKq(%g7a@w5k;lX}jy48uOtnETPqm)x5WUWA0XA!<3PbrdER!4J9dTP)f)w z-yI!KM$O22bUZR9tQeCN!vyVrNslKxo|-J5P}?Vz-Dd;o^_!JS zlb3-qiFL)YVYQc{0u*;ZN=5+zaJQ6AY!|BFMSrP^77|idc}U9raDG?vV7X2$JL551 z3MCw!QBAHu?pQKon)zaOHGi{Msb=OE7Z#S+aGyms7$3OQj*yie9ezlMO1s(<(Wxa; zgH}}%dpot?iWPes#VVM9k?DHwxYr7CXh&Vj?tAm?z&Ba3VR4!e(b4l*Da{vg+U&2 z+|#2NlP9zgT=f-YdSO;L=sBBXPX!l+tfg(?{Je9{#iZvzO}O*Bcy#-dv0pmf9OO)u z!I7Zc>9OMV+|u^Z??&JEKXrO^gc%=BFw&D1eu@Th#-#9wB;MO?%yb@$7eX0a94Zc{;wuN_hTs%ru-M=EDyBf2ZLpk1Lk((va+i(HlC`+JNijke zMstF!IGR(FHFFBGo@zI7{D)-G(Qb;!Vw1ZG`+DD_ZNL9}$kyOZ=n9C0|A}^(6ngipP Xw8GTP^qdk&qc16WiMcu1h7!L3r4T|{ literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001301,op:havoc,rep:16 b/test/data/cbor_regression/id:000002,sig:06,src:001301,op:havoc,rep:16 new file mode 100644 index 0000000000000000000000000000000000000000..0bde9e5dafffd9b12824980438be7148cc5b4cbd GIT binary patch literal 16 OcmeC4QvDwR7=!?C;0&Sw literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001317+000850,op:splice,rep:8 b/test/data/cbor_regression/id:000002,sig:06,src:001317+000850,op:splice,rep:8 new file mode 100644 index 0000000000000000000000000000000000000000..0a004d9e5d88a9143366bb06355f4802b8978194 GIT binary patch literal 349 zcmbPI{hvs1k4SY?SM~;I;o-dL3m6BMJnB|<4nVhZQky_NikjS8r zo|l`NSCWw50=N G%P#;ZUH69o literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001382,op:havoc,rep:128 b/test/data/cbor_regression/id:000002,sig:06,src:001382,op:havoc,rep:128 new file mode 100644 index 0000000000000000000000000000000000000000..6ff4823ae78f780f81f09637fa08249459c2338e GIT binary patch literal 3116 zcmZ3OGHdcIh6xj_8_N9CSaOQxfC&fTihD=^ zmJ%^tfy3DRUvO8cB0Cy~a;PgFg01&Mcv=CJu6MMh2i|+M{4J z1V%$(Gz11o2!IMPXxWP>{NN>izN8&e5ucl8EdW*zD$+f`HGmqDG@=Gk0GH(m^Pr6c zgaS}$>mQ$5VwJKZBm+`8LTYUXNUaU4$cV10vcb(2j7s7$j#3}exv8Y(RH1(cqw1~%i68hNn1;TEs0`+oC0P`eK(1T%FDknsW7PGcy~EXhzXRLD)u zO-e0NvWiwpEH7fH)Bp=xDtP6j=B8$pD8<(1DikXeD6p`yvAa3?xVd|HdU^Z!`uPV4 Q1_g(N&g}dzz`*bU07#X%a{vGU literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001413+001036,op:splice,rep:4 b/test/data/cbor_regression/id:000002,sig:06,src:001413+001036,op:splice,rep:4 new file mode 100644 index 0000000000000000000000000000000000000000..d32904abc2805ce51238f7023c52ab0f6152b0f2 GIT binary patch literal 395 zcmbPI?iK9srx22oSd!?IpU#+{T3n(KkXTZZT9l_ykXT%7xS%9IDJwO(M4?=vJhLQ2 z!B8PJCp9;Hdb)f138V78{F0|P^1PKA7eVe0gX!Xt~-fC3^Y z0%|$G4;=P#ZGV0s&o?DAJ+masz|hDzH!nRiF()%UFE=HxB(3IYDllwQN)vNaQ;JI& Q8PYTS)6$AlOC){)06NLQRsaA1 literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000003,sig:06,src:000846+000155,op:splice,rep:16 b/test/data/cbor_regression/id:000003,sig:06,src:000846+000155,op:splice,rep:16 new file mode 100644 index 0000000000000000000000000000000000000000..156afe0f177baf655b54b410a2128abd6b8002f9 GIT binary patch literal 222 zcmb>a4+XUofnf65i7(bnYy`5-mq`3dNi0dsa?Z(2&Q|b9Eoxv$WKc-Y%T3KINh{Ax zDap`f{F0WDnwjpDotsf2$@nEDFEJO$PRz`UFVD%%q_?{qTrpM zmzJ1Rnv+cz05x?4To@XD$phMKu6|zg48zX2k*_(E67XF hOw7rIx+WOtq|_pi^{FYvrFn)r89?KTQ+p(S0RTe0Vu%0$ literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000004,sig:06,src:001445,op:havoc,rep:128 b/test/data/cbor_regression/id:000004,sig:06,src:001445,op:havoc,rep:128 new file mode 100644 index 0000000000000000000000000000000000000000..e342979eb1b875aadac159e722c4259527aec25c GIT binary patch literal 4382 zcmeHKPmkL~6rTvhp{E|h0V$VqKs3?P3Zbl!*zTXgDrCbZ)p9}Ei9NPwGw~!dlg6Q? zUdoOCLWm>ni33)23Cp)x75c{Zah@Hb3zPN8kI&=IVC@qk7I47+?hN{{GDee5ZbDL zuV)2`R-K(Sc_Ygi`q1g}vHwTH9l+i`=S-Fvw}nL+X$kB~i-BhXT#(|Uo{9g)b%R^jGjyg0?gnJ{hznCho>~i%|2>n4u{D&t`_0Cm(j*oh zGBx>PX;z;nMrLK;-%aR|`i<|Oot>GN2Gb}%Ao7q{;(h5{l7Xv5dVPjfxSVd$9Q~D) zuaZVuH|ca;+uSXh-b)5|En2_H3J4)Fu2L7iOu4(I`VEINY!cRYD1Non*fJ%-!fZngQ8s(G6}k7R4zD4&L8Rp34+dN4y~u@KHu!I+lk(A6 z($Uz~>$r<2{$#Rn9D2D#+BX@O5<^f@K}-g~jl(OJ*U|@kLS3q^_OZadpSp0^HEslP zdsiw$DJl$1R#ChMUNCG~+196;&W#dEk{Ej0S&yF4BT0 zME`y0NN$6nXv2k;_J*#m!oh7K1T_IBlyjUYyicI;LcDm5X-L@A*l|lRZ}Y_L7}Xv% z1ee@WRXJdi2>431Qm7qlZ`IyDXtt^w*RNgM+<|!=yr=PLTFc7fqM4{KS$0$pk~qLA zn+!E_b&EgeAlJ04L--o#$e1E`T0&QISQWn}ssd+JMLQ2Mn65ceMf=`oG%}8e-gB=QAYu$Yw*)b7jG;a(45*xvUs9eV zbVJLTLH7)c*T>++Vo9`#xqs=riIy02y^CuTL5Q!XWT8!TpiP8bYjgNDBu%p=VI7{MDt;{nKi-4=G z{p-qFXq)@9ObW5{EV&3m1(Zc`5jsT?#m(X(c@!3*=QXcRQP@}3q_6?|&A8q$xR7|) zBl7zoAirOdV^%GSlb)jUJei!IBea-sCLCCN6?zNQEfqBJg&*w! N4a8pAxG&4w{{SA`b1?t_ literal 0 HcmV?d00001 diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 14944cff..5769ac98 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -1186,6 +1186,84 @@ TEST_CASE("single CBOR roundtrip") } } +TEST_CASE("CBOR regressions") +{ + SECTION("fuzz test results") + { + /* + The following test cases were found during a two-day session with + AFL-Fuzz. As a result, empty byte vectors and excessive lengths are + detected. + */ + for (std::string filename : + { + "test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2", + "test/data/cbor_regression/id:000000,sig:06,src:000787,op:havoc,rep:8", + "test/data/cbor_regression/id:000000,sig:06,src:000833,op:havoc,rep:32", + "test/data/cbor_regression/id:000000,sig:06,src:000838,op:havoc,rep:64", + "test/data/cbor_regression/id:000000,sig:06,src:000846+001064,op:splice,rep:16", + "test/data/cbor_regression/id:000000,sig:06,src:000848,op:flip1,pos:0", + "test/data/cbor_regression/id:000000,sig:06,src:001435,op:havoc,rep:32", + "test/data/cbor_regression/id:000000,sig:06,src:001436,op:havoc,rep:4v", + "test/data/cbor_regression/id:000001,sig:06,src:000864+000903,op:splice,rep:2", + "test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32", + "test/data/cbor_regression/id:000001,sig:06,src:001330+000569,op:splice,rep:64", + "test/data/cbor_regression/id:000001,sig:06,src:001413,op:havoc,rep:32", + "test/data/cbor_regression/id:000001,sig:06,src:001447,op:havoc,rep:4", + "test/data/cbor_regression/id:000001,sig:06,src:001465+000325,op:splice,rep:4", + "test/data/cbor_regression/id:000002,sig:06,src:000539,op:havoc,rep:8", + "test/data/cbor_regression/id:000002,sig:06,src:001301,op:havoc,rep:16", + "test/data/cbor_regression/id:000002,sig:06,src:001317+000850,op:splice,rep:8", + "test/data/cbor_regression/id:000002,sig:06,src:001382,op:havoc,rep:128", + "test/data/cbor_regression/id:000002,sig:06,src:001413+001036,op:splice,rep:4", + "test/data/cbor_regression/id:000003,sig:06,src:000846+000155,op:splice,rep:16", + "test/data/cbor_regression/id:000004,sig:06,src:001445,op:havoc,rep:128" + }) + { + CAPTURE(filename); + + try + { + // parse CBOR file + std::ifstream f_cbor(filename, std::ios::binary); + std::vector vec1( + (std::istreambuf_iterator(f_cbor)), + std::istreambuf_iterator()); + json j1 = json::from_cbor(vec1); + + try + { + // step 2: round trip + std::vector vec2 = json::to_cbor(j1); + + // parse serialization + json j2 = json::from_cbor(vec2); + + // deserializations must match + CHECK(j1 == j2); + } + catch (const std::invalid_argument&) + { + // parsing a CBOR serialization must not fail + CHECK(false); + } + } + catch (const std::invalid_argument&) + { + // parse errors are ok, because input may be random bytes + } + catch (const std::out_of_range&) + { + // parse errors are ok, because input may be random bytes + } + catch (const std::domain_error&) + { + // parse errors are ok, because input may be random bytes + } + } + } +} + TEST_CASE("CBOR roundtrips", "[hide]") { SECTION("input from flynn") From 888f5b9f60591579f72339dc653e614562eb6e31 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 28 Dec 2016 12:18:48 +0100 Subject: [PATCH 17/35] :truck: renamed test files to allow windows build --- ...c:000223+000677,op:splice,rep:2 => test01} | 0 ...ig:06,src:000787,op:havoc,rep:8 => test02} | Bin ...g:06,src:000833,op:havoc,rep:32 => test03} | Bin ...g:06,src:000838,op:havoc,rep:64 => test04} | Bin ...:000846+001064,op:splice,rep:16 => test05} | Bin ...ig:06,src:000848,op:flip1,pos:0 => test06} | Bin ...g:06,src:001435,op:havoc,rep:32 => test07} | Bin ...ig:06,src:001436,op:havoc,rep:4 => test08} | Bin ...c:000864+000903,op:splice,rep:2 => test09} | Bin ...:001310+001138,op:splice,rep:32 => test10} | 0 ...:001330+000569,op:splice,rep:64 => test11} | Bin ...g:06,src:001413,op:havoc,rep:32 => test12} | Bin ...ig:06,src:001447,op:havoc,rep:4 => test13} | Bin ...c:001465+000325,op:splice,rep:4 => test14} | Bin ...ig:06,src:000539,op:havoc,rep:8 => test15} | Bin ...:06,src:001445,op:havoc,rep:128 => test16} | Bin ...g:06,src:001301,op:havoc,rep:16 => test17} | Bin ...c:001317+000850,op:splice,rep:8 => test18} | Bin ...:06,src:001382,op:havoc,rep:128 => test19} | Bin ...c:001413+001036,op:splice,rep:4 => test20} | Bin ...:000846+000155,op:splice,rep:16 => test21} | Bin test/src/unit-cbor.cpp | 42 +++++++++--------- 22 files changed, 21 insertions(+), 21 deletions(-) rename test/data/cbor_regression/{id:000000,sig:06,src:000223+000677,op:splice,rep:2 => test01} (100%) rename test/data/cbor_regression/{id:000000,sig:06,src:000787,op:havoc,rep:8 => test02} (100%) rename test/data/cbor_regression/{id:000000,sig:06,src:000833,op:havoc,rep:32 => test03} (100%) rename test/data/cbor_regression/{id:000000,sig:06,src:000838,op:havoc,rep:64 => test04} (100%) rename test/data/cbor_regression/{id:000000,sig:06,src:000846+001064,op:splice,rep:16 => test05} (100%) rename test/data/cbor_regression/{id:000000,sig:06,src:000848,op:flip1,pos:0 => test06} (100%) rename test/data/cbor_regression/{id:000000,sig:06,src:001435,op:havoc,rep:32 => test07} (100%) rename test/data/cbor_regression/{id:000000,sig:06,src:001436,op:havoc,rep:4 => test08} (100%) rename test/data/cbor_regression/{id:000001,sig:06,src:000864+000903,op:splice,rep:2 => test09} (100%) rename test/data/cbor_regression/{id:000001,sig:06,src:001310+001138,op:splice,rep:32 => test10} (100%) rename test/data/cbor_regression/{id:000001,sig:06,src:001330+000569,op:splice,rep:64 => test11} (100%) rename test/data/cbor_regression/{id:000001,sig:06,src:001413,op:havoc,rep:32 => test12} (100%) rename test/data/cbor_regression/{id:000001,sig:06,src:001447,op:havoc,rep:4 => test13} (100%) rename test/data/cbor_regression/{id:000001,sig:06,src:001465+000325,op:splice,rep:4 => test14} (100%) rename test/data/cbor_regression/{id:000002,sig:06,src:000539,op:havoc,rep:8 => test15} (100%) rename test/data/cbor_regression/{id:000004,sig:06,src:001445,op:havoc,rep:128 => test16} (100%) rename test/data/cbor_regression/{id:000002,sig:06,src:001301,op:havoc,rep:16 => test17} (100%) rename test/data/cbor_regression/{id:000002,sig:06,src:001317+000850,op:splice,rep:8 => test18} (100%) rename test/data/cbor_regression/{id:000002,sig:06,src:001382,op:havoc,rep:128 => test19} (100%) rename test/data/cbor_regression/{id:000002,sig:06,src:001413+001036,op:splice,rep:4 => test20} (100%) rename test/data/cbor_regression/{id:000003,sig:06,src:000846+000155,op:splice,rep:16 => test21} (100%) diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2 b/test/data/cbor_regression/test01 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2 rename to test/data/cbor_regression/test01 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000787,op:havoc,rep:8 b/test/data/cbor_regression/test02 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:000787,op:havoc,rep:8 rename to test/data/cbor_regression/test02 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000833,op:havoc,rep:32 b/test/data/cbor_regression/test03 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:000833,op:havoc,rep:32 rename to test/data/cbor_regression/test03 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000838,op:havoc,rep:64 b/test/data/cbor_regression/test04 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:000838,op:havoc,rep:64 rename to test/data/cbor_regression/test04 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000846+001064,op:splice,rep:16 b/test/data/cbor_regression/test05 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:000846+001064,op:splice,rep:16 rename to test/data/cbor_regression/test05 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000848,op:flip1,pos:0 b/test/data/cbor_regression/test06 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:000848,op:flip1,pos:0 rename to test/data/cbor_regression/test06 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:001435,op:havoc,rep:32 b/test/data/cbor_regression/test07 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:001435,op:havoc,rep:32 rename to test/data/cbor_regression/test07 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:001436,op:havoc,rep:4 b/test/data/cbor_regression/test08 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:001436,op:havoc,rep:4 rename to test/data/cbor_regression/test08 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:000864+000903,op:splice,rep:2 b/test/data/cbor_regression/test09 similarity index 100% rename from test/data/cbor_regression/id:000001,sig:06,src:000864+000903,op:splice,rep:2 rename to test/data/cbor_regression/test09 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32 b/test/data/cbor_regression/test10 similarity index 100% rename from test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32 rename to test/data/cbor_regression/test10 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001330+000569,op:splice,rep:64 b/test/data/cbor_regression/test11 similarity index 100% rename from test/data/cbor_regression/id:000001,sig:06,src:001330+000569,op:splice,rep:64 rename to test/data/cbor_regression/test11 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001413,op:havoc,rep:32 b/test/data/cbor_regression/test12 similarity index 100% rename from test/data/cbor_regression/id:000001,sig:06,src:001413,op:havoc,rep:32 rename to test/data/cbor_regression/test12 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001447,op:havoc,rep:4 b/test/data/cbor_regression/test13 similarity index 100% rename from test/data/cbor_regression/id:000001,sig:06,src:001447,op:havoc,rep:4 rename to test/data/cbor_regression/test13 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001465+000325,op:splice,rep:4 b/test/data/cbor_regression/test14 similarity index 100% rename from test/data/cbor_regression/id:000001,sig:06,src:001465+000325,op:splice,rep:4 rename to test/data/cbor_regression/test14 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:000539,op:havoc,rep:8 b/test/data/cbor_regression/test15 similarity index 100% rename from test/data/cbor_regression/id:000002,sig:06,src:000539,op:havoc,rep:8 rename to test/data/cbor_regression/test15 diff --git a/test/data/cbor_regression/id:000004,sig:06,src:001445,op:havoc,rep:128 b/test/data/cbor_regression/test16 similarity index 100% rename from test/data/cbor_regression/id:000004,sig:06,src:001445,op:havoc,rep:128 rename to test/data/cbor_regression/test16 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001301,op:havoc,rep:16 b/test/data/cbor_regression/test17 similarity index 100% rename from test/data/cbor_regression/id:000002,sig:06,src:001301,op:havoc,rep:16 rename to test/data/cbor_regression/test17 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001317+000850,op:splice,rep:8 b/test/data/cbor_regression/test18 similarity index 100% rename from test/data/cbor_regression/id:000002,sig:06,src:001317+000850,op:splice,rep:8 rename to test/data/cbor_regression/test18 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001382,op:havoc,rep:128 b/test/data/cbor_regression/test19 similarity index 100% rename from test/data/cbor_regression/id:000002,sig:06,src:001382,op:havoc,rep:128 rename to test/data/cbor_regression/test19 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001413+001036,op:splice,rep:4 b/test/data/cbor_regression/test20 similarity index 100% rename from test/data/cbor_regression/id:000002,sig:06,src:001413+001036,op:splice,rep:4 rename to test/data/cbor_regression/test20 diff --git a/test/data/cbor_regression/id:000003,sig:06,src:000846+000155,op:splice,rep:16 b/test/data/cbor_regression/test21 similarity index 100% rename from test/data/cbor_regression/id:000003,sig:06,src:000846+000155,op:splice,rep:16 rename to test/data/cbor_regression/test21 diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 5769ac98..9c41dc2f 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -1197,27 +1197,27 @@ TEST_CASE("CBOR regressions") */ for (std::string filename : { - "test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2", - "test/data/cbor_regression/id:000000,sig:06,src:000787,op:havoc,rep:8", - "test/data/cbor_regression/id:000000,sig:06,src:000833,op:havoc,rep:32", - "test/data/cbor_regression/id:000000,sig:06,src:000838,op:havoc,rep:64", - "test/data/cbor_regression/id:000000,sig:06,src:000846+001064,op:splice,rep:16", - "test/data/cbor_regression/id:000000,sig:06,src:000848,op:flip1,pos:0", - "test/data/cbor_regression/id:000000,sig:06,src:001435,op:havoc,rep:32", - "test/data/cbor_regression/id:000000,sig:06,src:001436,op:havoc,rep:4v", - "test/data/cbor_regression/id:000001,sig:06,src:000864+000903,op:splice,rep:2", - "test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32", - "test/data/cbor_regression/id:000001,sig:06,src:001330+000569,op:splice,rep:64", - "test/data/cbor_regression/id:000001,sig:06,src:001413,op:havoc,rep:32", - "test/data/cbor_regression/id:000001,sig:06,src:001447,op:havoc,rep:4", - "test/data/cbor_regression/id:000001,sig:06,src:001465+000325,op:splice,rep:4", - "test/data/cbor_regression/id:000002,sig:06,src:000539,op:havoc,rep:8", - "test/data/cbor_regression/id:000002,sig:06,src:001301,op:havoc,rep:16", - "test/data/cbor_regression/id:000002,sig:06,src:001317+000850,op:splice,rep:8", - "test/data/cbor_regression/id:000002,sig:06,src:001382,op:havoc,rep:128", - "test/data/cbor_regression/id:000002,sig:06,src:001413+001036,op:splice,rep:4", - "test/data/cbor_regression/id:000003,sig:06,src:000846+000155,op:splice,rep:16", - "test/data/cbor_regression/id:000004,sig:06,src:001445,op:havoc,rep:128" + "test/data/cbor_regression/test01", + "test/data/cbor_regression/test02", + "test/data/cbor_regression/test03", + "test/data/cbor_regression/test04", + "test/data/cbor_regression/test05", + "test/data/cbor_regression/test06", + "test/data/cbor_regression/test07", + "test/data/cbor_regression/test08", + "test/data/cbor_regression/test09", + "test/data/cbor_regression/test10", + "test/data/cbor_regression/test11", + "test/data/cbor_regression/test12", + "test/data/cbor_regression/test13", + "test/data/cbor_regression/test14", + "test/data/cbor_regression/test15", + "test/data/cbor_regression/test16", + "test/data/cbor_regression/test17", + "test/data/cbor_regression/test18", + "test/data/cbor_regression/test19", + "test/data/cbor_regression/test20", + "test/data/cbor_regression/test21" }) { CAPTURE(filename); From 8381cd6020c57db59cf359ba4384424b93e51018 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 28 Dec 2016 14:19:08 +0100 Subject: [PATCH 18/35] :ambulance: removed unsafe call to strerror #403 --- src/json.hpp | 2 +- src/json.hpp.re2c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 67d05cf2..4515ca67 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -9077,7 +9077,7 @@ class basic_json // immediately abort if stream is erroneous if (s.fail()) { - throw std::invalid_argument("stream error: " + std::string(strerror(errno))); + throw std::invalid_argument("stream error"); } // fill buffer diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index cbd8b220..6d649db3 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -9077,7 +9077,7 @@ class basic_json // immediately abort if stream is erroneous if (s.fail()) { - throw std::invalid_argument("stream error: " + std::string(strerror(errno))); + throw std::invalid_argument("stream error"); } // fill buffer From 871cebaf84d4f896bc730a92937f8d02e09b0023 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 29 Dec 2016 15:39:16 +0100 Subject: [PATCH 19/35] :ambulance: fix for #405 --- src/json.hpp | 6 ++++++ src/json.hpp.re2c | 6 ++++++ test/src/unit-regression.cpp | 7 +++++++ 3 files changed, 19 insertions(+) diff --git a/src/json.hpp b/src/json.hpp index 4515ca67..76824cf8 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6871,6 +6871,12 @@ class basic_json { throw std::out_of_range("len+offset out of range"); } + + // last case: reading past the end of the vector + if (len + offset > size) + { + throw std::out_of_range("len+offset out of range"); + } } /*! diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 6d649db3..10bfaf57 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6871,6 +6871,12 @@ class basic_json { throw std::out_of_range("len+offset out of range"); } + + // last case: reading past the end of the vector + if (len + offset > size) + { + throw std::out_of_range("len+offset out of range"); + } } /*! diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 1e720ddb..033041ab 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -540,4 +540,11 @@ TEST_CASE("regression tests") CHECK(j.is_number_float()); CHECK(j.dump() == "1.66020696663386e+20"); } + + SECTION("issue #405 - Heap-buffer-overflow (OSS-Fuzz issue 342)") + { + // original test case + std::vector vec {0x65, 0xf5, 0x0a, 0x48, 0x21}; + CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range); + } } From 383a29a924af973e4cf9d5d2054a60f7d718f0d3 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 29 Dec 2016 16:14:15 +0100 Subject: [PATCH 20/35] :ambulance: fix for #407 --- src/json.hpp | 5 +++++ src/json.hpp.re2c | 5 +++++ test/src/unit-regression.cpp | 23 +++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/json.hpp b/src/json.hpp index 76824cf8..aab3b34f 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6981,6 +6981,7 @@ class basic_json case 0xca: // float 32 { // copy bytes in reverse order into the double variable + check_length(v.size(), sizeof(float), 1); float res; for (size_t byte = 0; byte < sizeof(float); ++byte) { @@ -6993,6 +6994,7 @@ class basic_json case 0xcb: // float 64 { // copy bytes in reverse order into the double variable + check_length(v.size(), sizeof(double), 1); double res; for (size_t byte = 0; byte < sizeof(double); ++byte) { @@ -7558,6 +7560,7 @@ class basic_json case 0xf9: // Half-Precision Float (two-byte IEEE 754) { + check_length(v.size(), 2, 1); idx += 2; // skip two content bytes // code from RFC 7049, Appendix D, Figure 3: @@ -7589,6 +7592,7 @@ class basic_json case 0xfa: // Single-Precision Float (four-byte IEEE 754) { // copy bytes in reverse order into the float variable + check_length(v.size(), sizeof(float), 1); float res; for (size_t byte = 0; byte < sizeof(float); ++byte) { @@ -7600,6 +7604,7 @@ class basic_json case 0xfb: // Double-Precision Float (eight-byte IEEE 754) { + check_length(v.size(), sizeof(double), 1); // copy bytes in reverse order into the double variable double res; for (size_t byte = 0; byte < sizeof(double); ++byte) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 10bfaf57..7b6b7ec4 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6981,6 +6981,7 @@ class basic_json case 0xca: // float 32 { // copy bytes in reverse order into the double variable + check_length(v.size(), sizeof(float), 1); float res; for (size_t byte = 0; byte < sizeof(float); ++byte) { @@ -6993,6 +6994,7 @@ class basic_json case 0xcb: // float 64 { // copy bytes in reverse order into the double variable + check_length(v.size(), sizeof(double), 1); double res; for (size_t byte = 0; byte < sizeof(double); ++byte) { @@ -7558,6 +7560,7 @@ class basic_json case 0xf9: // Half-Precision Float (two-byte IEEE 754) { + check_length(v.size(), 2, 1); idx += 2; // skip two content bytes // code from RFC 7049, Appendix D, Figure 3: @@ -7589,6 +7592,7 @@ class basic_json case 0xfa: // Single-Precision Float (four-byte IEEE 754) { // copy bytes in reverse order into the float variable + check_length(v.size(), sizeof(float), 1); float res; for (size_t byte = 0; byte < sizeof(float); ++byte) { @@ -7600,6 +7604,7 @@ class basic_json case 0xfb: // Double-Precision Float (eight-byte IEEE 754) { + check_length(v.size(), sizeof(double), 1); // copy bytes in reverse order into the double variable double res; for (size_t byte = 0; byte < sizeof(double); ++byte) diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 033041ab..6123352d 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -547,4 +547,27 @@ TEST_CASE("regression tests") std::vector vec {0x65, 0xf5, 0x0a, 0x48, 0x21}; CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range); } + + SECTION("issue #407 - Heap-buffer-overflow (OSS-Fuzz issue 343)") + { + // original test case: incomplete float64 + std::vector vec1 {0xcb, 0x8f, 0x0a}; + CHECK_THROWS_AS(json::from_msgpack(vec1), std::out_of_range); + + // related test case: incomplete float32 + std::vector vec2 {0xca, 0x8f, 0x0a}; + CHECK_THROWS_AS(json::from_msgpack(vec2), std::out_of_range); + + // related test case: incomplete Half-Precision Float (CBOR) + std::vector vec3 {0xf9, 0x8f}; + CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range); + + // related test case: incomplete Single-Precision Float (CBOR) + std::vector vec4 {0xfa, 0x8f, 0x0a}; + CHECK_THROWS_AS(json::from_cbor(vec4), std::out_of_range); + + // related test case: incomplete Double-Precision Float (CBOR) + std::vector vec5 {0xfb, 0x8f, 0x0a}; + CHECK_THROWS_AS(json::from_cbor(vec5), std::out_of_range); + } } From f0edab2363ae7dd50ee04bfacdb56f47ed6e7fb7 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 29 Dec 2016 17:00:02 +0100 Subject: [PATCH 21/35] :ambulance: fix for #408 --- src/json.hpp | 37 ++++++--------------------------- src/json.hpp.re2c | 37 ++++++--------------------------- test/src/unit-regression.cpp | 40 ++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 62 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index aab3b34f..f8d948f1 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6879,27 +6879,6 @@ class basic_json } } - /*! - @brief checks if a given length does not exceed the size of a given vector - - To secure the access to the byte vector during CBOR/MessagePack - deserialization, bytes are copied from the vector into buffers. This - function checks if the number of bytes to copy (@a len) does not exceed the - size of the given vector @a vec. - - @param[in] vec byte vector - @param[in] len length - - @throws out_of_range if `len > v.size()` - */ - static void check_length(const std::vector& vec, const size_t& len) - { - if (len > vec.size()) - { - throw std::out_of_range("len out of range"); - } - } - /*! @brief create a JSON value from a given MessagePack vector @@ -6916,6 +6895,9 @@ class basic_json */ static basic_json from_msgpack_internal(const std::vector& v, size_t& idx) { + // make sure reading 1 byte is safe + check_length(v.size(), 1, idx); + // store and increment index const size_t current_idx = idx++; @@ -7153,6 +7135,9 @@ class basic_json */ static basic_json from_cbor_internal(const std::vector& v, size_t& idx) { + // make sure reading 1 byte is safe + check_length(v.size(), 1, idx); + // store and increment index const size_t current_idx = idx++; @@ -7674,11 +7659,6 @@ class basic_json */ static basic_json from_msgpack(const std::vector& v) { - if (v.empty()) - { - throw std::invalid_argument("empty vector"); - } - size_t i = 0; return from_msgpack_internal(v, i); } @@ -7736,11 +7716,6 @@ class basic_json */ static basic_json from_cbor(const std::vector& v) { - if (v.empty()) - { - throw std::invalid_argument("empty vector"); - } - size_t i = 0; return from_cbor_internal(v, i); } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 7b6b7ec4..2faf3858 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6879,27 +6879,6 @@ class basic_json } } - /*! - @brief checks if a given length does not exceed the size of a given vector - - To secure the access to the byte vector during CBOR/MessagePack - deserialization, bytes are copied from the vector into buffers. This - function checks if the number of bytes to copy (@a len) does not exceed the - size of the given vector @a vec. - - @param[in] vec byte vector - @param[in] len length - - @throws out_of_range if `len > v.size()` - */ - static void check_length(const std::vector& vec, const size_t& len) - { - if (len > vec.size()) - { - throw std::out_of_range("len out of range"); - } - } - /*! @brief create a JSON value from a given MessagePack vector @@ -6916,6 +6895,9 @@ class basic_json */ static basic_json from_msgpack_internal(const std::vector& v, size_t& idx) { + // make sure reading 1 byte is safe + check_length(v.size(), 1, idx); + // store and increment index const size_t current_idx = idx++; @@ -7153,6 +7135,9 @@ class basic_json */ static basic_json from_cbor_internal(const std::vector& v, size_t& idx) { + // make sure reading 1 byte is safe + check_length(v.size(), 1, idx); + // store and increment index const size_t current_idx = idx++; @@ -7674,11 +7659,6 @@ class basic_json */ static basic_json from_msgpack(const std::vector& v) { - if (v.empty()) - { - throw std::invalid_argument("empty vector"); - } - size_t i = 0; return from_msgpack_internal(v, i); } @@ -7736,11 +7716,6 @@ class basic_json */ static basic_json from_cbor(const std::vector& v) { - if (v.empty()) - { - throw std::invalid_argument("empty vector"); - } - size_t i = 0; return from_cbor_internal(v, i); } diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 6123352d..421a386c 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -570,4 +570,44 @@ TEST_CASE("regression tests") std::vector vec5 {0xfb, 0x8f, 0x0a}; CHECK_THROWS_AS(json::from_cbor(vec5), std::out_of_range); } + + SECTION("issue #408 - Heap-buffer-overflow (OSS-Fuzz issue 344)") + { + // original test case + std::vector vec1 {0x87}; + CHECK_THROWS_AS(json::from_msgpack(vec1), std::out_of_range); + + // more test cases for MessagePack + for (uint8_t b : + { + 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, // fixmap + 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, // fixarray + 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, // fixstr + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf + }) + { + std::vector vec(1, b); + CHECK_THROWS_AS(json::from_msgpack(vec), std::out_of_range); + } + + // more test cases for CBOR + for (uint8_t b : + { + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // UTF-8 string + 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, // array + 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7 // map + }) + { + std::vector vec(1, b); + CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range); + } + + // special case: empty input + std::vector vec2; + CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range); + CHECK_THROWS_AS(json::from_msgpack(vec2), std::out_of_range); + } } From d7029c37aad0e93033242123e7a7a05a1c76bed4 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 30 Dec 2016 13:04:33 +0100 Subject: [PATCH 22/35] :white_check_mark: improved test coverage --- test/src/unit-cbor.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 9c41dc2f..49a54999 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -28,6 +28,7 @@ SOFTWARE. #include "catch.hpp" +#define private public #include "json.hpp" using nlohmann::json; @@ -1262,6 +1263,12 @@ TEST_CASE("CBOR regressions") } } } + + SECTION("improve code coverage") + { + // exotic edge case + CHECK_THROWS_AS(json::check_length(0xffffffffffffffff, 0xfffffffffffffff0, 0xff), std::out_of_range); + } } TEST_CASE("CBOR roundtrips", "[hide]") From 010ea126f3662c76e2d484e9710be2b8fcd53785 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Sun, 25 Dec 2016 14:04:04 +0200 Subject: [PATCH 23/35] going to try clang_sanitize with libstdc++ with g++-6 implementation Conflicts: .travis.yml Makefile --- .travis.yml | 22 ++++++++++++++-------- Makefile | 3 ++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 006647ed..6bf2297e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,14 +42,20 @@ matrix: - make check TEST_PREFIX="valgrind --error-exitcode=1 --leak-check=full " TEST_PATTERN="" # cLang sanitizer - - #- os: linux - # env: - # - LLVM_VERSION=3.8.1 - # - SPECIAL=sanitizer - # compiler: clang - # before_script: - # - make clang_sanitize + # note: sadly clang's libc++ has errors when running with sanitize, + # so we use clang with gcc's libstdc++ which doesn't give those error. + # that's why we need to install g++-6 to get the lastest version + - os: linux + env: + - LLVM_VERSION=3.8.1 + - SPECIAL=sanitizer + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: g++-6 + compiler: clang + before_script: + - make clang_sanitize # cppcheck diff --git a/Makefile b/Makefile index e2c120db..c16d9d9b 100644 --- a/Makefile +++ b/Makefile @@ -92,8 +92,9 @@ fuzzing-stop: cppcheck: cppcheck --enable=warning --inconclusive --force --std=c++11 src/json.hpp --error-exitcode=1 +# run clang sanitize (we are overrding the CXXFLAGS provided by travis in order to use gcc's libstdc++) clang_sanitize: clean - CXX=clang++ CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" $(MAKE) + CXX=clang++ CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" $(MAKE) check -C test ########################################################################## From ff3221a37573e3313d52d46fd3b94ec658ff66e8 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Sun, 25 Dec 2016 22:52:37 +0200 Subject: [PATCH 24/35] #394 fixed memory leak in unit-allocator, found by clang's fsanitize --- test/src/unit-allocator.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index 25fd3349..c439c1c3 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -111,6 +111,16 @@ struct my_allocator : std::allocator } }; +// allows deletion of raw pointer, usually hold by json_value +template +void my_allocator_clean_up(T* p) +{ + assert(p != nullptr); + my_allocator alloc; + alloc.destroy(p); + alloc.deallocate(p, 1); +} + TEST_CASE("controlled bad_alloc") { // create JSON type using the throwing allocator @@ -131,7 +141,8 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; auto t = my_json::value_t::object; - CHECK_NOTHROW(my_json::json_value j(t)); + auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.object); }; + CHECK_NOTHROW(my_json::json_value j(t); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); next_construct_fails = false; @@ -140,7 +151,8 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; auto t = my_json::value_t::array; - CHECK_NOTHROW(my_json::json_value j(t)); + auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.array); }; + CHECK_NOTHROW(my_json::json_value j(t); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); next_construct_fails = false; @@ -149,7 +161,8 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; auto t = my_json::value_t::string; - CHECK_NOTHROW(my_json::json_value j(t)); + auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.string); }; + CHECK_NOTHROW(my_json::json_value j(t); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); next_construct_fails = false; @@ -160,7 +173,8 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; my_json::string_t v("foo"); - CHECK_NOTHROW(my_json::json_value j(v)); + auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.string); }; + CHECK_NOTHROW(my_json::json_value j(v); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); next_construct_fails = false; From cf9bf2d9136a9d1c2131f83aa493bb03f36849ab Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 1 Jan 2017 15:28:01 +0100 Subject: [PATCH 25/35] :ambulance: fix for #411 and #412 --- src/json.hpp | 11 +++----- src/json.hpp.re2c | 11 +++----- test/src/unit-regression.cpp | 53 ++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index f8d948f1..5dc8e67c 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -7135,13 +7135,10 @@ class basic_json */ static basic_json from_cbor_internal(const std::vector& v, size_t& idx) { - // make sure reading 1 byte is safe - check_length(v.size(), 1, idx); - // store and increment index const size_t current_idx = idx++; - switch (v[current_idx]) + switch (v.at(current_idx)) { // Integer 0x00..0x17 (0..23) case 0x00: @@ -7322,7 +7319,7 @@ class basic_json case 0x7f: // UTF-8 string (indefinite length) { std::string result; - while (v[idx] != 0xff) + while (v.at(idx) != 0xff) { string_t s = from_cbor_internal(v, idx); result += s; @@ -7418,7 +7415,7 @@ class basic_json case 0x9f: // array (indefinite length) { basic_json result = value_t::array; - while (v[idx] != 0xff) + while (v.at(idx) != 0xff) { result.push_back(from_cbor_internal(v, idx)); } @@ -7518,7 +7515,7 @@ class basic_json case 0xbf: // map (indefinite length) { basic_json result = value_t::object; - while (v[idx] != 0xff) + while (v.at(idx) != 0xff) { std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 2faf3858..49bccb02 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -7135,13 +7135,10 @@ class basic_json */ static basic_json from_cbor_internal(const std::vector& v, size_t& idx) { - // make sure reading 1 byte is safe - check_length(v.size(), 1, idx); - // store and increment index const size_t current_idx = idx++; - switch (v[current_idx]) + switch (v.at(current_idx)) { // Integer 0x00..0x17 (0..23) case 0x00: @@ -7322,7 +7319,7 @@ class basic_json case 0x7f: // UTF-8 string (indefinite length) { std::string result; - while (v[idx] != 0xff) + while (v.at(idx) != 0xff) { string_t s = from_cbor_internal(v, idx); result += s; @@ -7418,7 +7415,7 @@ class basic_json case 0x9f: // array (indefinite length) { basic_json result = value_t::array; - while (v[idx] != 0xff) + while (v.at(idx) != 0xff) { result.push_back(from_cbor_internal(v, idx)); } @@ -7518,7 +7515,7 @@ class basic_json case 0xbf: // map (indefinite length) { basic_json result = value_t::object; - while (v[idx] != 0xff) + while (v.at(idx) != 0xff) { std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 421a386c..89b27e0e 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -610,4 +610,57 @@ TEST_CASE("regression tests") CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range); CHECK_THROWS_AS(json::from_msgpack(vec2), std::out_of_range); } + + SECTION("issue #411 - Heap-buffer-overflow (OSS-Fuzz issue 366)") + { + // original test case: empty UTF-8 string (indefinite length) + std::vector vec1 {0x7f}; + CHECK_THROWS_AS(json::from_cbor(vec1), std::out_of_range); + + // related test case: empty array (indefinite length) + std::vector vec2 {0x9f}; + CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range); + + // related test case: empty map (indefinite length) + std::vector vec3 {0xbf}; + CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range); + } + + SECTION("issue #412 - Heap-buffer-overflow (OSS-Fuzz issue 367)") + { + // original test case + std::vector vec + { + 0xab, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x00, 0x00, 0x00, + 0x60, 0xab, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x00, 0x00, 0x00, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xa0, 0x9f, + 0x9f, 0x97, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60 + }; + CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range); + + // related test case: nonempty UTF-8 string (indefinite length) + std::vector vec1 {0x7f, 0x61, 0x61}; + CHECK_THROWS_AS(json::from_cbor(vec1), std::out_of_range); + + // related test case: nonempty array (indefinite length) + std::vector vec2 {0x9f, 0x01}; + CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range); + + // related test case: nonempty map (indefinite length) + std::vector vec3 {0xbf, 0x61, 0x61, 0x01}; + CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range); + } } From ab7d55e7ae7c9fab666912876aaa9d9e20d1b618 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 1 Jan 2017 19:38:54 +0100 Subject: [PATCH 26/35] :construction_worker: removed failing test suites --- .doozer.json | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/.doozer.json b/.doozer.json index 589dccc3..7dbaa5d2 100644 --- a/.doozer.json +++ b/.doozer.json @@ -1,30 +1,5 @@ { "targets": { - "jessie-i386": { - "buildenv": "jessie-i386", - "builddeps": ["build-essential", "cmake", "clang"], - "buildcmd": ["mkdir cm", "cd cm", "CXX=clang++ cmake ..", "cmake --build .", "ctest --output-on-failure"] - }, - "precise-i386": { - "buildenv": "precise-i386", - "builddeps": ["build-essential", "cmake"], - "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] - }, - "precise-amd64": { - "buildenv": "precise-amd64", - "builddeps": ["build-essential", "cmake"], - "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] - }, - "trusty-i386": { - "buildenv": "trusty-i386", - "builddeps": ["build-essential", "cmake", "clang"], - "buildcmd": ["make check CXX=clang++"] - }, - "trusty-amd64": { - "buildenv": "trusty-amd64", - "builddeps": ["build-essential", "cmake", "clang"], - "buildcmd": ["make check CXX=clang++"] - }, "xenial-i386": { "buildenv": "xenial-i386", "builddeps": ["build-essential", "cmake"], @@ -40,11 +15,6 @@ "builddeps": ["cmake", "make", "clang"], "buildcmd": ["mkdir cm", "cd cm", "CXX=clang++ cmake ..", "cmake --build .", "ctest --output-on-failure"] }, - "centos7-x86_64": { - "buildenv": "centos7-x86_64", - "builddeps": ["build-essential", "cmake"], - "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] - }, "osx": { "buildenv": "osx", "builddeps": ["build-essential"], From db33629990c0125bde0fb61e894e00496e1e22b0 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 1 Jan 2017 21:34:58 +0100 Subject: [PATCH 27/35] :lipstick: cleanup after #410 --- Makefile | 2 +- README.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c16d9d9b..56cbdb0c 100644 --- a/Makefile +++ b/Makefile @@ -94,7 +94,7 @@ cppcheck: # run clang sanitize (we are overrding the CXXFLAGS provided by travis in order to use gcc's libstdc++) clang_sanitize: clean - CXX=clang++ CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" $(MAKE) check -C test + CXX=clang++ CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" $(MAKE) ########################################################################## diff --git a/README.md b/README.md index 16c7eabf..06dca7ff 100644 --- a/README.md +++ b/README.md @@ -586,6 +586,7 @@ I deeply appreciate the help of the following people. - [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning. - [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check. - [Bosswestfalen](https://github.com/Bosswestfalen) merged two iterator classes into a smaller one. +- [Daniel599](https://github.com/Daniel599) helped to get Travis execute the tests with Clang's sanitizers. Thanks a lot for helping out! From a3063c24194df831cc95b6429a648ea3a6739fcf Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 1 Jan 2017 21:51:36 +0100 Subject: [PATCH 28/35] :construction: added target to build all fuzzers --- .gitignore | 6 ++++++ test/Makefile | 2 ++ 2 files changed, 8 insertions(+) diff --git a/.gitignore b/.gitignore index eedc9641..6e15abc4 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,9 @@ test/test-* .svn test/thirdparty/Fuzzer/libFuzzer.a + +test/parse_afl_fuzzer + +test/parse_cbor_fuzzer + +test/parse_msgpack_fuzzer diff --git a/test/Makefile b/test/Makefile index 556ab0d7..68520bd3 100644 --- a/test/Makefile +++ b/test/Makefile @@ -84,6 +84,8 @@ check: $(TESTCASES) # fuzzer ############################################################################## +fuzzers: parse_afl_fuzzer parse_cbor_fuzzer parse_msgpack_fuzzer + parse_afl_fuzzer: $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_json.cpp -o $@ From d1735172127c37bfee7a816d96d34647cfff6494 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 2 Jan 2017 09:21:26 +0100 Subject: [PATCH 29/35] :lipstick: clean up --- src/json.hpp | 9 +++++++++ src/json.hpp.re2c | 9 +++++++++ test/src/unit-allocator.cpp | 20 ++++++++++++++++---- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 5dc8e67c..afa35baa 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -873,8 +873,17 @@ class basic_json break; } + case value_t::null: + { + break; + } + default: { + if (t == value_t::null) + { + throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21"); // LCOV_EXCL_LINE + } break; } } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 49bccb02..db0b4c88 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -873,8 +873,17 @@ class basic_json break; } + case value_t::null: + { + break; + } + default: { + if (t == value_t::null) + { + throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21"); // LCOV_EXCL_LINE + } break; } } diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index c439c1c3..04f6ac9d 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -141,7 +141,10 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; auto t = my_json::value_t::object; - auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.object); }; + auto clean_up = [](my_json::json_value & j) + { + my_allocator_clean_up(j.object); + }; CHECK_NOTHROW(my_json::json_value j(t); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); @@ -151,7 +154,10 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; auto t = my_json::value_t::array; - auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.array); }; + auto clean_up = [](my_json::json_value & j) + { + my_allocator_clean_up(j.array); + }; CHECK_NOTHROW(my_json::json_value j(t); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); @@ -161,7 +167,10 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; auto t = my_json::value_t::string; - auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.string); }; + auto clean_up = [](my_json::json_value & j) + { + my_allocator_clean_up(j.string); + }; CHECK_NOTHROW(my_json::json_value j(t); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); @@ -173,7 +182,10 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; my_json::string_t v("foo"); - auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.string); }; + auto clean_up = [](my_json::json_value & j) + { + my_allocator_clean_up(j.string); + }; CHECK_NOTHROW(my_json::json_value j(v); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); From 60b3703c62bdc3740ca7564bd8c28abb5337a2b4 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 2 Jan 2017 09:35:57 +0100 Subject: [PATCH 30/35] :bookmark: version bump to 2.0.10 --- CMakeLists.txt | 2 +- doc/Doxyfile | 2 +- doc/index.md | 2 +- src/json.hpp | 4 ++-- src/json.hpp.re2c | 4 ++-- test/src/fuzzer-driver_afl.cpp | 2 +- test/src/fuzzer-parse_cbor.cpp | 2 +- test/src/fuzzer-parse_json.cpp | 2 +- test/src/fuzzer-parse_msgpack.cpp | 2 +- test/src/unit-algorithms.cpp | 2 +- test/src/unit-allocator.cpp | 2 +- test/src/unit-capacity.cpp | 2 +- test/src/unit-cbor.cpp | 2 +- test/src/unit-class_const_iterator.cpp | 2 +- test/src/unit-class_iterator.cpp | 2 +- test/src/unit-class_lexer.cpp | 2 +- test/src/unit-class_parser.cpp | 2 +- test/src/unit-comparison.cpp | 2 +- test/src/unit-concepts.cpp | 2 +- test/src/unit-constructor1.cpp | 2 +- test/src/unit-constructor2.cpp | 2 +- test/src/unit-convenience.cpp | 2 +- test/src/unit-conversions.cpp | 2 +- test/src/unit-deserialization.cpp | 2 +- test/src/unit-element_access1.cpp | 2 +- test/src/unit-element_access2.cpp | 2 +- test/src/unit-inspection.cpp | 2 +- test/src/unit-iterator_wrapper.cpp | 2 +- test/src/unit-iterators1.cpp | 2 +- test/src/unit-iterators2.cpp | 2 +- test/src/unit-json_patch.cpp | 2 +- test/src/unit-json_pointer.cpp | 2 +- test/src/unit-modifiers.cpp | 2 +- test/src/unit-msgpack.cpp | 2 +- test/src/unit-pointer_access.cpp | 2 +- test/src/unit-readme.cpp | 2 +- test/src/unit-reference_access.cpp | 2 +- test/src/unit-regression.cpp | 2 +- test/src/unit-serialization.cpp | 2 +- test/src/unit-testsuites.cpp | 2 +- test/src/unit-unicode.cpp | 2 +- test/src/unit.cpp | 2 +- 42 files changed, 44 insertions(+), 44 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 80770367..9f9931c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.0) # define the project -project(nlohmann_json VERSION 2.0.9 LANGUAGES CXX) +project(nlohmann_json VERSION 2.0.10 LANGUAGES CXX) enable_testing() diff --git a/doc/Doxyfile b/doc/Doxyfile index 17c81d8b..5064a0a0 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "JSON for Modern C++" -PROJECT_NUMBER = 2.0.9 +PROJECT_NUMBER = 2.0.10 PROJECT_BRIEF = PROJECT_LOGO = OUTPUT_DIRECTORY = . diff --git a/doc/index.md b/doc/index.md index 8292fe65..cc5cd793 100644 --- a/doc/index.md +++ b/doc/index.md @@ -277,4 +277,4 @@ The container functions known from STL have been extended to support the differe @author [Niels Lohmann](http://nlohmann.me) @see https://github.com/nlohmann/json to download the source code -@version 2.0.9 +@version 2.0.10 diff --git a/src/json.hpp b/src/json.hpp index afa35baa..471b9ec7 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . @@ -882,7 +882,7 @@ class basic_json { if (t == value_t::null) { - throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21"); // LCOV_EXCL_LINE + throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.0.10"); // LCOV_EXCL_LINE } break; } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index db0b4c88..9a7c2043 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . @@ -882,7 +882,7 @@ class basic_json { if (t == value_t::null) { - throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21"); // LCOV_EXCL_LINE + throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.0.10"); // LCOV_EXCL_LINE } break; } diff --git a/test/src/fuzzer-driver_afl.cpp b/test/src/fuzzer-driver_afl.cpp index eddeae41..0c173b45 100644 --- a/test/src/fuzzer-driver_afl.cpp +++ b/test/src/fuzzer-driver_afl.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (fuzz test support) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json This file implements a driver for American Fuzzy Lop (afl-fuzz). It relies on diff --git a/test/src/fuzzer-parse_cbor.cpp b/test/src/fuzzer-parse_cbor.cpp index 42ce679c..bba56747 100644 --- a/test/src/fuzzer-parse_cbor.cpp +++ b/test/src/fuzzer-parse_cbor.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (fuzz test support) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json This file implements a parser test suitable for fuzz testing. Given a byte diff --git a/test/src/fuzzer-parse_json.cpp b/test/src/fuzzer-parse_json.cpp index 51ac440d..f61df56d 100644 --- a/test/src/fuzzer-parse_json.cpp +++ b/test/src/fuzzer-parse_json.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (fuzz test support) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json This file implements a parser test suitable for fuzz testing. Given a byte diff --git a/test/src/fuzzer-parse_msgpack.cpp b/test/src/fuzzer-parse_msgpack.cpp index 992697c2..0355db3b 100644 --- a/test/src/fuzzer-parse_msgpack.cpp +++ b/test/src/fuzzer-parse_msgpack.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (fuzz test support) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json This file implements a parser test suitable for fuzz testing. Given a byte diff --git a/test/src/unit-algorithms.cpp b/test/src/unit-algorithms.cpp index 31be6556..04704847 100644 --- a/test/src/unit-algorithms.cpp +++ b/test/src/unit-algorithms.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index 04f6ac9d..00eb5b9a 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-capacity.cpp b/test/src/unit-capacity.cpp index 7fc3d49c..4664f1bf 100644 --- a/test/src/unit-capacity.cpp +++ b/test/src/unit-capacity.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 49a54999..d01019d9 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp index 2970b865..2097e830 100644 --- a/test/src/unit-class_const_iterator.cpp +++ b/test/src/unit-class_const_iterator.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp index 14c6828b..bd64cbf8 100644 --- a/test/src/unit-class_iterator.cpp +++ b/test/src/unit-class_iterator.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 6dec3f82..5656942f 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 7bf23d50..3e11d80c 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-comparison.cpp b/test/src/unit-comparison.cpp index 73c31abc..81c3066d 100644 --- a/test/src/unit-comparison.cpp +++ b/test/src/unit-comparison.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-concepts.cpp b/test/src/unit-concepts.cpp index dea6b510..cf60b5c1 100644 --- a/test/src/unit-concepts.cpp +++ b/test/src/unit-concepts.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 23c0db38..114ae5b3 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-constructor2.cpp b/test/src/unit-constructor2.cpp index 0bbd13cf..ae5ba3b7 100644 --- a/test/src/unit-constructor2.cpp +++ b/test/src/unit-constructor2.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp index 94341be1..1aedea44 100644 --- a/test/src/unit-convenience.cpp +++ b/test/src/unit-convenience.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index 72c3bf60..77d476bd 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 605c596b..f0cf2e15 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-element_access1.cpp b/test/src/unit-element_access1.cpp index 0a1715e7..04265244 100644 --- a/test/src/unit-element_access1.cpp +++ b/test/src/unit-element_access1.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index 29be17a1..f2758a59 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp index 1660c719..1051d2f5 100644 --- a/test/src/unit-inspection.cpp +++ b/test/src/unit-inspection.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-iterator_wrapper.cpp b/test/src/unit-iterator_wrapper.cpp index 96928eb4..784fa77a 100644 --- a/test/src/unit-iterator_wrapper.cpp +++ b/test/src/unit-iterator_wrapper.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-iterators1.cpp b/test/src/unit-iterators1.cpp index 60f7c2fa..bf14435b 100644 --- a/test/src/unit-iterators1.cpp +++ b/test/src/unit-iterators1.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-iterators2.cpp b/test/src/unit-iterators2.cpp index da0fc937..5c04826d 100644 --- a/test/src/unit-iterators2.cpp +++ b/test/src/unit-iterators2.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp index 880bc46d..4983aab2 100644 --- a/test/src/unit-json_patch.cpp +++ b/test/src/unit-json_patch.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp index 91d003ab..4268c3c0 100644 --- a/test/src/unit-json_pointer.cpp +++ b/test/src/unit-json_pointer.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index 02ffa6a5..2db3f291 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index 46997e27..eab4dcac 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp index e1c9caab..f5d934d3 100644 --- a/test/src/unit-pointer_access.cpp +++ b/test/src/unit-pointer_access.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-readme.cpp b/test/src/unit-readme.cpp index 92b13222..bddcd0d6 100644 --- a/test/src/unit-readme.cpp +++ b/test/src/unit-readme.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-reference_access.cpp b/test/src/unit-reference_access.cpp index 4a8047f8..321907a5 100644 --- a/test/src/unit-reference_access.cpp +++ b/test/src/unit-reference_access.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 89b27e0e..0bfdc91a 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp index 8385a4a4..3375a1c4 100644 --- a/test/src/unit-serialization.cpp +++ b/test/src/unit-serialization.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-testsuites.cpp b/test/src/unit-testsuites.cpp index 92b6e5aa..dfc75936 100644 --- a/test/src/unit-testsuites.cpp +++ b/test/src/unit-testsuites.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-unicode.cpp b/test/src/unit-unicode.cpp index 4cb51e21..69f4a9ee 100644 --- a/test/src/unit-unicode.cpp +++ b/test/src/unit-unicode.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit.cpp b/test/src/unit.cpp index 1a278868..e7a7a799 100644 --- a/test/src/unit.cpp +++ b/test/src/unit.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . From 6a207900562cededec7189c548c86743c65902cd Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 2 Jan 2017 09:36:30 +0100 Subject: [PATCH 31/35] :memo: updated test count --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 06dca7ff..a0534615 100644 --- a/README.md +++ b/README.md @@ -610,7 +610,7 @@ To compile and run the tests, you need to execute $ make check =============================================================================== -All tests passed (11201893 assertions in 43 test cases) +All tests passed (11202040 assertions in 44 test cases) ``` Alternatively, you can use [CMake](https://cmake.org) and run From 8b46eb8ec0abd05535520fc25e2bdbd3a2c71820 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 2 Jan 2017 09:40:00 +0100 Subject: [PATCH 32/35] :page_facing_up: it's 2017 already --- LICENSE.MIT | 2 +- doc/index.md | 2 +- src/json.hpp | 2 +- src/json.hpp.re2c | 2 +- test/src/unit-algorithms.cpp | 2 +- test/src/unit-allocator.cpp | 2 +- test/src/unit-capacity.cpp | 2 +- test/src/unit-cbor.cpp | 2 +- test/src/unit-class_const_iterator.cpp | 2 +- test/src/unit-class_iterator.cpp | 2 +- test/src/unit-class_lexer.cpp | 2 +- test/src/unit-class_parser.cpp | 2 +- test/src/unit-comparison.cpp | 2 +- test/src/unit-concepts.cpp | 2 +- test/src/unit-constructor1.cpp | 2 +- test/src/unit-constructor2.cpp | 2 +- test/src/unit-convenience.cpp | 2 +- test/src/unit-conversions.cpp | 2 +- test/src/unit-deserialization.cpp | 2 +- test/src/unit-element_access1.cpp | 2 +- test/src/unit-element_access2.cpp | 2 +- test/src/unit-inspection.cpp | 2 +- test/src/unit-iterator_wrapper.cpp | 2 +- test/src/unit-iterators1.cpp | 2 +- test/src/unit-iterators2.cpp | 2 +- test/src/unit-json_patch.cpp | 2 +- test/src/unit-json_pointer.cpp | 2 +- test/src/unit-modifiers.cpp | 2 +- test/src/unit-msgpack.cpp | 2 +- test/src/unit-pointer_access.cpp | 2 +- test/src/unit-readme.cpp | 2 +- test/src/unit-reference_access.cpp | 2 +- test/src/unit-regression.cpp | 2 +- test/src/unit-serialization.cpp | 2 +- test/src/unit-testsuites.cpp | 2 +- test/src/unit-unicode.cpp | 2 +- test/src/unit.cpp | 2 +- 37 files changed, 37 insertions(+), 37 deletions(-) diff --git a/LICENSE.MIT b/LICENSE.MIT index 79145efa..c4ce40d1 100644 --- a/LICENSE.MIT +++ b/LICENSE.MIT @@ -1,7 +1,7 @@ JSON for Modern C++ is licensed under the MIT License : -Copyright (c) 2013-2016 Niels Lohmann +Copyright (c) 2013-2017 Niels Lohmann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/doc/index.md b/doc/index.md index cc5cd793..f7ef3554 100644 --- a/doc/index.md +++ b/doc/index.md @@ -272,7 +272,7 @@ The container functions known from STL have been extended to support the differe -@copyright Copyright © 2013-2016 Niels Lohmann. The code is licensed under the [MIT License](http://opensource.org/licenses/MIT). +@copyright Copyright © 2013-2017 Niels Lohmann. The code is licensed under the [MIT License](http://opensource.org/licenses/MIT). @author [Niels Lohmann](http://nlohmann.me) @see https://github.com/nlohmann/json to download the source code diff --git a/src/json.hpp b/src/json.hpp index 471b9ec7..9d48e7a6 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 9a7c2043..e1a43b54 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-algorithms.cpp b/test/src/unit-algorithms.cpp index 04704847..0905d05e 100644 --- a/test/src/unit-algorithms.cpp +++ b/test/src/unit-algorithms.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index 00eb5b9a..f11d8538 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-capacity.cpp b/test/src/unit-capacity.cpp index 4664f1bf..d3ee33d2 100644 --- a/test/src/unit-capacity.cpp +++ b/test/src/unit-capacity.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index d01019d9..92238b79 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp index 2097e830..13ce7c3f 100644 --- a/test/src/unit-class_const_iterator.cpp +++ b/test/src/unit-class_const_iterator.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp index bd64cbf8..640bc816 100644 --- a/test/src/unit-class_iterator.cpp +++ b/test/src/unit-class_iterator.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 5656942f..33ea610a 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 3e11d80c..09515042 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-comparison.cpp b/test/src/unit-comparison.cpp index 81c3066d..7f89729e 100644 --- a/test/src/unit-comparison.cpp +++ b/test/src/unit-comparison.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-concepts.cpp b/test/src/unit-concepts.cpp index cf60b5c1..1c04b62b 100644 --- a/test/src/unit-concepts.cpp +++ b/test/src/unit-concepts.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 114ae5b3..6bfb4402 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-constructor2.cpp b/test/src/unit-constructor2.cpp index ae5ba3b7..ab1e43cc 100644 --- a/test/src/unit-constructor2.cpp +++ b/test/src/unit-constructor2.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp index 1aedea44..cd78f83a 100644 --- a/test/src/unit-convenience.cpp +++ b/test/src/unit-convenience.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index 77d476bd..b82127bb 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index f0cf2e15..21e3bb44 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-element_access1.cpp b/test/src/unit-element_access1.cpp index 04265244..a596ac21 100644 --- a/test/src/unit-element_access1.cpp +++ b/test/src/unit-element_access1.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index f2758a59..1ba6aa61 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp index 1051d2f5..9e114718 100644 --- a/test/src/unit-inspection.cpp +++ b/test/src/unit-inspection.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-iterator_wrapper.cpp b/test/src/unit-iterator_wrapper.cpp index 784fa77a..f4255f99 100644 --- a/test/src/unit-iterator_wrapper.cpp +++ b/test/src/unit-iterator_wrapper.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-iterators1.cpp b/test/src/unit-iterators1.cpp index bf14435b..a6fd2df9 100644 --- a/test/src/unit-iterators1.cpp +++ b/test/src/unit-iterators1.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-iterators2.cpp b/test/src/unit-iterators2.cpp index 5c04826d..6f1b6251 100644 --- a/test/src/unit-iterators2.cpp +++ b/test/src/unit-iterators2.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp index 4983aab2..b7987509 100644 --- a/test/src/unit-json_patch.cpp +++ b/test/src/unit-json_patch.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp index 4268c3c0..67257943 100644 --- a/test/src/unit-json_pointer.cpp +++ b/test/src/unit-json_pointer.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index 2db3f291..3e8d9600 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index eab4dcac..89fa450d 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp index f5d934d3..4771c508 100644 --- a/test/src/unit-pointer_access.cpp +++ b/test/src/unit-pointer_access.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-readme.cpp b/test/src/unit-readme.cpp index bddcd0d6..5c62e850 100644 --- a/test/src/unit-readme.cpp +++ b/test/src/unit-readme.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-reference_access.cpp b/test/src/unit-reference_access.cpp index 321907a5..54db1a86 100644 --- a/test/src/unit-reference_access.cpp +++ b/test/src/unit-reference_access.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 0bfdc91a..401867c2 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp index 3375a1c4..72d9ae6c 100644 --- a/test/src/unit-serialization.cpp +++ b/test/src/unit-serialization.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-testsuites.cpp b/test/src/unit-testsuites.cpp index dfc75936..a43e1997 100644 --- a/test/src/unit-testsuites.cpp +++ b/test/src/unit-testsuites.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-unicode.cpp b/test/src/unit-unicode.cpp index 69f4a9ee..0b1c0e5e 100644 --- a/test/src/unit-unicode.cpp +++ b/test/src/unit-unicode.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit.cpp b/test/src/unit.cpp index e7a7a799..096a2973 100644 --- a/test/src/unit.cpp +++ b/test/src/unit.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 4d3bf433b45d8f445536f51585ad0644611277f7 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 2 Jan 2017 09:56:21 +0100 Subject: [PATCH 33/35] :bookmark: updated ChangeLog --- ChangeLog.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 92545768..ce82d14b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,26 @@ # Change Log All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [v2.0.10](https://github.com/nlohmann/json/releases/tag/v2.0.10) (2017-01-02) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.9...v2.0.10) + +- Heap-buffer-overflow \(OSS-Fuzz issue 367\) [\#412](https://github.com/nlohmann/json/issues/412) +- Heap-buffer-overflow \(OSS-Fuzz issue 366\) [\#411](https://github.com/nlohmann/json/issues/411) +- Use-of-uninitialized-value \(OSS-Fuzz issue 347\) [\#409](https://github.com/nlohmann/json/issues/409) +- Heap-buffer-overflow \(OSS-Fuzz issue 344\) [\#408](https://github.com/nlohmann/json/issues/408) +- Heap-buffer-overflow \(OSS-Fuzz issue 343\) [\#407](https://github.com/nlohmann/json/issues/407) +- Heap-buffer-overflow \(OSS-Fuzz issue 342\) [\#405](https://github.com/nlohmann/json/issues/405) +- strerror throwing error in compiler VS2015 [\#403](https://github.com/nlohmann/json/issues/403) +- json::parse of std::string being underlined by Visual Studio [\#402](https://github.com/nlohmann/json/issues/402) +- Explicitly getting string without .dump\(\) [\#401](https://github.com/nlohmann/json/issues/401) +- Possible to speed up json::parse? [\#398](https://github.com/nlohmann/json/issues/398) +- the alphabetic order in the code influence console\_output. [\#396](https://github.com/nlohmann/json/issues/396) +- Execute tests with clang sanitizers [\#394](https://github.com/nlohmann/json/issues/394) +- Check if library can be used with ETL [\#361](https://github.com/nlohmann/json/issues/361) + +- Feature/clang sanitize [\#410](https://github.com/nlohmann/json/pull/410) ([Daniel599](https://github.com/Daniel599)) +- Add Doozer build badge [\#400](https://github.com/nlohmann/json/pull/400) ([andoma](https://github.com/andoma)) + ## [v2.0.9](https://github.com/nlohmann/json/releases/tag/v2.0.9) (2016-12-16) [Full Changelog](https://github.com/nlohmann/json/compare/v2.0.8...v2.0.9) From 69c615e1274ddb8ba07b3ba19eee951946bd5ddb Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 2 Jan 2017 10:00:44 +0100 Subject: [PATCH 34/35] :bookmark: version bump to 2.0.10 --- doc/json.gif | Bin 1325973 -> 1331326 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/json.gif b/doc/json.gif index 7d3bf81645496c9a72e4f187e21a3ce2ba9223ef..efd2ee634a0704653811c31714f7cc1757c02966 100644 GIT binary patch delta 50413 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Ⓢ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�hzIEzI6Z~tJ>Uj>Be6qjLnaY}Ex>Pwe;=0mG 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 Date: Mon, 2 Jan 2017 16:37:29 +0100 Subject: [PATCH 35/35] :memo: updated online link --- README.md | 2 +- doc/examples/README.link | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a0534615..498d1bbe 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Build status](https://doozer.io/badge/nlohmann/json/buildstatus/develop)](https://doozer.io/user/nlohmann/json) [![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json) [![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json) -[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/8soFCqS532vOyZcK) +[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/IoZNMHqubixQx2dN) [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) [![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases) diff --git a/doc/examples/README.link b/doc/examples/README.link index 128ab1dc..58daac88 100644 --- a/doc/examples/README.link +++ b/doc/examples/README.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file