[libFuzzer] debug prints in tracing
[oota-llvm.git] / lib / Fuzzer / FuzzerTraceState.cpp
index f0537c005800bb3476789eb0a22f67c67ecdbcb0..36fc6dcfd23e8393fe17bfc91a57cb870d5b6ae3 100644 (file)
@@ -46,8 +46,6 @@
 //   * The __dfsw_* functions (implemented in this file) record the
 //     parameters (i.e. the application data and the corresponding taint labels)
 //     in a global state.
-//   * Fuzzer::ApplyTraceBasedMutation() tries to use the data recorded
-//     by __dfsw_* hooks to guide the fuzzing towards new application states.
 //
 // Parts of this code will not function when DFSan is not linked in.
 // Instead of using ifdefs and thus requiring a separate build of lib/Fuzzer
   clang  -fPIC -c -g -O2 -std=c++11 Fuzzer*.cpp
   clang++ -O0 -std=c++11 -fsanitize-coverage=edge,trace-cmp \
     -fsanitize=dataflow \
-    test/dfsan/DFSanSimpleCmpTest.cpp Fuzzer*.o
-  ./a.out
+    test/SimpleCmpTest.cpp Fuzzer*.o
+  ./a.out -use_traces=1
 )
 */
 
+#include "FuzzerDFSan.h"
 #include "FuzzerInternal.h"
-#include <sanitizer/dfsan_interface.h>
 
 #include <algorithm>
 #include <cstring>
+#include <thread>
 #include <unordered_map>
 
+#if !LLVM_FUZZER_SUPPORTS_DFSAN
+// Stubs for dfsan for platforms where dfsan does not exist and weak
+// functions don't work.
 extern "C" {
-__attribute__((weak))
-dfsan_label dfsan_create_label(const char *desc, void *userdata);
-__attribute__((weak))
-void dfsan_set_label(dfsan_label label, void *addr, size_t size);
-__attribute__((weak))
-void dfsan_add_label(dfsan_label label, void *addr, size_t size);
-__attribute__((weak))
-const struct dfsan_label_info *dfsan_get_label_info(dfsan_label label);
-__attribute__((weak))
-dfsan_label dfsan_read_label(const void *addr, size_t size);
+dfsan_label dfsan_create_label(const char *desc, void *userdata) { return 0; }
+void dfsan_set_label(dfsan_label label, void *addr, size_t size) {}
+void dfsan_add_label(dfsan_label label, void *addr, size_t size) {}
+const struct dfsan_label_info *dfsan_get_label_info(dfsan_label label) {
+  return nullptr;
+}
+dfsan_label dfsan_read_label(const void *addr, size_t size) { return 0; }
 }  // extern "C"
+#endif  // !LLVM_FUZZER_SUPPORTS_DFSAN
 
 namespace fuzzer {
 
-static bool ReallyHaveDFSan() {
-  return &dfsan_create_label != nullptr;
-}
-
 // These values are copied from include/llvm/IR/InstrTypes.h.
 // We do not include the LLVM headers here to remain independent.
 // If these values ever change, an assertion in ComputeCmp will fail.
@@ -140,7 +136,7 @@ static bool ComputeCmp(size_t CmpSize, size_t CmpType, uint64_t Arg1,
   if (CmpSize == 1) return ComputeCmp<uint8_t, int8_t>(CmpType, Arg1, Arg2);
   // Other size, ==
   if (CmpType == ICMP_EQ) return Arg1 == Arg2;
-  assert(0 && "unsupported cmp and type size combination");
+  // assert(0 && "unsupported cmp and type size combination");
   return true;
 }
 
@@ -166,77 +162,96 @@ struct LabelRange {
   }
 };
 
