diff options
| author | 2017-01-24 08:32:59 +0000 | |
|---|---|---|
| committer | 2017-01-24 08:32:59 +0000 | |
| commit | 53d771aafdbe5b919f264f53cba3788e2c4cffd2 (patch) | |
| tree | 7eca39498be0ff1e3a6daf583cd9ca5886bb2636 /gnu/llvm/lib/Fuzzer/FuzzerTracePC.cpp | |
| parent | In preparation of compiling our kernels with -ffreestanding, explicitly map (diff) | |
| download | wireguard-openbsd-53d771aafdbe5b919f264f53cba3788e2c4cffd2.tar.xz wireguard-openbsd-53d771aafdbe5b919f264f53cba3788e2c4cffd2.zip | |
Import LLVM 4.0.0 rc1 including clang and lld to help the current
development effort on OpenBSD/arm64.
Diffstat (limited to 'gnu/llvm/lib/Fuzzer/FuzzerTracePC.cpp')
| -rw-r--r-- | gnu/llvm/lib/Fuzzer/FuzzerTracePC.cpp | 340 |
1 files changed, 305 insertions, 35 deletions
diff --git a/gnu/llvm/lib/Fuzzer/FuzzerTracePC.cpp b/gnu/llvm/lib/Fuzzer/FuzzerTracePC.cpp index 46c43d0c17f..39d6e602621 100644 --- a/gnu/llvm/lib/Fuzzer/FuzzerTracePC.cpp +++ b/gnu/llvm/lib/Fuzzer/FuzzerTracePC.cpp @@ -7,65 +7,335 @@ // //===----------------------------------------------------------------------===// // Trace PCs. -// This module implements __sanitizer_cov_trace_pc, a callback required -// for -fsanitize-coverage=trace-pc instrumentation. +// This module implements __sanitizer_cov_trace_pc_guard[_init], +// the callback required for -fsanitize-coverage=trace-pc-guard instrumentation. // //===----------------------------------------------------------------------===// -#include "FuzzerInternal.h" +#include "FuzzerCorpus.h" +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerTracePC.h" +#include "FuzzerValueBitMap.h" +#include <map> +#include <sanitizer/coverage_interface.h> +#include <set> +#include <sstream> namespace fuzzer { -void PcCoverageMap::Reset() { memset(Map, 0, sizeof(Map)); } +TracePC TPC; -void PcCoverageMap::Update(uintptr_t Addr) { - uintptr_t Idx = Addr % kMapSizeInBits; - uintptr_t WordIdx = Idx / kBitsInWord; - uintptr_t BitIdx = Idx % kBitsInWord; - Map[WordIdx] |= 1UL << BitIdx; +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 PcCoverageMap::MergeFrom(const PcCoverageMap &Other) { - uintptr_t Res = 0; - for (size_t i = 0; i < kMapSizeInWords; i++) - Res += __builtin_popcountl(Map[i] |= Other.Map[i]); +size_t TracePC::GetTotalPCCoverage() { + size_t Res = 0; + for (size_t i = 1; i < GetNumPCs(); i++) + if (PCs[i]) + Res++; return Res; } -static PcCoverageMap CurrentMap; -static thread_local uintptr_t Prev; +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); +} -void PcMapResetCurrent() { - if (Prev) { - Prev = 0; - CurrentMap.Reset(); +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 == "<null>") + return false; + return true; +} + +void TracePC::InitializePrintNewPCs() { + if (!DoPrintNewPCs) return; + assert(!PrintedPCs); + PrintedPCs = new std::set<uintptr_t>; + for (size_t i = 1; i < GetNumPCs(); i++) + if (PCs[i]) + PrintedPCs->insert(PCs[i]); +} + +void TracePC::PrintNewPCs() { + if (!DoPrintNewPCs) return; + assert(PrintedPCs); + for (size_t i = 1; i < GetNumPCs(); i++) + if (PCs[i] && PrintedPCs->insert(PCs[i]).second) + PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PCs[i]); +} + +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<std::string, std::vector<uintptr_t>> CoveredPCsPerModule; + std::map<std::string, uintptr_t> ModuleOffsets; + std::set<std::string> 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<void *>(PCs[i]), ModulePathRaw, + sizeof(ModulePathRaw), &OffsetRaw)) + continue; + std::string Module = ModulePathRaw; + uintptr_t FixedPC = std::stol(FixedPCStr, 0, 16); + uintptr_t PcOffset = reinterpret_cast<uintptr_t>(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<std::string> UncoveredFiles, UncoveredFunctions; + std::map<std::string, std::set<int> > 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()); } } -size_t PcMapMergeInto(PcCoverageMap *Map) { - if (!Prev) - return 0; - return Map->MergeFrom(CurrentMap); +void TracePC::DumpCoverage() { + __sanitizer_dump_coverage(PCs, GetNumPCs()); } -static void HandlePC(uint32_t PC) { - // We take 12 bits of PC and mix it with the previous PCs. - uintptr_t Next = (Prev << 5) ^ (PC & 4095); - CurrentMap.Update(Next); - Prev = Next; +// 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<const uint8_t *>(s1); + const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2); + size_t I = 0; + for (; I < Len; I++) + if (A1[I] != A2[I]) + break; + size_t PC = reinterpret_cast<size_t>(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<const uint8_t *>(s1); + const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2); + size_t I = 0; + for (; I < Len; I++) + if (A1[I] != A2[I] || A1[I] == 0) + break; + size_t PC = reinterpret_cast<size_t>(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 <class T> +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<uintptr_t>(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" { -void __sanitizer_cov_trace_pc() { - fuzzer::HandlePC(static_cast<uint32_t>( - reinterpret_cast<uintptr_t>(__builtin_return_address(0)))); +__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) { + uint64_t N = Cases[0]; + uint64_t ValSizeInBits = Cases[1]; + uint64_t *Vals = Cases + 2; + // Skip the most common and the most boring case. + if (Vals[N - 1] < 256 && Val < 256) + return; + char *PC = (char*)__builtin_return_address(0); + size_t i; + uint64_t Token = 0; + for (i = 0; i < N; i++) { + Token = Val ^ Vals[i]; + if (Val < Vals[i]) + break; + } -void __sanitizer_cov_trace_pc_indir(int *) { - // Stub to allow linking with code built with - // -fsanitize=indirect-calls,trace-pc. - // This isn't used currently. + if (ValSizeInBits == 16) + fuzzer::TPC.HandleCmp(PC + i, static_cast<uint16_t>(Token), (uint16_t)(0)); + else if (ValSizeInBits == 32) + fuzzer::TPC.HandleCmp(PC + i, static_cast<uint32_t>(Token), (uint32_t)(0)); + else + fuzzer::TPC.HandleCmp(PC + i, Token, (uint64_t)(0)); } + +__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" |
