1 //===- FuzzerTraceState.cpp - Trace-based fuzzer mutator ------------------===// 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 // This file implements a mutation algorithm based on instruction traces and 10 // on taint analysis feedback from DFSan. 11 // 12 // Instruction traces are special hooks inserted by the compiler around 13 // interesting instructions. Currently supported traces: 14 // * __sanitizer_cov_trace_cmp -- inserted before every ICMP instruction, 15 // receives the type, size and arguments of ICMP. 16 // 17 // Every time a traced event is intercepted we analyse the data involved 18 // in the event and suggest a mutation for future executions. 19 // For example if 4 bytes of data that derive from input bytes {4,5,6,7} 20 // are compared with a constant 12345, 21 // we try to insert 12345, 12344, 12346 into bytes 22 // {4,5,6,7} of the next fuzzed inputs. 23 // 24 // The fuzzer can work only with the traces, or with both traces and DFSan. 25 // 26 // DataFlowSanitizer (DFSan) is a tool for 27 // generalised dynamic data flow (taint) analysis: 28 // http://clang.llvm.org/docs/DataFlowSanitizer.html . 29 // 30 // The approach with DFSan-based fuzzing has some similarity to 31 // "Taint-based Directed Whitebox Fuzzing" 32 // by Vijay Ganesh & Tim Leek & Martin Rinard: 33 // http://dspace.mit.edu/openaccess-disseminate/1721.1/59320, 34 // but it uses a full blown LLVM IR taint analysis and separate instrumentation 35 // to analyze all of the "attack points" at once. 36 // 37 // Workflow with DFSan: 38 // * lib/Fuzzer/Fuzzer*.cpp is compiled w/o any instrumentation. 39 // * The code under test is compiled with DFSan *and* with instruction traces. 40 // * Every call to HOOK(a,b) is replaced by DFSan with 41 // __dfsw_HOOK(a, b, label(a), label(b)) so that __dfsw_HOOK 42 // gets all the taint labels for the arguments. 43 // * At the Fuzzer startup we assign a unique DFSan label 44 // to every byte of the input string (Fuzzer::CurrentUnitData) so that 45 // for any chunk of data we know which input bytes it has derived from. 46 // * The __dfsw_* functions (implemented in this file) record the 47 // parameters (i.e. the application data and the corresponding taint labels) 48 // in a global state. 49 // 50 // Parts of this code will not function when DFSan is not linked in. 51 // Instead of using ifdefs and thus requiring a separate build of lib/Fuzzer 52 // we redeclare the dfsan_* interface functions as weak and check if they 53 // are nullptr before calling. 54 // If this approach proves to be useful we may add attribute(weak) to the 55 // dfsan declarations in dfsan_interface.h 56 // 57 // This module is in the "proof of concept" stage. 58 // It is capable of solving only the simplest puzzles 59 // like test/dfsan/DFSanSimpleCmpTest.cpp. 60 //===----------------------------------------------------------------------===// 61 62 /* Example of manual usage (-fsanitize=dataflow is optional): 63 ( 64 cd $LLVM/lib/Fuzzer/ 65 clang -fPIC -c -g -O2 -std=c++11 Fuzzer*.cpp 66 clang++ -O0 -std=c++11 -fsanitize-coverage=edge,trace-cmp \ 67 -fsanitize=dataflow \ 68 test/SimpleCmpTest.cpp Fuzzer*.o 69 ./a.out -use_traces=1 70 ) 71 */ 72 73 #include "FuzzerDFSan.h" 74 #include "FuzzerInternal.h" 75 76 #include <algorithm> 77 #include <cstring> 78 #include <thread> 79 #include <map> 80 81 #if !LLVM_FUZZER_SUPPORTS_DFSAN 82 // Stubs for dfsan for platforms where dfsan does not exist and weak 83 // functions don't work. 84 extern "C" { 85 dfsan_label dfsan_create_label(const char *desc, void *userdata) { return 0; } 86 void dfsan_set_label(dfsan_label label, void *addr, size_t size) {} 87 void dfsan_add_label(dfsan_label label, void *addr, size_t size) {} 88 const struct dfsan_label_info *dfsan_get_label_info(dfsan_label label) { 89 return nullptr; 90 } 91 dfsan_label dfsan_read_label(const void *addr, size_t size) { return 0; } 92 } // extern "C" 93 #endif // !LLVM_FUZZER_SUPPORTS_DFSAN 94 95 namespace fuzzer { 96 97 // These values are copied from include/llvm/IR/InstrTypes.h. 98 // We do not include the LLVM headers here to remain independent. 99 // If these values ever change, an assertion in ComputeCmp will fail. 100 enum Predicate { 101 ICMP_EQ = 32, ///< equal 102 ICMP_NE = 33, ///< not equal 103 ICMP_UGT = 34, ///< unsigned greater than 104 ICMP_UGE = 35, ///< unsigned greater or equal 105 ICMP_ULT = 36, ///< unsigned less than 106 ICMP_ULE = 37, ///< unsigned less or equal 107 ICMP_SGT = 38, ///< signed greater than 108 ICMP_SGE = 39, ///< signed greater or equal 109 ICMP_SLT = 40, ///< signed less than 110 ICMP_SLE = 41, ///< signed less or equal 111 }; 112 113 template <class U, class S> 114 bool ComputeCmp(size_t CmpType, U Arg1, U Arg2) { 115 switch(CmpType) { 116 case ICMP_EQ : return Arg1 == Arg2; 117 case ICMP_NE : return Arg1 != Arg2; 118 case ICMP_UGT: return Arg1 > Arg2; 119 case ICMP_UGE: return Arg1 >= Arg2; 120 case ICMP_ULT: return Arg1 < Arg2; 121 case ICMP_ULE: return Arg1 <= Arg2; 122 case ICMP_SGT: return (S)Arg1 > (S)Arg2; 123 case ICMP_SGE: return (S)Arg1 >= (S)Arg2; 124 case ICMP_SLT: return (S)Arg1 < (S)Arg2; 125 case ICMP_SLE: return (S)Arg1 <= (S)Arg2; 126 default: assert(0 && "unsupported CmpType"); 127 } 128 return false; 129 } 130 131 static bool ComputeCmp(size_t CmpSize, size_t CmpType, uint64_t Arg1, 132 uint64_t Arg2) { 133 if (CmpSize == 8) return ComputeCmp<uint64_t, int64_t>(CmpType, Arg1, Arg2); 134 if (CmpSize == 4) return ComputeCmp<uint32_t, int32_t>(CmpType, Arg1, Arg2); 135 if (CmpSize == 2) return ComputeCmp<uint16_t, int16_t>(CmpType, Arg1, Arg2); 136 if (CmpSize == 1) return ComputeCmp<uint8_t, int8_t>(CmpType, Arg1, Arg2); 137 // Other size, == 138 if (CmpType == ICMP_EQ) return Arg1 == Arg2; 139 // assert(0 && "unsupported cmp and type size combination"); 140 return true; 141 } 142 143 // As a simplification we use the range of input bytes instead of a set of input 144 // bytes. 145 struct LabelRange { 146 uint16_t Beg, End; // Range is [Beg, End), thus Beg==End is an empty range. 147 148 LabelRange(uint16_t Beg = 0, uint16_t End = 0) : Beg(Beg), End(End) {} 149 150 static LabelRange Join(LabelRange LR1, LabelRange LR2) { 151 if (LR1.Beg == LR1.End) return LR2; 152 if (LR2.Beg == LR2.End) return LR1; 153 return {std::min(LR1.Beg, LR2.Beg), std::max(LR1.End, LR2.End)}; 154 } 155 LabelRange &Join(LabelRange LR) { 156 return *this = Join(*this, LR); 157 } 158 static LabelRange Singleton(const dfsan_label_info *LI) { 159 uint16_t Idx = (uint16_t)(uintptr_t)LI->userdata; 160 assert(Idx > 0); 161 return {(uint16_t)(Idx - 1), Idx}; 162 } 163 }; 164 165 // For now, very simple: put Size bytes of Data at position Pos. 166 struct TraceBasedMutation { 167 uint32_t Pos; 168 Word W; 169 }; 170 171 // Declared as static globals for faster checks inside the hooks. 172 static bool RecordingTraces = false; 173 static bool RecordingMemcmp = false; 174 175 class TraceState { 176 public: 177 TraceState(MutationDispatcher &MD, const FuzzingOptions &Options, 178 const Fuzzer *F) 179 : MD(MD), Options(Options), F(F) {} 180 181 LabelRange GetLabelRange(dfsan_label L); 182 void DFSanCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType, 183 uint64_t Arg1, uint64_t Arg2, dfsan_label L1, 184 dfsan_label L2); 185 void DFSanMemcmpCallback(size_t CmpSize, const uint8_t *Data1, 186 const uint8_t *Data2, dfsan_label L1, 187 dfsan_label L2); 188 void DFSanSwitchCallback(uint64_t PC, size_t ValSizeInBits, uint64_t Val, 189 size_t NumCases, uint64_t *Cases, dfsan_label L); 190 void TraceCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType, 191 uint64_t Arg1, uint64_t Arg2); 192 void TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1, 193 const uint8_t *Data2); 194 195 void TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits, uint64_t Val, 196 size_t NumCases, uint64_t *Cases); 197 int TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData, 198 size_t DataSize); 199 int TryToAddDesiredData(const uint8_t *PresentData, 200 const uint8_t *DesiredData, size_t DataSize); 201 202 void StartTraceRecording() { 203 if (!Options.UseTraces && !Options.UseMemcmp) 204 return; 205 RecordingTraces = Options.UseTraces; 206 RecordingMemcmp = Options.UseMemcmp; 207 NumMutations = 0; 208 MD.ClearAutoDictionary(); 209 } 210 211 void StopTraceRecording() { 212 if (!RecordingTraces && !RecordingMemcmp) 213 return; 214 RecordingTraces = false; 215 RecordingMemcmp = false; 216 for (size_t i = 0; i < NumMutations; i++) { 217 auto &M = Mutations[i]; 218 if (Options.Verbosity >= 2) { 219 AutoDictUnitCounts[M.W]++; 220 AutoDictAdds++; 221 if ((AutoDictAdds & (AutoDictAdds - 1)) == 0) { 222 typedef std::pair<size_t, Word> CU; 223 std::vector<CU> CountedUnits; 224 for (auto &I : AutoDictUnitCounts) 225 CountedUnits.push_back(std::make_pair(I.second, I.first)); 226 std::sort(CountedUnits.begin(), CountedUnits.end(), 227 [](const CU &a, const CU &b) { return a.first > b.first; }); 228 Printf("AutoDict:\n"); 229 for (auto &I : CountedUnits) { 230 Printf(" %zd ", I.first); 231 PrintASCII(I.second); 232 Printf("\n"); 233 } 234 } 235 } 236 MD.AddWordToAutoDictionary(M.W, M.Pos); 237 } 238 } 239 240 void AddMutation(uint32_t Pos, uint32_t Size, const uint8_t *Data) { 241 if (NumMutations >= kMaxMutations) return; 242 auto &M = Mutations[NumMutations++]; 243 M.Pos = Pos; 244 M.W.Set(Data, Size); 245 } 246 247 void AddMutation(uint32_t Pos, uint32_t Size, uint64_t Data) { 248 assert(Size <= sizeof(Data)); 249 AddMutation(Pos, Size, reinterpret_cast<uint8_t*>(&Data)); 250 } 251 252 void EnsureDfsanLabels(size_t Size) { 253 for (; LastDfsanLabel < Size; LastDfsanLabel++) { 254 dfsan_label L = dfsan_create_label("input", (void *)(LastDfsanLabel + 1)); 255 // We assume that no one else has called dfsan_create_label before. 256 if (L != LastDfsanLabel + 1) { 257 Printf("DFSan labels are not starting from 1, exiting\n"); 258 exit(1); 259 } 260 } 261 } 262 263 private: 264 bool IsTwoByteData(uint64_t Data) { 265 int64_t Signed = static_cast<int64_t>(Data); 266 Signed >>= 16; 267 return Signed == 0 || Signed == -1L; 268 } 269 270 // We don't want to create too many trace-based mutations as it is both 271 // expensive and useless. So after some number of mutations is collected, 272 // start rejecting some of them. The more there are mutations the more we 273 // reject. 274 bool WantToHandleOneMoreMutation() { 275 const size_t FirstN = 64; 276 // Gladly handle first N mutations. 277 if (NumMutations <= FirstN) return true; 278 size_t Diff = NumMutations - FirstN; 279 size_t DiffLog = sizeof(long) * 8 - __builtin_clzl((long)Diff); 280 assert(DiffLog > 0 && DiffLog < 64); 281 bool WantThisOne = MD.GetRand()(1 << DiffLog) == 0; // 1 out of DiffLog. 282 return WantThisOne; 283 } 284 285 static const size_t kMaxMutations = 1 << 16; 286 size_t NumMutations; 287 TraceBasedMutation Mutations[kMaxMutations]; 288 LabelRange LabelRanges[1 << (sizeof(dfsan_label) * 8)]; 289 size_t LastDfsanLabel = 0; 290 MutationDispatcher &MD; 291 const FuzzingOptions Options; 292 const Fuzzer *F; 293 std::map<Word, size_t> AutoDictUnitCounts; 294 size_t AutoDictAdds = 0; 295 }; 296 297 298 LabelRange TraceState::GetLabelRange(dfsan_label L) { 299 LabelRange &LR = LabelRanges[L]; 300 if (LR.Beg < LR.End || L == 0) 301 return LR; 302 const dfsan_label_info *LI = dfsan_get_label_info(L); 303 if (LI->l1 || LI->l2) 304 return LR = LabelRange::Join(GetLabelRange(LI->l1), GetLabelRange(LI->l2)); 305 return LR = LabelRange::Singleton(LI); 306 } 307 308 void TraceState::DFSanCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType, 309 uint64_t Arg1, uint64_t Arg2, dfsan_label L1, 310 dfsan_label L2) { 311 assert(ReallyHaveDFSan()); 312 if (!RecordingTraces || !F->InFuzzingThread()) return; 313 if (L1 == 0 && L2 == 0) 314 return; // Not actionable. 315 if (L1 != 0 && L2 != 0) 316 return; // Probably still actionable. 317 bool Res = ComputeCmp(CmpSize, CmpType, Arg1, Arg2); 318 uint64_t Data = L1 ? Arg2 : Arg1; 319 LabelRange LR = L1 ? GetLabelRange(L1) : GetLabelRange(L2); 320 321 for (size_t Pos = LR.Beg; Pos + CmpSize <= LR.End; Pos++) { 322 AddMutation(Pos, CmpSize, Data); 323 AddMutation(Pos, CmpSize, Data + 1); 324 AddMutation(Pos, CmpSize, Data - 1); 325 } 326 327 if (CmpSize > (size_t)(LR.End - LR.Beg)) 328 AddMutation(LR.Beg, (unsigned)(LR.End - LR.Beg), Data); 329 330 331 if (Options.Verbosity >= 3) 332 Printf("DFSanCmpCallback: PC %lx S %zd T %zd A1 %llx A2 %llx R %d L1 %d L2 " 333 "%d MU %zd\n", 334 PC, CmpSize, CmpType, Arg1, Arg2, Res, L1, L2, NumMutations); 335 } 336 337 void TraceState::DFSanMemcmpCallback(size_t CmpSize, const uint8_t *Data1, 338 const uint8_t *Data2, dfsan_label L1, 339 dfsan_label L2) { 340 341 assert(ReallyHaveDFSan()); 342 if (!RecordingMemcmp || !F->InFuzzingThread()) return; 343 if (L1 == 0 && L2 == 0) 344 return; // Not actionable. 345 if (L1 != 0 && L2 != 0) 346 return; // Probably still actionable. 347 348 const uint8_t *Data = L1 ? Data2 : Data1; 349 LabelRange LR = L1 ? GetLabelRange(L1) : GetLabelRange(L2); 350 for (size_t Pos = LR.Beg; Pos + CmpSize <= LR.End; Pos++) { 351 AddMutation(Pos, CmpSize, Data); 352 if (Options.Verbosity >= 3) 353 Printf("DFSanMemcmpCallback: Pos %d Size %d\n", Pos, CmpSize); 354 } 355 } 356 357 void TraceState::DFSanSwitchCallback(uint64_t PC, size_t ValSizeInBits, 358 uint64_t Val, size_t NumCases, 359 uint64_t *Cases, dfsan_label L) { 360 assert(ReallyHaveDFSan()); 361 if (!RecordingTraces || !F->InFuzzingThread()) return; 362 if (!L) return; // Not actionable. 363 LabelRange LR = GetLabelRange(L); 364 size_t ValSize = ValSizeInBits / 8; 365 bool TryShort = IsTwoByteData(Val); 366 for (size_t i = 0; i < NumCases; i++) 367 TryShort &= IsTwoByteData(Cases[i]); 368 369 for (size_t Pos = LR.Beg; Pos + ValSize <= LR.End; Pos++) 370 for (size_t i = 0; i < NumCases; i++) 371 AddMutation(Pos, ValSize, Cases[i]); 372 373 if (TryShort) 374 for (size_t Pos = LR.Beg; Pos + 2 <= LR.End; Pos++) 375 for (size_t i = 0; i < NumCases; i++) 376 AddMutation(Pos, 2, Cases[i]); 377 378 if (Options.Verbosity >= 3) 379 Printf("DFSanSwitchCallback: PC %lx Val %zd SZ %zd # %zd L %d: {%d, %d} " 380 "TryShort %d\n", 381 PC, Val, ValSize, NumCases, L, LR.Beg, LR.End, TryShort); 382 } 383 384 int TraceState::TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData, 385 size_t DataSize) { 386 if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0; 387 const uint8_t *UnitData; 388 auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData); 389 int Res = 0; 390 const uint8_t *Beg = UnitData; 391 const uint8_t *End = Beg + UnitSize; 392 for (const uint8_t *Cur = Beg; Cur < End; Cur++) { 393 Cur = (uint8_t *)memmem(Cur, End - Cur, &PresentData, DataSize); 394 if (!Cur) 395 break; 396 size_t Pos = Cur - Beg; 397 assert(Pos < UnitSize); 398 AddMutation(Pos, DataSize, DesiredData); 399 AddMutation(Pos, DataSize, DesiredData + 1); 400 AddMutation(Pos, DataSize, DesiredData - 1); 401 Res++; 402 } 403 return Res; 404 } 405 406 int TraceState::TryToAddDesiredData(const uint8_t *PresentData, 407 const uint8_t *DesiredData, 408 size_t DataSize) { 409 if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0; 410 const uint8_t *UnitData; 411 auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData); 412 int Res = 0; 413 const uint8_t *Beg = UnitData; 414 const uint8_t *End = Beg + UnitSize; 415 for (const uint8_t *Cur = Beg; Cur < End; Cur++) { 416 Cur = (uint8_t *)memmem(Cur, End - Cur, PresentData, DataSize); 417 if (!Cur) 418 break; 419 size_t Pos = Cur - Beg; 420 assert(Pos < UnitSize); 421 AddMutation(Pos, DataSize, DesiredData); 422 Res++; 423 } 424 return Res; 425 } 426 427 void TraceState::TraceCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType, 428 uint64_t Arg1, uint64_t Arg2) { 429 if (!RecordingTraces || !F->InFuzzingThread()) return; 430 if ((CmpType == ICMP_EQ || CmpType == ICMP_NE) && Arg1 == Arg2) 431 return; // No reason to mutate. 432 int Added = 0; 433 Added += TryToAddDesiredData(Arg1, Arg2, CmpSize); 434 Added += TryToAddDesiredData(Arg2, Arg1, CmpSize); 435 if (!Added && CmpSize == 4 && IsTwoByteData(Arg1) && IsTwoByteData(Arg2)) { 436 Added += TryToAddDesiredData(Arg1, Arg2, 2); 437 Added += TryToAddDesiredData(Arg2, Arg1, 2); 438 } 439 if (Options.Verbosity >= 3 && Added) 440 Printf("TraceCmp %zd/%zd: %p %zd %zd\n", CmpSize, CmpType, PC, Arg1, Arg2); 441 } 442 443 void TraceState::TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1, 444 const uint8_t *Data2) { 445 if (!RecordingMemcmp || !F->InFuzzingThread()) return; 446 CmpSize = std::min(CmpSize, Word::GetMaxSize()); 447 int Added2 = TryToAddDesiredData(Data1, Data2, CmpSize); 448 int Added1 = TryToAddDesiredData(Data2, Data1, CmpSize); 449 if ((Added1 || Added2) && Options.Verbosity >= 3) { 450 Printf("MemCmp Added %d%d: ", Added1, Added2); 451 if (Added1) PrintASCII(Data1, CmpSize); 452 if (Added2) PrintASCII(Data2, CmpSize); 453 Printf("\n"); 454 } 455 } 456 457 void TraceState::TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits, 458 uint64_t Val, size_t NumCases, 459 uint64_t *Cases) { 460 if (!RecordingTraces || !F->InFuzzingThread()) return; 461 size_t ValSize = ValSizeInBits / 8; 462 bool TryShort = IsTwoByteData(Val); 463 for (size_t i = 0; i < NumCases; i++) 464 TryShort &= IsTwoByteData(Cases[i]); 465 466 if (Options.Verbosity >= 3) 467 Printf("TraceSwitch: %p %zd # %zd; TryShort %d\n", PC, Val, NumCases, 468 TryShort); 469 470 for (size_t i = 0; i < NumCases; i++) { 471 TryToAddDesiredData(Val, Cases[i], ValSize); 472 if (TryShort) 473 TryToAddDesiredData(Val, Cases[i], 2); 474 } 475 } 476 477 static TraceState *TS; 478 479 void Fuzzer::StartTraceRecording() { 480 if (!TS) return; 481 TS->StartTraceRecording(); 482 } 483 484 void Fuzzer::StopTraceRecording() { 485 if (!TS) return; 486 TS->StopTraceRecording(); 487 } 488 489 void Fuzzer::AssignTaintLabels(uint8_t *Data, size_t Size) { 490 if (!Options.UseTraces && !Options.UseMemcmp) return; 491 if (!ReallyHaveDFSan()) return; 492 TS->EnsureDfsanLabels(Size); 493 for (size_t i = 0; i < Size; i++) 494 dfsan_set_label(i + 1, &Data[i], 1); 495 } 496 497 void Fuzzer::InitializeTraceState() { 498 if (!Options.UseTraces && !Options.UseMemcmp) return; 499 TS = new TraceState(MD, Options, this); 500 } 501 502 static size_t InternalStrnlen(const char *S, size_t MaxLen) { 503 size_t Len = 0; 504 for (; Len < MaxLen && S[Len]; Len++) {} 505 return Len; 506 } 507 508 } // namespace fuzzer 509 510 using fuzzer::TS; 511 using fuzzer::RecordingTraces; 512 using fuzzer::RecordingMemcmp; 513 514 extern "C" { 515 void __dfsw___sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1, 516 uint64_t Arg2, dfsan_label L0, 517 dfsan_label L1, dfsan_label L2) { 518 if (!RecordingTraces) return; 519 assert(L0 == 0); 520 uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); 521 uint64_t CmpSize = (SizeAndType >> 32) / 8; 522 uint64_t Type = (SizeAndType << 32) >> 32; 523 TS->DFSanCmpCallback(PC, CmpSize, Type, Arg1, Arg2, L1, L2); 524 } 525 526 void __dfsw___sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases, 527 dfsan_label L1, dfsan_label L2) { 528 if (!RecordingTraces) return; 529 uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); 530 TS->DFSanSwitchCallback(PC, Cases[1], Val, Cases[0], Cases+2, L1); 531 } 532 533 void dfsan_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2, 534 size_t n, dfsan_label s1_label, 535 dfsan_label s2_label, dfsan_label n_label) { 536 if (!RecordingMemcmp) return; 537 dfsan_label L1 = dfsan_read_label(s1, n); 538 dfsan_label L2 = dfsan_read_label(s2, n); 539 TS->DFSanMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1), 540 reinterpret_cast<const uint8_t *>(s2), L1, L2); 541 } 542 543 void dfsan_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2, 544 size_t n, dfsan_label s1_label, 545 dfsan_label s2_label, dfsan_label n_label) { 546 if (!RecordingMemcmp) return; 547 n = std::min(n, fuzzer::InternalStrnlen(s1, n)); 548 n = std::min(n, fuzzer::InternalStrnlen(s2, n)); 549 dfsan_label L1 = dfsan_read_label(s1, n); 550 dfsan_label L2 = dfsan_read_label(s2, n); 551 TS->DFSanMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1), 552 reinterpret_cast<const uint8_t *>(s2), L1, L2); 553 } 554 555 void dfsan_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2, 556 dfsan_label s1_label, dfsan_label s2_label) { 557 if (!RecordingMemcmp) return; 558 size_t Len1 = strlen(s1); 559 size_t Len2 = strlen(s2); 560 size_t N = std::min(Len1, Len2); 561 if (N <= 1) return; // Not interesting. 562 dfsan_label L1 = dfsan_read_label(s1, Len1); 563 dfsan_label L2 = dfsan_read_label(s2, Len2); 564 TS->DFSanMemcmpCallback(N, reinterpret_cast<const uint8_t *>(s1), 565 reinterpret_cast<const uint8_t *>(s2), L1, L2); 566 } 567 568 // We may need to avoid defining weak hooks to stay compatible with older clang. 569 #ifndef LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS 570 # define LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS 1 571 #endif 572 573 #if LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS 574 void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1, 575 const void *s2, size_t n, int result) { 576 if (!RecordingMemcmp) return; 577 if (result == 0) return; // No reason to mutate. 578 if (n <= 1) return; // Not interesting. 579 TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1), 580 reinterpret_cast<const uint8_t *>(s2)); 581 } 582 583 void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1, 584 const char *s2, size_t n, int result) { 585 if (!RecordingMemcmp) return; 586 if (result == 0) return; // No reason to mutate. 587 size_t Len1 = fuzzer::InternalStrnlen(s1, n); 588 size_t Len2 = fuzzer::InternalStrnlen(s2, n); 589 n = std::min(n, Len1); 590 n = std::min(n, Len2); 591 if (n <= 1) return; // Not interesting. 592 TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1), 593 reinterpret_cast<const uint8_t *>(s2)); 594 } 595 596 void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, 597 const char *s2, int result) { 598 if (!RecordingMemcmp) return; 599 if (result == 0) return; // No reason to mutate. 600 size_t Len1 = strlen(s1); 601 size_t Len2 = strlen(s2); 602 size_t N = std::min(Len1, Len2); 603 if (N <= 1) return; // Not interesting. 604 TS->TraceMemcmpCallback(N, reinterpret_cast<const uint8_t *>(s1), 605 reinterpret_cast<const uint8_t *>(s2)); 606 } 607 608 #endif // LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS 609 610 __attribute__((visibility("default"))) 611 void __sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1, 612 uint64_t Arg2) { 613 if (!RecordingTraces) return; 614 uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); 615 uint64_t CmpSize = (SizeAndType >> 32) / 8; 616 uint64_t Type = (SizeAndType << 32) >> 32; 617 TS->TraceCmpCallback(PC, CmpSize, Type, Arg1, Arg2); 618 } 619 620 __attribute__((visibility("default"))) 621 void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { 622 if (!RecordingTraces) return; 623 uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); 624 TS->TraceSwitchCallback(PC, Cases[1], Val, Cases[0], Cases + 2); 625 } 626 627 } // extern "C" 628