LLVM  4.0.0
FuzzerTracePC.cpp
Go to the documentation of this file.
1 //===- FuzzerTracePC.cpp - PC tracing--------------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 // Trace PCs.
10 // This module implements __sanitizer_cov_trace_pc_guard[_init],
11 // the callback required for -fsanitize-coverage=trace-pc-guard instrumentation.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "FuzzerCorpus.h"
16 #include "FuzzerDefs.h"
17 #include "FuzzerDictionary.h"
18 #include "FuzzerExtFunctions.h"
19 #include "FuzzerIO.h"
20 #include "FuzzerTracePC.h"
21 #include "FuzzerValueBitMap.h"
22 #include <map>
23 #include <sanitizer/coverage_interface.h>
24 #include <set>
25 #include <sstream>
26 
27 namespace fuzzer {
28 
30 
31 void TracePC::HandleTrace(uint32_t *Guard, uintptr_t PC) {
32  uint32_t Idx = *Guard;
33  if (!Idx) return;
34  PCs[Idx % kNumPCs] = PC;
35  Counters[Idx % kNumCounters]++;
36 }
37 
39  size_t Res = 0;
40  for (size_t i = 1; i < GetNumPCs(); i++)
41  if (PCs[i])
42  Res++;
43  return Res;
44 }
45 
46 void TracePC::HandleInit(uint32_t *Start, uint32_t *Stop) {
47  if (Start == Stop || *Start) return;
48  assert(NumModules < sizeof(Modules) / sizeof(Modules[0]));
49  for (uint32_t *P = Start; P < Stop; P++)
50  *P = ++NumGuards;
51  Modules[NumModules].Start = Start;
52  Modules[NumModules].Stop = Stop;
53  NumModules++;
54 }
55 
57  Printf("INFO: Loaded %zd modules (%zd guards): ", NumModules, NumGuards);
58  for (size_t i = 0; i < NumModules; i++)
59  Printf("[%p, %p), ", Modules[i].Start, Modules[i].Stop);
60  Printf("\n");
61 }
62 
63 void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) {
64  const uintptr_t kBits = 12;
65  const uintptr_t kMask = (1 << kBits) - 1;
66  uintptr_t Idx = (Caller & kMask) | ((Callee & kMask) << kBits);
67  HandleValueProfile(Idx);
68 }
69 
70 static bool IsInterestingCoverageFile(std::string &File) {
71  if (File.find("compiler-rt/lib/") != std::string::npos)
72  return false; // sanitizer internal.
73  if (File.find("/usr/lib/") != std::string::npos)
74  return false;
75  if (File.find("/usr/include/") != std::string::npos)
76  return false;
77  if (File == "<null>")
78  return false;
79  return true;
80 }
81 
83  if (!DoPrintNewPCs) return;
84  assert(!PrintedPCs);
85  PrintedPCs = new std::set<uintptr_t>;
86  for (size_t i = 1; i < GetNumPCs(); i++)
87  if (PCs[i])
88  PrintedPCs->insert(PCs[i]);
89 }
90 
92  if (!DoPrintNewPCs) return;
93  assert(PrintedPCs);
94  for (size_t i = 1; i < GetNumPCs(); i++)
95  if (PCs[i] && PrintedPCs->insert(PCs[i]).second)
96  PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PCs[i]);
97 }
98 
100  if (!EF->__sanitizer_symbolize_pc ||
101  !EF->__sanitizer_get_module_and_offset_for_pc) {
102  Printf("INFO: __sanitizer_symbolize_pc or "
103  "__sanitizer_get_module_and_offset_for_pc is not available,"
104  " not printing coverage\n");
105  return;
106  }
107  std::map<std::string, std::vector<uintptr_t>> CoveredPCsPerModule;
108  std::map<std::string, uintptr_t> ModuleOffsets;
109  std::set<std::string> CoveredDirs, CoveredFiles, CoveredFunctions,
110  CoveredLines;
111  Printf("COVERAGE:\n");
112  for (size_t i = 1; i < GetNumPCs(); i++) {
113  if (!PCs[i]) continue;
114  std::string FileStr = DescribePC("%s", PCs[i]);
115  if (!IsInterestingCoverageFile(FileStr)) continue;
116  std::string FixedPCStr = DescribePC("%p", PCs[i]);
117  std::string FunctionStr = DescribePC("%F", PCs[i]);
118  std::string LineStr = DescribePC("%l", PCs[i]);
119  char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++?
120  void *OffsetRaw = nullptr;
121  if (!EF->__sanitizer_get_module_and_offset_for_pc(
122  reinterpret_cast<void *>(PCs[i]), ModulePathRaw,
123  sizeof(ModulePathRaw), &OffsetRaw))
124  continue;
125  std::string Module = ModulePathRaw;
126  uintptr_t FixedPC = std::stol(FixedPCStr, 0, 16);
127  uintptr_t PcOffset = reinterpret_cast<uintptr_t>(OffsetRaw);
128  ModuleOffsets[Module] = FixedPC - PcOffset;
129  CoveredPCsPerModule[Module].push_back(PcOffset);
130  CoveredFunctions.insert(FunctionStr);
131  CoveredFiles.insert(FileStr);
132  CoveredDirs.insert(DirName(FileStr));
133  if (!CoveredLines.insert(FileStr + ":" + LineStr).second)
134  continue;
135  Printf("COVERED: %s %s:%s\n", FunctionStr.c_str(),
136  FileStr.c_str(), LineStr.c_str());
137  }
138 
139  std::string CoveredDirsStr;
140  for (auto &Dir : CoveredDirs) {
141  if (!CoveredDirsStr.empty())
142  CoveredDirsStr += ",";
143  CoveredDirsStr += Dir;
144  }
145  Printf("COVERED_DIRS: %s\n", CoveredDirsStr.c_str());
146 
147  for (auto &M : CoveredPCsPerModule) {
148  std::set<std::string> UncoveredFiles, UncoveredFunctions;
149  std::map<std::string, std::set<int> > UncoveredLines; // Func+File => lines
150  auto &ModuleName = M.first;
151  auto &CoveredOffsets = M.second;
152  uintptr_t ModuleOffset = ModuleOffsets[ModuleName];
153  std::sort(CoveredOffsets.begin(), CoveredOffsets.end());
154  Printf("MODULE_WITH_COVERAGE: %s\n", ModuleName.c_str());
155  // sancov does not yet fully support DSOs.
156  // std::string Cmd = "sancov -print-coverage-pcs " + ModuleName;
157  std::string Cmd = "objdump -d " + ModuleName +
158  " | grep 'call.*__sanitizer_cov_trace_pc_guard' | awk -F: '{print $1}'";
159  std::string SanCovOutput;
160  if (!ExecuteCommandAndReadOutput(Cmd, &SanCovOutput)) {
161  Printf("INFO: Command failed: %s\n", Cmd.c_str());
162  continue;
163  }
164  std::istringstream ISS(SanCovOutput);
165  std::string S;
166  while (std::getline(ISS, S, '\n')) {
167  uintptr_t PcOffset = std::stol(S, 0, 16);
168  if (!std::binary_search(CoveredOffsets.begin(), CoveredOffsets.end(),
169  PcOffset)) {
170  uintptr_t PC = ModuleOffset + PcOffset;
171  auto FileStr = DescribePC("%s", PC);
172  if (!IsInterestingCoverageFile(FileStr)) continue;
173  if (CoveredFiles.count(FileStr) == 0) {
174  UncoveredFiles.insert(FileStr);
175  continue;
176  }
177  auto FunctionStr = DescribePC("%F", PC);
178  if (CoveredFunctions.count(FunctionStr) == 0) {
179  UncoveredFunctions.insert(FunctionStr);
180  continue;
181  }
182  std::string LineStr = DescribePC("%l", PC);
183  uintptr_t Line = std::stoi(LineStr);
184  std::string FileLineStr = FileStr + ":" + LineStr;
185  if (CoveredLines.count(FileLineStr) == 0)
186  UncoveredLines[FunctionStr + " " + FileStr].insert(Line);
187  }
188  }
189  for (auto &FileLine: UncoveredLines)
190  for (int Line : FileLine.second)
191  Printf("UNCOVERED_LINE: %s:%d\n", FileLine.first.c_str(), Line);
192  for (auto &Func : UncoveredFunctions)
193  Printf("UNCOVERED_FUNC: %s\n", Func.c_str());
194  for (auto &File : UncoveredFiles)
195  Printf("UNCOVERED_FILE: %s\n", File.c_str());
196  }
197 }
198 
200  __sanitizer_dump_coverage(PCs, GetNumPCs());
201 }
202 
203 // Value profile.
204 // We keep track of various values that affect control flow.
205 // These values are inserted into a bit-set-based hash map.
206 // Every new bit in the map is treated as a new coverage.
207 //
208 // For memcmp/strcmp/etc the interesting value is the length of the common
209 // prefix of the parameters.
210 // For cmp instructions the interesting value is a XOR of the parameters.
211 // The interesting value is mixed up with the PC and is then added to the map.
212 
214 void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
215  size_t n) {
216  if (!n) return;
217  size_t Len = std::min(n, (size_t)32);
218  const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1);
219  const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2);
220  size_t I = 0;
221  for (; I < Len; I++)
222  if (A1[I] != A2[I])
223  break;
224  size_t PC = reinterpret_cast<size_t>(caller_pc);
225  size_t Idx = I;
226  // if (I < Len)
227  // Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1;
228  TPC.HandleValueProfile((PC & 4095) | (Idx << 12));
229 }
230 
232 void TracePC::AddValueForStrcmp(void *caller_pc, const char *s1, const char *s2,
233  size_t n) {
234  if (!n) return;
235  size_t Len = std::min(n, (size_t)32);
236  const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1);
237  const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2);
238  size_t I = 0;
239  for (; I < Len; I++)
240  if (A1[I] != A2[I] || A1[I] == 0)
241  break;
242  size_t PC = reinterpret_cast<size_t>(caller_pc);
243  size_t Idx = I;
244  // if (I < Len && A1[I])
245  // Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1;
246  TPC.HandleValueProfile((PC & 4095) | (Idx << 12));
247 }
248 
249 template <class T>
251 #ifdef __clang__ // g++ can't handle this __attribute__ here :(
252 __attribute__((always_inline))
253 #endif // __clang__
254 void TracePC::HandleCmp(void *PC, T Arg1, T Arg2) {
255  uintptr_t PCuint = reinterpret_cast<uintptr_t>(PC);
256  uint64_t ArgXor = Arg1 ^ Arg2;
257  uint64_t ArgDistance = __builtin_popcountl(ArgXor) + 1; // [1,65]
258  uintptr_t Idx = ((PCuint & 4095) + 1) * ArgDistance;
259  if (sizeof(T) == 4)
260  TORC4.Insert(ArgXor, Arg1, Arg2);
261  else if (sizeof(T) == 8)
262  TORC8.Insert(ArgXor, Arg1, Arg2);
263  HandleValueProfile(Idx);
264 }
265 
266 } // namespace fuzzer
267 
268 extern "C" {
269 __attribute__((visibility("default")))
270 void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
271  uintptr_t PC = (uintptr_t)__builtin_return_address(0);
272  fuzzer::TPC.HandleTrace(Guard, PC);
273 }
274 
275 __attribute__((visibility("default")))
276 void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) {
277  fuzzer::TPC.HandleInit(Start, Stop);
278 }
279 
280 __attribute__((visibility("default")))
281 void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) {
282  uintptr_t PC = (uintptr_t)__builtin_return_address(0);
283  fuzzer::TPC.HandleCallerCallee(PC, Callee);
284 }
285 
286 __attribute__((visibility("default")))
287 void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) {
288  fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
289 }
290 __attribute__((visibility("default")))
291 void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) {
292  fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
293 }
294 __attribute__((visibility("default")))
295 void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) {
296  fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
297 }
298 __attribute__((visibility("default")))
299 void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) {
300  fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
301 }
302 
303 __attribute__((visibility("default")))
304 void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
305  uint64_t N = Cases[0];
306  uint64_t ValSizeInBits = Cases[1];
307  uint64_t *Vals = Cases + 2;
308  // Skip the most common and the most boring case.
309  if (Vals[N - 1] < 256 && Val < 256)
310  return;
311  char *PC = (char*)__builtin_return_address(0);
312  size_t i;
313  uint64_t Token = 0;
314  for (i = 0; i < N; i++) {
315  Token = Val ^ Vals[i];
316  if (Val < Vals[i])
317  break;
318  }
319 
320  if (ValSizeInBits == 16)
321  fuzzer::TPC.HandleCmp(PC + i, static_cast<uint16_t>(Token), (uint16_t)(0));
322  else if (ValSizeInBits == 32)
323  fuzzer::TPC.HandleCmp(PC + i, static_cast<uint32_t>(Token), (uint32_t)(0));
324  else
325  fuzzer::TPC.HandleCmp(PC + i, Token, (uint64_t)(0));
326 }
327 
328 __attribute__((visibility("default")))
329 void __sanitizer_cov_trace_div4(uint32_t Val) {
330  fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint32_t)0);
331 }
332 __attribute__((visibility("default")))
333 void __sanitizer_cov_trace_div8(uint64_t Val) {
334  fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint64_t)0);
335 }
336 __attribute__((visibility("default")))
337 void __sanitizer_cov_trace_gep(uintptr_t Idx) {
338  fuzzer::TPC.HandleCmp(__builtin_return_address(0), Idx, (uintptr_t)0);
339 }
340 
341 } // extern "C"
void AddValueForStrcmp(void *caller_pc, const char *s1, const char *s2, size_t n)
__attribute__((visibility("default"))) void __sanitizer_cov_trace_pc_guard(uint32_t *Guard)
uint64_t Token
TableOfRecentCompares< uint32_t, kTORCSize > TORC4
Definition: FuzzerTracePC.h:84
size_t i
bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out)
Definition: FuzzerUtil.cpp:208
void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, size_t n)
#define ATTRIBUTE_NO_SANITIZE_MEMORY
Definition: FuzzerDefs.h:51
void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee)
ExternalFunctions * EF
void HandleTrace(uint32_t *guard, uintptr_t PC)
std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC)
Definition: FuzzerUtil.cpp:182
uint64_t * Cases
Maximum length of the test input libFuzzer tries to guess a good value based on the corpus and reports it always prefer smaller inputs during the corpus shuffle When libFuzzer itself reports a bug this exit code will be used If indicates the maximal total time in seconds to run the fuzzer minimizes the provided crash input Use with etc Experimental Use value profile to guide fuzzing Number of simultaneous worker processes to run the jobs If min(jobs, NumberOfCpuCores()/2)\" is used.") FUZZER_FLAG_INT(reload
TracePC TPC
void Printf(const char *Fmt,...)
Definition: FuzzerIO.cpp:109
#define P(N)
void HandleValueProfile(size_t Value)
Definition: FuzzerTracePC.h:52
uint64_t ValSizeInBits
size_t GetTotalPCCoverage()
void HandleCmp(void *PC, T Arg1, T Arg2)
void HandleInit(uint32_t *start, uint32_t *stop)
uint64_t * Vals
void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC)
Definition: FuzzerUtil.cpp:191
void Insert(size_t Idx, T Arg1, T Arg2)
Definition: FuzzerTracePC.h:34
size_t GetNumPCs() const
Definition: FuzzerTracePC.h:89
#define ATTRIBUTE_TARGET_POPCNT
Definition: FuzzerDefs.h:44
void InitializePrintNewPCs()
TableOfRecentCompares< uint64_t, kTORCSize > TORC8
Definition: FuzzerTracePC.h:85
#define I(x, y, z)
Definition: MD5.cpp:54
#define N
uint64_t Arg2
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
std::string DirName(const std::string &FileName)
static bool IsInterestingCoverageFile(std::string &File)
char * PC