-// A passport for a CMP site. We want to keep track of where the given CMP is
-// and how many times it is evaluated to true or false.
-struct CmpSitePassport {
-  uintptr_t PC;
-  size_t Counter[2];
-
-  bool IsInterestingCmpTarget() {
-    static const size_t kRareEnough = 50;
-    size_t C0 = Counter[0];
-    size_t C1 = Counter[1];
-    return C0 > kRareEnough * (C1 + 1) || C1 > kRareEnough * (C0 + 1);
-  }
+// For now, very simple: put Size bytes of Data at position Pos.
+struct TraceBasedMutation {
+  static const size_t kMaxSize = 28;
+  uint32_t Pos : 24;
+  uint32_t Size : 8;
+  uint8_t  Data[kMaxSize];
 };
 
-// For now, just keep a simple imprecise hash table PC => CmpSitePassport.
-// Potentially, will need to have a compiler support to have a precise mapping
-// and also thread-safety.
-struct CmpSitePassportTable {
-  static const size_t kSize = 99991;  // Prime.
-  CmpSitePassport Passports[kSize];
-
-  CmpSitePassport *GetPassport(uintptr_t PC) {
-    uintptr_t Idx = PC & kSize;
-    CmpSitePassport *Res = &Passports[Idx];
-    if (Res->PC == 0)  // Not thread safe.
-      Res->PC = PC;
-    return Res->PC == PC ? Res : nullptr;
-  }
-};
+static void PrintDataByte(uint8_t Byte) {
+  if (Byte == '\\')
+    Printf("\\\\");
+  else if (Byte == '"')
+    Printf("\\\"");
+  else if (Byte >= 32 && Byte < 127)
+    Printf("%c", Byte);
+  else
+    Printf("\\x02x", Byte);
+}
 
-static CmpSitePassportTable CSPTable;  // Zero initialized.
+static void PrintData(const uint8_t *Data, size_t Size) {
+  Printf("\"");
+  for (size_t i = 0; i < Size; i++) {
+    PrintDataByte(Data[i]);
+  }
+  Printf("\"");
+}
 
-// For now, very simple: put Size bytes of Data at position Pos.
-struct TraceBasedMutation {
-  size_t Pos;
-  size_t Size;
-  uint64_t Data;
-};
+const size_t TraceBasedMutation::kMaxSize;
 
 class TraceState {
  public:
-   TraceState(const Fuzzer::FuzzingOptions &Options, const Unit &CurrentUnit)
-       : Options(Options), CurrentUnit(CurrentUnit) {}
+  TraceState(UserSuppliedFuzzer &USF,
+             const Fuzzer::FuzzingOptions &Options, const Unit &CurrentUnit)
+       : USF(USF), Options(Options), CurrentUnit(CurrentUnit) {
+    // Current trace collection is not thread-friendly and it probably
+    // does not have to be such, but at least we should not crash in presence
+    // of threads. So, just ignore all traces coming from all threads but one.
+    IsMyThread = true;
+  }
 
   LabelRange GetLabelRange(dfsan_label L);
   void DFSanCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType,
                         uint64_t Arg1, uint64_t Arg2, dfsan_label L1,
                         dfsan_label L2);
+  void DFSanMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
+                           const uint8_t *Data2, dfsan_label L1,
+                           dfsan_label L2);
   void DFSanSwitchCallback(uint64_t PC, size_t ValSizeInBits, uint64_t Val,
                            size_t NumCases, uint64_t *Cases, dfsan_label L);
-  void TraceCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType, uint64_t Arg1,
-                        uint64_t Arg2);
+  void TraceCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType,
+                        uint64_t Arg1, uint64_t Arg2);
+  void TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
+                           const uint8_t *Data2);
 
   void TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits, uint64_t Val,
                            size_t NumCases, uint64_t *Cases);
   int TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData,
                            size_t DataSize);
+  int TryToAddDesiredData(const uint8_t *PresentData,
+                          const uint8_t *DesiredData, size_t DataSize);
 
   void StartTraceRecording() {
     if (!Options.UseTraces) return;
     RecordingTraces = true;
-    Mutations.clear();
+    NumMutations = 0;
+    USF.GetMD().ClearAutoDictionary();
   }
 
