diff options
Diffstat (limited to 'lib/Fuzzer/FuzzerTracePC.cpp')
-rw-r--r-- | lib/Fuzzer/FuzzerTracePC.cpp | 246 |
1 files changed, 150 insertions, 96 deletions
diff --git a/lib/Fuzzer/FuzzerTracePC.cpp b/lib/Fuzzer/FuzzerTracePC.cpp index 39d6e6026210..ce0f7a47eee6 100644 --- a/lib/Fuzzer/FuzzerTracePC.cpp +++ b/lib/Fuzzer/FuzzerTracePC.cpp @@ -18,27 +18,37 @@ #include "FuzzerExtFunctions.h" #include "FuzzerIO.h" #include "FuzzerTracePC.h" +#include "FuzzerUtil.h" #include "FuzzerValueBitMap.h" #include <map> -#include <sanitizer/coverage_interface.h> #include <set> #include <sstream> +// The coverage counters and PCs. +// These are declared as global variables named "__sancov_*" to simplify +// experiments with inlined instrumentation. +alignas(64) ATTRIBUTE_INTERFACE +uint8_t __sancov_trace_pc_guard_8bit_counters[fuzzer::TracePC::kNumPCs]; + +ATTRIBUTE_INTERFACE +uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs]; + 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]++; +uint8_t *TracePC::Counters() const { + return __sancov_trace_pc_guard_8bit_counters; +} + +uintptr_t *TracePC::PCs() const { + return __sancov_trace_pc_pcs; } size_t TracePC::GetTotalPCCoverage() { size_t Res = 0; - for (size_t i = 1; i < GetNumPCs(); i++) - if (PCs[i]) + for (size_t i = 1, N = GetNumPCs(); i < N; i++) + if (PCs()[i]) Res++; return Res; } @@ -46,8 +56,16 @@ size_t TracePC::GetTotalPCCoverage() { 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; + for (uint32_t *P = Start; P < Stop; P++) { + NumGuards++; + if (NumGuards == kNumPCs) { + RawPrint( + "WARNING: The binary has too many instrumented PCs.\n" + " You may want to reduce the size of the binary\n" + " for more efficient fuzzing and precise coverage data\n"); + } + *P = NumGuards % kNumPCs; + } Modules[NumModules].Start = Start; Modules[NumModules].Stop = Stop; NumModules++; @@ -60,23 +78,12 @@ void TracePC::PrintModuleInfo() { Printf("\n"); } +ATTRIBUTE_NO_SANITIZE_ALL 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 == "<null>") - return false; - return true; + ValueProfileMap.AddValueModPrime(Idx); } void TracePC::InitializePrintNewPCs() { @@ -84,16 +91,16 @@ void TracePC::InitializePrintNewPCs() { assert(!PrintedPCs); PrintedPCs = new std::set<uintptr_t>; for (size_t i = 1; i < GetNumPCs(); i++) - if (PCs[i]) - PrintedPCs->insert(PCs[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]); + if (PCs()[i] && PrintedPCs->insert(PCs()[i]).second) + PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PCs()[i]); } void TracePC::PrintCoverage() { @@ -110,20 +117,21 @@ void TracePC::PrintCoverage() { CoveredLines; Printf("COVERAGE:\n"); for (size_t i = 1; i < GetNumPCs(); i++) { - if (!PCs[i]) continue; - std::string FileStr = DescribePC("%s", PCs[i]); + uintptr_t PC = PCs()[i]; + if (!PC) continue; + std::string FileStr = DescribePC("%s", PC); 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]); + std::string FixedPCStr = DescribePC("%p", PC); + std::string FunctionStr = DescribePC("%F", PC); + std::string LineStr = DescribePC("%l", PC); char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++? void *OffsetRaw = nullptr; if (!EF->__sanitizer_get_module_and_offset_for_pc( - reinterpret_cast<void *>(PCs[i]), ModulePathRaw, + reinterpret_cast<void *>(PC), ModulePathRaw, sizeof(ModulePathRaw), &OffsetRaw)) continue; std::string Module = ModulePathRaw; - uintptr_t FixedPC = std::stol(FixedPCStr, 0, 16); + uintptr_t FixedPC = std::stoull(FixedPCStr, 0, 16); uintptr_t PcOffset = reinterpret_cast<uintptr_t>(OffsetRaw); ModuleOffsets[Module] = FixedPC - PcOffset; CoveredPCsPerModule[Module].push_back(PcOffset); @@ -154,8 +162,8 @@ void TracePC::PrintCoverage() { 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 Cmd = DisassembleCmd(ModuleName) + " | " + + SearchRegexCmd("call.*__sanitizer_cov_trace_pc_guard"); std::string SanCovOutput; if (!ExecuteCommandAndReadOutput(Cmd, &SanCovOutput)) { Printf("INFO: Command failed: %s\n", Cmd.c_str()); @@ -164,7 +172,11 @@ void TracePC::PrintCoverage() { std::istringstream ISS(SanCovOutput); std::string S; while (std::getline(ISS, S, '\n')) { - uintptr_t PcOffset = std::stol(S, 0, 16); + size_t PcOffsetEnd = S.find(':'); + if (PcOffsetEnd == std::string::npos) + continue; + S.resize(PcOffsetEnd); + uintptr_t PcOffset = std::stoull(S, 0, 16); if (!std::binary_search(CoveredOffsets.begin(), CoveredOffsets.end(), PcOffset)) { uintptr_t PC = ModuleOffset + PcOffset; @@ -196,8 +208,19 @@ void TracePC::PrintCoverage() { } } +inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) { + // TODO: this implementation is x86 only. + // see sanitizer_common GetPreviousInstructionPc for full implementation. + return PC - 1; +} + void TracePC::DumpCoverage() { - __sanitizer_dump_coverage(PCs, GetNumPCs()); + if (EF->__sanitizer_dump_coverage) { + std::vector<uintptr_t> PCsCopy(GetNumPCs()); + for (size_t i = 0; i < GetNumPCs(); i++) + PCsCopy[i] = PCs()[i] ? GetPreviousInstructionPc(PCs()[i]) : 0; + EF->__sanitizer_dump_coverage(PCsCopy.data(), PCsCopy.size()); + } } // Value profile. @@ -210,97 +233,118 @@ void TracePC::DumpCoverage() { // 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 +ATTRIBUTE_NO_SANITIZE_ALL void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, - size_t n) { + size_t n, bool StopAtZero) { 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); + size_t Len = std::min(n, Word::GetMaxSize()); const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1); const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2); + uint8_t B1[Word::kMaxSize]; + uint8_t B2[Word::kMaxSize]; + // Copy the data into locals in this non-msan-instrumented function + // to avoid msan complaining further. + size_t Hash = 0; // Compute some simple hash of both strings. + for (size_t i = 0; i < Len; i++) { + B1[i] = A1[i]; + B2[i] = A2[i]; + size_t T = B1[i]; + Hash ^= (T << 8) | B2[i]; + } size_t I = 0; for (; I < Len; I++) - if (A1[I] != A2[I] || A1[I] == 0) + if (B1[I] != B2[I] || (StopAtZero && B1[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)); + size_t Idx = (PC & 4095) | (I << 12); + ValueProfileMap.AddValue(Idx); + TORCW.Insert(Idx ^ Hash, Word(B1, Len), Word(B2, Len)); } 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); +ATTRIBUTE_TARGET_POPCNT ALWAYS_INLINE +ATTRIBUTE_NO_SANITIZE_ALL +void TracePC::HandleCmp(uintptr_t PC, T Arg1, T Arg2) { uint64_t ArgXor = Arg1 ^ Arg2; - uint64_t ArgDistance = __builtin_popcountl(ArgXor) + 1; // [1,65] - uintptr_t Idx = ((PCuint & 4095) + 1) * ArgDistance; + uint64_t ArgDistance = __builtin_popcountll(ArgXor) + 1; // [1,65] + uintptr_t Idx = ((PC & 4095) + 1) * ArgDistance; if (sizeof(T) == 4) TORC4.Insert(ArgXor, Arg1, Arg2); else if (sizeof(T) == 8) TORC8.Insert(ArgXor, Arg1, Arg2); - HandleValueProfile(Idx); + ValueProfileMap.AddValue(Idx); } } // namespace fuzzer extern "C" { -__attribute__((visibility("default"))) +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) { - uintptr_t PC = (uintptr_t)__builtin_return_address(0); - fuzzer::TPC.HandleTrace(Guard, PC); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + uint32_t Idx = *Guard; + __sancov_trace_pc_pcs[Idx] = PC; + __sancov_trace_pc_guard_8bit_counters[Idx]++; +} + +// Best-effort support for -fsanitize-coverage=trace-pc, which is available +// in both Clang and GCC. +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +void __sanitizer_cov_trace_pc() { + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + uintptr_t Idx = PC & (((uintptr_t)1 << fuzzer::TracePC::kTracePcBits) - 1); + __sancov_trace_pc_pcs[Idx] = PC; + __sancov_trace_pc_guard_8bit_counters[Idx]++; } -__attribute__((visibility("default"))) +ATTRIBUTE_INTERFACE void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) { fuzzer::TPC.HandleInit(Start, Stop); } -__attribute__((visibility("default"))) +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) { - uintptr_t PC = (uintptr_t)__builtin_return_address(0); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); fuzzer::TPC.HandleCallerCallee(PC, Callee); } -__attribute__((visibility("default"))) +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) { - fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); } -__attribute__((visibility("default"))) + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) { - fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); } -__attribute__((visibility("default"))) + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) { - fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); } -__attribute__((visibility("default"))) + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) { - fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); } -__attribute__((visibility("default"))) +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { uint64_t N = Cases[0]; uint64_t ValSizeInBits = Cases[1]; @@ -308,7 +352,7 @@ void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { // Skip the most common and the most boring case. if (Vals[N - 1] < 256 && Val < 256) return; - char *PC = (char*)__builtin_return_address(0); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); size_t i; uint64_t Token = 0; for (i = 0; i < N; i++) { @@ -325,17 +369,27 @@ void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { fuzzer::TPC.HandleCmp(PC + i, Token, (uint64_t)(0)); } -__attribute__((visibility("default"))) +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_div4(uint32_t Val) { - fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint32_t)0); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + fuzzer::TPC.HandleCmp(PC, Val, (uint32_t)0); } -__attribute__((visibility("default"))) + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_div8(uint64_t Val) { - fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint64_t)0); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + fuzzer::TPC.HandleCmp(PC, Val, (uint64_t)0); } -__attribute__((visibility("default"))) + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_gep(uintptr_t Idx) { - fuzzer::TPC.HandleCmp(__builtin_return_address(0), Idx, (uintptr_t)0); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + fuzzer::TPC.HandleCmp(PC, Idx, (uintptr_t)0); } - } // extern "C" |