diff options
Diffstat (limited to 'llvm/lib')
-rw-r--r-- | llvm/lib/Fuzzer/CMakeLists.txt | 2 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/FuzzerDriver.cpp | 3 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/FuzzerFlags.def | 2 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/FuzzerInternal.h | 8 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/FuzzerLoop.cpp | 13 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/FuzzerTraceState.cpp | 70 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/FuzzerValueBitMap.h | 5 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/test/CMakeLists.txt | 3 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/test/SingleMemcmpTest.cpp | 17 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/test/SingleStrcmpTest.cpp | 17 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/test/SingleStrncmpTest.cpp | 17 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/test/StrncmpTest.cpp | 2 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/test/value-profile-cmp.test | 3 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/test/value-profile-mem.test | 4 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/test/value-profile-set.test | 3 |
15 files changed, 158 insertions, 11 deletions
diff --git a/llvm/lib/Fuzzer/CMakeLists.txt b/llvm/lib/Fuzzer/CMakeLists.txt index 1829f0586b8..05e05816365 100644 --- a/llvm/lib/Fuzzer/CMakeLists.txt +++ b/llvm/lib/Fuzzer/CMakeLists.txt @@ -1,6 +1,6 @@ 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=all -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters -Werror") +set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -mpopcnt -fno-sanitize=all -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters -Werror") if( LLVM_USE_SANITIZE_COVERAGE ) if(NOT "${LLVM_USE_SANITIZER}" STREQUAL "Address") message(FATAL_ERROR diff --git a/llvm/lib/Fuzzer/FuzzerDriver.cpp b/llvm/lib/Fuzzer/FuzzerDriver.cpp index a6db9fa338b..e9913e470fd 100644 --- a/llvm/lib/Fuzzer/FuzzerDriver.cpp +++ b/llvm/lib/Fuzzer/FuzzerDriver.cpp @@ -339,6 +339,9 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Options.TruncateUnits = Flags.truncate_units; Options.PruneCorpus = Flags.prune_corpus; + if (Flags.use_value_profile) + EnableValueProfile(); + unsigned Seed = Flags.seed; // Initialize Seed. if (Seed == 0) diff --git a/llvm/lib/Fuzzer/FuzzerFlags.def b/llvm/lib/Fuzzer/FuzzerFlags.def index 599ac368634..0b313eb6570 100644 --- a/llvm/lib/Fuzzer/FuzzerFlags.def +++ b/llvm/lib/Fuzzer/FuzzerFlags.def @@ -44,6 +44,8 @@ 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(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.") diff --git a/llvm/lib/Fuzzer/FuzzerInternal.h b/llvm/lib/Fuzzer/FuzzerInternal.h index db1e505ac38..940d8365f34 100644 --- a/llvm/lib/Fuzzer/FuzzerInternal.h +++ b/llvm/lib/Fuzzer/FuzzerInternal.h @@ -134,6 +134,10 @@ void SleepSeconds(int Seconds); // See FuzzerTracePC.cpp size_t PCMapMergeFromCurrent(ValueBitMap &M); +// See FuzzerTraceState.cpp +void EnableValueProfile(); +size_t VPMapMergeFromCurrent(ValueBitMap &M); + class Random { public: Random(unsigned int seed) : R(seed) {} @@ -356,6 +360,8 @@ public: CounterBitmap.clear(); PCMap.Reset(); PCMapBits = 0; + VPMap.Reset(); + VPMapBits = 0; PcBufferPos = 0; } @@ -369,6 +375,8 @@ public: std::vector<uint8_t> CounterBitmap; ValueBitMap PCMap; size_t PCMapBits; + ValueBitMap VPMap; + size_t VPMapBits; }; Fuzzer(UserCallback CB, MutationDispatcher &MD, FuzzingOptions Options); diff --git a/llvm/lib/Fuzzer/FuzzerLoop.cpp b/llvm/lib/Fuzzer/FuzzerLoop.cpp index f90b7092d49..07de3c7c8b9 100644 --- a/llvm/lib/Fuzzer/FuzzerLoop.cpp +++ b/llvm/lib/Fuzzer/FuzzerLoop.cpp @@ -122,6 +122,12 @@ class CoverageController { C->PCMapBits = NewPCMapBits; } + size_t NewVPMapBits = VPMapMergeFromCurrent(C->VPMap); + if (NewVPMapBits > C->VPMapBits) { + Res = true; + C->VPMapBits = NewVPMapBits; + } + if (EF->__sanitizer_get_coverage_pc_buffer_pos) { uint64_t NewPcBufferPos = EF->__sanitizer_get_coverage_pc_buffer_pos(); if (NewPcBufferPos > C->PcBufferPos) { @@ -307,6 +313,8 @@ void Fuzzer::PrintStats(const char *Where, const char *End) { Printf(" cov: %zd", MaxCoverage.BlockCoverage); if (MaxCoverage.PCMapBits) Printf(" path: %zd", MaxCoverage.PCMapBits); + if (MaxCoverage.VPMapBits) + Printf(" vp: %zd", MaxCoverage.VPMapBits); if (auto TB = MaxCoverage.CounterBitmapBits) Printf(" bits: %zd", TB); if (MaxCoverage.CallerCalleeCoverage) @@ -520,8 +528,9 @@ std::string Fuzzer::Coverage::DebugString() const { std::string("Coverage{") + "BlockCoverage=" + std::to_string(BlockCoverage) + " CallerCalleeCoverage=" + std::to_string(CallerCalleeCoverage) + " CounterBitmapBits=" + - std::to_string(CounterBitmapBits) + " PcMapBits=" + - std::to_string(PCMapBits) + "}"; + std::to_string(CounterBitmapBits) + " PCMapBits=" + + std::to_string(PCMapBits) + " VPMapBits " + + std::to_string(VPMapBits) + "}"; return Result; } diff --git a/llvm/lib/Fuzzer/FuzzerTraceState.cpp b/llvm/lib/Fuzzer/FuzzerTraceState.cpp index 6f87fcae5fc..9eae068c354 100644 --- a/llvm/lib/Fuzzer/FuzzerTraceState.cpp +++ b/llvm/lib/Fuzzer/FuzzerTraceState.cpp @@ -173,6 +173,7 @@ struct TraceBasedMutation { static bool RecordingTraces = false; static bool RecordingMemcmp = false; static bool RecordingMemmem = false; +static bool RecordingValueProfile = false; static bool DoingMyOwnMemmem = false; struct ScopedDoingMyOwnMemmem { @@ -529,11 +530,60 @@ static size_t InternalStrnlen(const char *S, size_t MaxLen) { return Len; } +// Value profile. +// We keep track of various values that affect control flow. +// These values are inserted into a bit-set-based hash map (ValueBitMap VP). +// 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. +static ValueBitMap VP; + +void EnableValueProfile() { RecordingValueProfile = true; } + +size_t VPMapMergeFromCurrent(ValueBitMap &M) { + if (!RecordingValueProfile) return 0; + return M.MergeFrom(VP); +} + +static void 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 char *A1 = reinterpret_cast<const char *>(s1); + const char *A2 = reinterpret_cast<const char *>(s2); + size_t LastSameByte = 0; + for (; LastSameByte < Len; LastSameByte++) + if (A1[LastSameByte] != A2[LastSameByte]) + break; + size_t PC = reinterpret_cast<size_t>(caller_pc); + VP.AddValue((PC & 4095) | (LastSameByte << 12)); +} + +static void 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); + size_t LastSameByte = 0; + for (; LastSameByte < Len; LastSameByte++) + if (s1[LastSameByte] != s2[LastSameByte] || s1[LastSameByte] == 0) + break; + size_t PC = reinterpret_cast<size_t>(caller_pc); + VP.AddValue((PC & 4095) | (LastSameByte << 12)); +} + +static void AddValueForCmp(uintptr_t PC, uint64_t Arg1, uint64_t Arg2) { + VP.AddValue((PC & 4095) | (__builtin_popcountl(Arg1 ^ Arg2) << 12)); +} + } // namespace fuzzer using fuzzer::TS; using fuzzer::RecordingTraces; using fuzzer::RecordingMemcmp; +using fuzzer::RecordingValueProfile; extern "C" { void __dfsw___sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1, @@ -597,6 +647,8 @@ void dfsan_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2, #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) { + if (RecordingValueProfile) + fuzzer::AddValueForMemcmp(caller_pc, s1, s2, n); if (!RecordingMemcmp) return; if (result == 0) return; // No reason to mutate. if (n <= 1) return; // Not interesting. @@ -606,6 +658,8 @@ void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1, void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2, size_t n, int result) { + if (RecordingValueProfile) + fuzzer::AddValueForStrcmp(caller_pc, s1, s2, n); if (!RecordingMemcmp) return; if (result == 0) return; // No reason to mutate. size_t Len1 = fuzzer::InternalStrnlen(s1, n); @@ -619,6 +673,8 @@ void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1, void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2, int result) { + if (RecordingValueProfile) + fuzzer::AddValueForStrcmp(caller_pc, s1, s2, 64); if (!RecordingMemcmp) return; if (result == 0) return; // No reason to mutate. size_t Len1 = strlen(s1); @@ -656,11 +712,15 @@ void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1, __attribute__((visibility("default"))) void __sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1, uint64_t Arg2) { - if (!RecordingTraces) return; - uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); - uint64_t CmpSize = (SizeAndType >> 32) / 8; - uint64_t Type = (SizeAndType << 32) >> 32; - TS->TraceCmpCallback(PC, CmpSize, Type, Arg1, Arg2); + if (RecordingTraces) { + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + uint64_t CmpSize = (SizeAndType >> 32) / 8; + uint64_t Type = (SizeAndType << 32) >> 32; + TS->TraceCmpCallback(PC, CmpSize, Type, Arg1, Arg2); + } + if (RecordingValueProfile) + fuzzer::AddValueForCmp( + reinterpret_cast<uintptr_t>(__builtin_return_address(0)), Arg1, Arg2); } __attribute__((visibility("default"))) diff --git a/llvm/lib/Fuzzer/FuzzerValueBitMap.h b/llvm/lib/Fuzzer/FuzzerValueBitMap.h index 41169d66bc0..493afc574cc 100644 --- a/llvm/lib/Fuzzer/FuzzerValueBitMap.h +++ b/llvm/lib/Fuzzer/FuzzerValueBitMap.h @@ -32,7 +32,7 @@ struct ValueBitMap { Map[WordIdx] |= 1UL << BitIdx; } - // Merges 'Other' into 'this', clear Other, + // Merges 'Other' into 'this', clears 'Other', // returns the number of set bits in 'this'. size_t MergeFrom(ValueBitMap &Other) { uintptr_t Res = 0; @@ -43,7 +43,8 @@ struct ValueBitMap { Map[i] = (M |= O); Other.Map[i] = 0; } - Res += __builtin_popcountl(M); + if (M) + Res += __builtin_popcountl(M); } return Res; } diff --git a/llvm/lib/Fuzzer/test/CMakeLists.txt b/llvm/lib/Fuzzer/test/CMakeLists.txt index 2164086b5a7..1d82408d7cb 100644 --- a/llvm/lib/Fuzzer/test/CMakeLists.txt +++ b/llvm/lib/Fuzzer/test/CMakeLists.txt @@ -88,6 +88,9 @@ set(Tests SimpleHashTest SimpleTest SimpleThreadedTest + SingleMemcmpTest + SingleStrcmpTest + SingleStrncmpTest SpamyTest StrcmpTest StrncmpTest diff --git a/llvm/lib/Fuzzer/test/SingleMemcmpTest.cpp b/llvm/lib/Fuzzer/test/SingleMemcmpTest.cpp new file mode 100644 index 00000000000..c73f68a7ee6 --- /dev/null +++ b/llvm/lib/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 <cstring> +#include <cstdint> +#include <cstdio> +#include <cstdlib> + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + char *S = (char*)Data; + if (Size >= 6 && !memcmp(S, "qwerty", 6)) { + fprintf(stderr, "BINGO\n"); + exit(1); + } + return 0; +} diff --git a/llvm/lib/Fuzzer/test/SingleStrcmpTest.cpp b/llvm/lib/Fuzzer/test/SingleStrcmpTest.cpp new file mode 100644 index 00000000000..73470b527ee --- /dev/null +++ b/llvm/lib/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 <cstring> +#include <cstdint> +#include <cstdio> +#include <cstdlib> + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + char *S = (char*)Data; + if (Size >= 7 && !strcmp(S, "qwerty")) { + fprintf(stderr, "BINGO\n"); + exit(1); + } + return 0; +} diff --git a/llvm/lib/Fuzzer/test/SingleStrncmpTest.cpp b/llvm/lib/Fuzzer/test/SingleStrncmpTest.cpp new file mode 100644 index 00000000000..dbcc464b0a7 --- /dev/null +++ b/llvm/lib/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 <cstring> +#include <cstdint> +#include <cstdio> +#include <cstdlib> + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + char *S = (char*)Data; + if (Size >= 6 && !strncmp(S, "qwerty", 6)) { + fprintf(stderr, "BINGO\n"); + exit(1); + } + return 0; +} diff --git a/llvm/lib/Fuzzer/test/StrncmpTest.cpp b/llvm/lib/Fuzzer/test/StrncmpTest.cpp index 8575c2682f1..5ffd011dcdf 100644 --- a/llvm/lib/Fuzzer/test/StrncmpTest.cpp +++ b/llvm/lib/Fuzzer/test/StrncmpTest.cpp @@ -17,7 +17,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { 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 >= 16 && strncmp(S + 14, "KLM", 3) == 0) { + if (Size >= 17 && strncmp(S + 14, "KLM", 3) == 0) { fprintf(stderr, "BINGO\n"); exit(1); } diff --git a/llvm/lib/Fuzzer/test/value-profile-cmp.test b/llvm/lib/Fuzzer/test/value-profile-cmp.test new file mode 100644 index 00000000000..e4059cd82ee --- /dev/null +++ b/llvm/lib/Fuzzer/test/value-profile-cmp.test @@ -0,0 +1,3 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SimpleCmpTest -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s + diff --git a/llvm/lib/Fuzzer/test/value-profile-mem.test b/llvm/lib/Fuzzer/test/value-profile-mem.test new file mode 100644 index 00000000000..2052e879164 --- /dev/null +++ b/llvm/lib/Fuzzer/test/value-profile-mem.test @@ -0,0 +1,4 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SingleMemcmpTest -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s +RUN: not LLVMFuzzer-SingleStrcmpTest -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s +RUN: not LLVMFuzzer-SingleStrncmpTest -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/llvm/lib/Fuzzer/test/value-profile-set.test b/llvm/lib/Fuzzer/test/value-profile-set.test new file mode 100644 index 00000000000..300afa9320d --- /dev/null +++ b/llvm/lib/Fuzzer/test/value-profile-set.test @@ -0,0 +1,3 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-FourIndependentBranchesTest -seed=1 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s + |