-  size_t StopTraceRecording(FuzzerRandomBase &Rand) {
+  void StopTraceRecording() {
+    if (!RecordingTraces) return;
     RecordingTraces = false;
-    std::random_shuffle(Mutations.begin(), Mutations.end(), Rand);
-    return std::min(Mutations.size(), 128UL);
+    for (size_t i = 0; i < NumMutations; i++) {
+      auto &M = Mutations[i];
+      USF.GetMD().AddWordToAutoDictionary(Unit(M.Data, M.Data + M.Size), M.Pos);
+    }
   }
 
-  void ApplyTraceBasedMutation(size_t Idx, fuzzer::Unit *U);
+  void AddMutation(uint32_t Pos, uint32_t Size, const uint8_t *Data) {
+    if (NumMutations >= kMaxMutations) return;
+    assert(Size <= TraceBasedMutation::kMaxSize);
+    auto &M = Mutations[NumMutations++];
+    M.Pos = Pos;
+    M.Size = Size;
+    memcpy(M.Data, Data, Size);
+  }
+
+  void AddMutation(uint32_t Pos, uint32_t Size, uint64_t Data) {
+    assert(Size <= sizeof(Data));
+    AddMutation(Pos, Size, reinterpret_cast<uint8_t*>(&Data));
+  }
 
  private:
   bool IsTwoByteData(uint64_t Data) {
@@ -245,12 +260,18 @@ class TraceState {
     return Signed == 0 || Signed == -1L;
   }
   bool RecordingTraces = false;
-  std::vector<TraceBasedMutation> Mutations;
-  LabelRange LabelRanges[1 << (sizeof(dfsan_label) * 8)] = {};
+  static const size_t kMaxMutations = 1 << 16;
+  size_t NumMutations;
+  TraceBasedMutation Mutations[kMaxMutations];
+  LabelRange LabelRanges[1 << (sizeof(dfsan_label) * 8)];
+  UserSuppliedFuzzer &USF;
   const Fuzzer::FuzzingOptions &Options;
   const Unit &CurrentUnit;
+  static thread_local bool IsMyThread;
 };
 
+thread_local bool TraceState::IsMyThread;
+
 LabelRange TraceState::GetLabelRange(dfsan_label L) {
   LabelRange &LR = LabelRanges[L];
   if (LR.Beg < LR.End || L == 0)
@@ -261,20 +282,11 @@ LabelRange TraceState::GetLabelRange(dfsan_label L) {
   return LR = LabelRange::Singleton(LI);
 }
 
-void TraceState::ApplyTraceBasedMutation(size_t Idx, fuzzer::Unit *U) {
-  assert(Idx < Mutations.size());
-  auto &M = Mutations[Idx];
-  if (Options.Verbosity >= 3)
-    Printf("TBM %zd %zd %zd\n", M.Pos, M.Size, M.Data);
-  if (M.Pos + M.Size > U->size()) return;
-  memcpy(U->data() + M.Pos, &M.Data, M.Size);
-}
-
 void TraceState::DFSanCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType,
                                   uint64_t Arg1, uint64_t Arg2, dfsan_label L1,
                                   dfsan_label L2) {
   assert(ReallyHaveDFSan());
-  if (!RecordingTraces) return;
+  if (!RecordingTraces || !IsMyThread) return;
   if (L1 == 0 && L2 == 0)
     return;  // Not actionable.
   if (L1 != 0 && L2 != 0)
@@ -284,26 +296,46 @@ void TraceState::DFSanCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType,
   LabelRange LR = L1 ? GetLabelRange(L1) : GetLabelRange(L2);
 
   for (size_t Pos = LR.Beg; Pos + CmpSize <= LR.End; Pos++) {
-    Mutations.push_back({Pos, CmpSize, Data});
-    Mutations.push_back({Pos, CmpSize, Data + 1});
-    Mutations.push_back({Pos, CmpSize, Data - 1});
+    AddMutation(Pos, CmpSize, Data);
+    AddMutation(Pos, CmpSize, Data + 1);
+    AddMutation(Pos, CmpSize, Data - 1);
   }
 
   if (CmpSize > LR.End - LR.Beg)
-    Mutations.push_back({LR.Beg, (unsigned)(LR.End - LR.Beg), Data});
+    AddMutation(LR.Beg, (unsigned)(LR.End - LR.Beg), Data);
 
 
   if (Options.Verbosity >= 3)
     Printf("DFSanCmpCallback: PC %lx S %zd T %zd A1 %llx A2 %llx R %d L1 %d L2 "
            "%d MU %zd\n",
-           PC, CmpSize, CmpType, Arg1, Arg2, Res, L1, L2, Mutations.size());
+           PC, CmpSize, CmpType, Arg1, Arg2, Res, L1, L2, NumMutations);
+}
+
+void TraceState::DFSanMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
+                                     const uint8_t *Data2, dfsan_label L1,
+                                     dfsan_label L2) {
+
+  assert(ReallyHaveDFSan());
+  if (!RecordingTraces || !IsMyThread) return;
+  if (L1 == 0 && L2 == 0)
+    return;  // Not actionable.
+  if (L1 != 0 && L2 != 0)
+    return;  // Probably still actionable.
+
+  const uint8_t *Data = L1 ? Data2 : Data1;
+  LabelRange LR = L1 ? GetLabelRange(L1) : GetLabelRange(L2);
+  for (size_t Pos = LR.Beg; Pos + CmpSize <= LR.End; Pos++) {
+    AddMutation(Pos, CmpSize, Data);
+    if (Options.Verbosity >= 3)
+      Printf("DFSanMemcmpCallback: Pos %d Size %d\n", Pos, CmpSize);
+  }
 }
 
 void TraceState::DFSanSwitchCallback(uint64_t PC, size_t ValSizeInBits,
                                      uint64_t Val, size_t NumCases,
                                      uint64_t *Cases, dfsan_label L) {
   assert(ReallyHaveDFSan());
-  if (!RecordingTraces) return;
+  if (!RecordingTraces || !IsMyThread) return;
   if (!L) return;  // Not actionable.
   LabelRange LR = GetLabelRange(L);
   size_t ValSize = ValSizeInBits / 8;
@@ -313,12 +345,12 @@ void TraceState::DFSanSwitchCallback(uint64_t PC, size_t ValSizeInBits,
 
   for (size_t Pos = LR.Beg; Pos + ValSize <= LR.End; Pos++)
     for (size_t i = 0; i < NumCases; i++)
-      Mutations.push_back({Pos, ValSize, Cases[i]});
+      AddMutation(Pos, ValSize, Cases[i]);
 
   if (TryShort)
     for (size_t Pos = LR.Beg; Pos + 2 <= LR.End; Pos++)
       for (size_t i = 0; i < NumCases; i++)
-        Mutations.push_back({Pos, 2, Cases[i]});
+        AddMutation(Pos, 2, Cases[i]);
 
   if (Options.Verbosity >= 3)
     Printf("DFSanSwitchCallback: PC %lx Val %zd SZ %zd # %zd L %d: {%d, %d} "
@@ -331,35 +363,44 @@ int TraceState::TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData,
   int Res = 0;
   const uint8_t *Beg = CurrentUnit.data();
   const uint8_t *End = Beg + CurrentUnit.size();
-  for (const uint8_t *Cur = Beg; Cur < End; Cur += DataSize) {
+  for (const uint8_t *Cur = Beg; Cur < End; Cur++) {
     Cur = (uint8_t *)memmem(Cur, End - Cur, &PresentData, DataSize);
     if (!Cur)
       break;
     size_t Pos = Cur - Beg;
     assert(Pos < CurrentUnit.size());
-    if (Mutations.size() > 100000U) return Res;  // Just in case.
-    Mutations.push_back({Pos, DataSize, DesiredData});
-    Mutations.push_back({Pos, DataSize, DesiredData + 1});
-    Mutations.push_back({Pos, DataSize, DesiredData - 1});
-    Cur += DataSize;
+    AddMutation(Pos, DataSize, DesiredData);
+    AddMutation(Pos, DataSize, DesiredData + 1);
+    AddMutation(Pos, DataSize, DesiredData - 1);
+    Res++;
+  }
+  return Res;
+}
+
+int TraceState::TryToAddDesiredData(const uint8_t *PresentData,
+                                    const uint8_t *DesiredData,
+                                    size_t DataSize) {
+  int Res = 0;
+  const uint8_t *Beg = CurrentUnit.data();
+  const uint8_t *End = Beg + CurrentUnit.size();
+  for (const uint8_t *Cur = Beg; Cur < End; Cur++) {
+    Cur = (uint8_t *)memmem(Cur, End - Cur, PresentData, DataSize);
+    if (!Cur)
+      break;
+    size_t Pos = Cur - Beg;
+    assert(Pos < CurrentUnit.size());
+    AddMutation(Pos, DataSize, DesiredData);
     Res++;
   }
   return Res;
 }
 
-void TraceState::TraceCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType, uint64_t Arg1,
-                        uint64_t Arg2) {
-  if (!RecordingTraces) return;
+void TraceState::TraceCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType,
+                                  uint64_t Arg1, uint64_t Arg2) {
+  if (!RecordingTraces || !IsMyThread) return;
   int Added = 0;
-  CmpSitePassport *CSP = CSPTable.GetPassport(PC);
-  if (!CSP) return;
-  CSP->Counter[ComputeCmp(CmpSize, CmpType, Arg1, Arg2)]++;
-  size_t C0 = CSP->Counter[0];
-  size_t C1 = CSP->Counter[1];
-  if (!CSP->IsInterestingCmpTarget())
-    return;
   if (Options.Verbosity >= 3)
-    Printf("TraceCmp: %p %zd/%zd; %zd %zd\n", CSP->PC, C0, C1, Arg1, Arg2);
+    Printf("TraceCmp %zd/%zd: %p %zd %zd\n", CmpSize, CmpType, PC, Arg1, Arg2);
   Added += TryToAddDesiredData(Arg1, Arg2, CmpSize);
   Added += TryToAddDesiredData(Arg2, Arg1, CmpSize);
   if (!Added && CmpSize == 4 && IsTwoByteData(Arg1) && IsTwoByteData(Arg2)) {
@@ -368,10 +409,24 @@ void TraceState::TraceCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType,
   }
 }
 
+void TraceState::TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
+                                     const uint8_t *Data2) {
+  if (!RecordingTraces || !IsMyThread) return;
+  CmpSize = std::min(CmpSize, TraceBasedMutation::kMaxSize);
+  int Added2 = TryToAddDesiredData(Data1, Data2, CmpSize);
+  int Added1 = TryToAddDesiredData(Data2, Data1, CmpSize);
+  if ((Added1 || Added2) && Options.Verbosity >= 3) {
+    Printf("MemCmp Added %d%d: ", Added1, Added2);
+    if (Added1) PrintData(Data1, CmpSize);
+    if (Added2) PrintData(Data2, CmpSize);
+    Printf("\n");
+  }
+}
+
 void TraceState::TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits,
                                      uint64_t Val, size_t NumCases,
                                      uint64_t *Cases) {
-  if (!RecordingTraces) return;
+  if (!RecordingTraces || !IsMyThread) return;
   size_t ValSize = ValSizeInBits / 8;
   bool TryShort = IsTwoByteData(Val);
   for (size_t i = 0; i < NumCases; i++)
@@ -386,37 +441,36 @@ void TraceState::TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits,
     if (TryShort)
       TryToAddDesiredData(Val, Cases[i], 2);
   }
