From da63c1d09a82471d9fdbd396b5da23d34485da00 Mon Sep 17 00:00:00 2001 From: Kostya Serebryany Date: Fri, 26 Feb 2016 21:33:56 +0000 Subject: [PATCH] [libFuzzer] initial implementation of path coverage based on -fsanitize-coverage=trace-pc. This does not scale well yet, but already cracks FullCoverageSetTest in seconds llvm-svn: 262073 --- llvm/lib/Fuzzer/CMakeLists.txt | 1 + llvm/lib/Fuzzer/FuzzerInternal.h | 8 +++ llvm/lib/Fuzzer/FuzzerLoop.cpp | 8 +++ llvm/lib/Fuzzer/FuzzerTracePC.cpp | 59 ++++++++++++++++++++ llvm/lib/Fuzzer/test/CMakeLists.txt | 11 ++++ llvm/lib/Fuzzer/test/fuzzer-trace-pc.test | 2 + llvm/lib/Fuzzer/test/trace-pc/CMakeLists.txt | 14 +++++ 7 files changed, 103 insertions(+) create mode 100644 llvm/lib/Fuzzer/FuzzerTracePC.cpp create mode 100644 llvm/lib/Fuzzer/test/fuzzer-trace-pc.test create mode 100644 llvm/lib/Fuzzer/test/trace-pc/CMakeLists.txt diff --git a/llvm/lib/Fuzzer/CMakeLists.txt b/llvm/lib/Fuzzer/CMakeLists.txt index 726eec6b7430..eeae5112ca5a 100644 --- a/llvm/lib/Fuzzer/CMakeLists.txt +++ b/llvm/lib/Fuzzer/CMakeLists.txt @@ -12,6 +12,7 @@ if( LLVM_USE_SANITIZE_COVERAGE ) FuzzerMutate.cpp FuzzerSanitizerOptions.cpp FuzzerSHA1.cpp + FuzzerTracePC.cpp FuzzerUtil.cpp ) add_library(LLVMFuzzerNoMain STATIC diff --git a/llvm/lib/Fuzzer/FuzzerInternal.h b/llvm/lib/Fuzzer/FuzzerInternal.h index 1c941abd13bb..03905c2ec3a1 100644 --- a/llvm/lib/Fuzzer/FuzzerInternal.h +++ b/llvm/lib/Fuzzer/FuzzerInternal.h @@ -99,6 +99,13 @@ bool IsASCII(const Unit &U); int NumberOfCpuCores(); int GetPid(); +// Clears the current PC Map. +void PcMapResetCurrent(); +// Merges the current PC Map into the combined one, and clears the former. +void PcMapMergeCurrentToCombined(); +// Returns the size of the combined PC Map. +size_t PcMapCombinedSize(); + class Random { public: Random(unsigned int seed) : R(seed) {} @@ -390,6 +397,7 @@ private: long TimeOfLongestUnitInSeconds = 0; long EpochOfLastReadOfOutputCorpus = 0; size_t LastRecordedBlockCoverage = 0; + size_t LastRecordedPcMapSize = 0; size_t LastRecordedCallerCalleeCoverage = 0; size_t LastCoveragePcBufferLen = 0; }; diff --git a/llvm/lib/Fuzzer/FuzzerLoop.cpp b/llvm/lib/Fuzzer/FuzzerLoop.cpp index e08ca7702b23..82f1828064f7 100644 --- a/llvm/lib/Fuzzer/FuzzerLoop.cpp +++ b/llvm/lib/Fuzzer/FuzzerLoop.cpp @@ -151,6 +151,8 @@ void Fuzzer::PrintStats(const char *Where, const char *End) { Printf("#%zd\t%s", TotalNumberOfRuns, Where); if (LastRecordedBlockCoverage) Printf(" cov: %zd", LastRecordedBlockCoverage); + if (LastRecordedPcMapSize) + Printf(" path: %zd", LastRecordedPcMapSize); if (auto TB = TotalBits()) Printf(" bits: %zd", TB); if (LastRecordedCallerCalleeCoverage) @@ -316,6 +318,12 @@ bool Fuzzer::CheckCoverageAfterRun() { size_t OldCallerCalleeCoverage = LastRecordedCallerCalleeCoverage; size_t NewCallerCalleeCoverage = RecordCallerCalleeCoverage(); size_t NumNewBits = 0; + size_t OldPcMapSize = LastRecordedPcMapSize; + PcMapMergeCurrentToCombined(); + size_t NewPcMapSize = PcMapCombinedSize(); + LastRecordedPcMapSize = NewPcMapSize; + if (NewPcMapSize > OldPcMapSize) + return true; if (Options.UseCounters) NumNewBits = __sanitizer_update_counter_bitset_and_clear_counters( CounterBitmap.data()); diff --git a/llvm/lib/Fuzzer/FuzzerTracePC.cpp b/llvm/lib/Fuzzer/FuzzerTracePC.cpp new file mode 100644 index 000000000000..b6f29957c4db --- /dev/null +++ b/llvm/lib/Fuzzer/FuzzerTracePC.cpp @@ -0,0 +1,59 @@ +//===- 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, a callback required +// for -fsanitize-coverage=trace-pc instrumentation. +// +// Experimental and not yet tuned for performance. +//===----------------------------------------------------------------------===// + +#include "FuzzerInternal.h" + +namespace fuzzer { +static const size_t kMapSize = 65371; // Prime. +static uint8_t CurMap[kMapSize]; +static uint8_t CombinedMap[kMapSize]; +static size_t CombinedMapSize; +static thread_local uintptr_t Prev; + +void PcMapResetCurrent() { + if (Prev) { + Prev = 0; + memset(CurMap, 0, sizeof(CurMap)); + } +} + +// TODO: speed this up. +void PcMapMergeCurrentToCombined() { + if (!Prev) return; + uintptr_t Res = 0; + for (size_t i = 0; i < kMapSize; i++) { + uint8_t p = (CombinedMap[i] |= CurMap[i]); + CurMap[i] = 0; + Res += p != 0; + } + CombinedMapSize = Res; +} + +size_t PcMapCombinedSize() { return CombinedMapSize; } + +static void HandlePC(uintptr_t PC) { + // We take 12 bits of PC and mix it with the previous PCs. + uintptr_t Idx = (Prev << 5) ^ (PC & 4095); + CurMap[Idx % kMapSize] = 1; + Prev = Idx; +} + +} // namespace fuzzer + +extern "C" void __sanitizer_cov_trace_pc() { + fuzzer::HandlePC(reinterpret_cast(__builtin_return_address(0))); +} +//uintptr_t __sanitizer_get_total_unique_coverage() { return 0; } +//uintptr_t __sanitizer_get_number_of_counters() { return 0; } diff --git a/llvm/lib/Fuzzer/test/CMakeLists.txt b/llvm/lib/Fuzzer/test/CMakeLists.txt index 709e96bf6159..e4b1926978e8 100644 --- a/llvm/lib/Fuzzer/test/CMakeLists.txt +++ b/llvm/lib/Fuzzer/test/CMakeLists.txt @@ -47,6 +47,11 @@ set(TraceBBTests SimpleTest ) +set(TracePCTests + FourIndependentBranchesTest + FullCoverageSetTest + ) + set(TestBinaries) foreach(Test ${Tests}) @@ -113,6 +118,12 @@ foreach(Test ${TraceBBTests}) set(TestBinaries ${TestBinaries} LLVMFuzzer-${Test}-TraceBB) endforeach() +add_subdirectory(trace-pc) + +foreach(Test ${TracePCTests}) + set(TestBinaries ${TestBinaries} LLVMFuzzer-${Test}-TracePC) +endforeach() + set_target_properties(${TestBinaries} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) diff --git a/llvm/lib/Fuzzer/test/fuzzer-trace-pc.test b/llvm/lib/Fuzzer/test/fuzzer-trace-pc.test new file mode 100644 index 000000000000..3dbd177c127a --- /dev/null +++ b/llvm/lib/Fuzzer/test/fuzzer-trace-pc.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-FourIndependentBranchesTest-TracePC -seed=1 -runs=1000000 2>&1 | FileCheck %s diff --git a/llvm/lib/Fuzzer/test/trace-pc/CMakeLists.txt b/llvm/lib/Fuzzer/test/trace-pc/CMakeLists.txt new file mode 100644 index 000000000000..f921a61ecba9 --- /dev/null +++ b/llvm/lib/Fuzzer/test/trace-pc/CMakeLists.txt @@ -0,0 +1,14 @@ +# These tests are not instrumented with coverage. + +set(CMAKE_CXX_FLAGS_RELEASE + "${LIBFUZZER_FLAGS_BASE} -O0 -fno-sanitize-coverage=8bit-counters -fsanitize-coverage=trace-pc") + +foreach(Test ${TracePCTests}) + add_executable(LLVMFuzzer-${Test}-TracePC + ../${Test}.cpp + ) + target_link_libraries(LLVMFuzzer-${Test}-TracePC + LLVMFuzzer + ) +endforeach() +