aboutsummaryrefslogtreecommitdiff
path: root/lib/Fuzzer/FuzzerTracePC.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Fuzzer/FuzzerTracePC.cpp')
-rw-r--r--lib/Fuzzer/FuzzerTracePC.cpp246
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"