-
 }
 
 static TraceState *TS;
 
 void Fuzzer::StartTraceRecording() {
   if (!TS) return;
+  if (ReallyHaveDFSan())
+    for (size_t i = 0; i < static_cast<size_t>(Options.MaxLen); i++)
+      dfsan_set_label(i + 1, &CurrentUnit[i], 1);
   TS->StartTraceRecording();
 }
 
-size_t Fuzzer::StopTraceRecording() {
-  if (!TS) return 0;
-  return TS->StopTraceRecording(USF.GetRand());
-}
-
-void Fuzzer::ApplyTraceBasedMutation(size_t Idx, Unit *U) {
-  assert(TS);
-  TS->ApplyTraceBasedMutation(Idx, U);
+void Fuzzer::StopTraceRecording() {
+  if (!TS) return;
+  TS->StopTraceRecording();
 }
 
 void Fuzzer::InitializeTraceState() {
   if (!Options.UseTraces) return;
-  TS = new TraceState(Options, CurrentUnit);
+  TS = new TraceState(USF, Options, CurrentUnit);
   CurrentUnit.resize(Options.MaxLen);
   // The rest really requires DFSan.
   if (!ReallyHaveDFSan()) return;
   for (size_t i = 0; i < static_cast<size_t>(Options.MaxLen); i++) {
     dfsan_label L = dfsan_create_label("input", (void*)(i + 1));
     // We assume that no one else has called dfsan_create_label before.
-    assert(L == i + 1);
-    dfsan_set_label(L, &CurrentUnit[i], 1);
+    if (L != i + 1) {
+      Printf("DFSan labels are not starting from 1, exiting\n");
+      exit(1);
+    }
   }
 }
 
@@ -453,59 +507,69 @@ void dfsan_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2,
                             size_t n, dfsan_label s1_label,
                             dfsan_label s2_label, dfsan_label n_label) {
   if (!TS) return;
-  uintptr_t PC = reinterpret_cast<uintptr_t>(caller_pc);
-  uint64_t S1 = 0, S2 = 0;
-  // Simplification: handle only first 8 bytes.
-  memcpy(&S1, s1, std::min(n, sizeof(S1)));
-  memcpy(&S2, s2, std::min(n, sizeof(S2)));
   dfsan_label L1 = dfsan_read_label(s1, n);
   dfsan_label L2 = dfsan_read_label(s2, n);
-  TS->DFSanCmpCallback(PC, n, fuzzer::ICMP_EQ, S1, S2, L1, L2);
+  TS->DFSanMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
+                          reinterpret_cast<const uint8_t *>(s2), L1, L2);
 }
 
 void dfsan_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2,
                              size_t n, dfsan_label s1_label,
                              dfsan_label s2_label, dfsan_label n_label) {
   if (!TS) return;
-  uintptr_t PC = reinterpret_cast<uintptr_t>(caller_pc);
-  uint64_t S1 = 0, S2 = 0;
   n = std::min(n, fuzzer::InternalStrnlen(s1, n));
   n = std::min(n, fuzzer::InternalStrnlen(s2, n));
-  // Simplification: handle only first 8 bytes.
-  memcpy(&S1, s1, std::min(n, sizeof(S1)));
-  memcpy(&S2, s2, std::min(n, sizeof(S2)));
   dfsan_label L1 = dfsan_read_label(s1, n);
   dfsan_label L2 = dfsan_read_label(s2, n);
-  TS->DFSanCmpCallback(PC, n, fuzzer::ICMP_EQ, S1, S2, L1, L2);
+  TS->DFSanMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
+                          reinterpret_cast<const uint8_t *>(s2), L1, L2);
+}
+
+void dfsan_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2,
+                            dfsan_label s1_label, dfsan_label s2_label) {
+  if (!TS) return;
+  size_t Len1 = strlen(s1);
+  size_t Len2 = strlen(s2);
+  size_t N = std::min(Len1, Len2);
+  if (N <= 1) return;  // Not interesting.
+  dfsan_label L1 = dfsan_read_label(s1, Len1);
+  dfsan_label L2 = dfsan_read_label(s2, Len2);
+  TS->DFSanMemcmpCallback(N, reinterpret_cast<const uint8_t *>(s1),
+                          reinterpret_cast<const uint8_t *>(s2), L1, L2);
 }
 
 void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
                                   const void *s2, size_t n) {
   if (!TS) return;
-  uintptr_t PC = reinterpret_cast<uintptr_t>(caller_pc);
-  uint64_t S1 = 0, S2 = 0;
-  // Simplification: handle only first 8 bytes.
-  memcpy(&S1, s1, std::min(n, sizeof(S1)));
-  memcpy(&S2, s2, std::min(n, sizeof(S2)));
-  TS->TraceCmpCallback(PC, n, fuzzer::ICMP_EQ, S1, S2);
+  if (n <= 1) return;  // Not interesting.
+  TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
+                          reinterpret_cast<const uint8_t *>(s2));
 }
 
 void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1,
                                    const char *s2, size_t n) {
   if (!TS) return;
-  uintptr_t PC = reinterpret_cast<uintptr_t>(caller_pc);
-  uint64_t S1 = 0, S2 = 0;
   size_t Len1 = fuzzer::InternalStrnlen(s1, n);
   size_t Len2 = fuzzer::InternalStrnlen(s2, n);
   n = std::min(n, Len1);
   n = std::min(n, Len2);
   if (n <= 1) return;  // Not interesting.
-  // Simplification: handle only first 8 bytes.
-  memcpy(&S1, s1, std::min(n, sizeof(S1)));
-  memcpy(&S2, s2, std::min(n, sizeof(S2)));
-  TS->TraceCmpCallback(PC, n, fuzzer::ICMP_EQ, S1, S2);
+  TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
+                          reinterpret_cast<const uint8_t *>(s2));
+}
+
+void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
+                                   const char *s2) {
+  if (!TS) return;
+  size_t Len1 = strlen(s1);
+  size_t Len2 = strlen(s2);
+  size_t N = std::min(Len1, Len2);
+  if (N <= 1) return;  // Not interesting.
+  TS->TraceMemcmpCallback(N, reinterpret_cast<const uint8_t *>(s1),
+                          reinterpret_cast<const uint8_t *>(s2));
 }
 
+__attribute__((visibility("default")))
 void __sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1,
                                uint64_t Arg2) {
   if (!TS) return;
@@ -515,6 +579,7 @@ void __sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1,
   TS->TraceCmpCallback(PC, CmpSize, Type, Arg1, Arg2);
 }
 
+__attribute__((visibility("default")))
 void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
   if (!TS) return;
   uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));