[libFuzzer] when a single mutation fails try a few more times with other mutations...
[oota-llvm.git] / lib / Fuzzer / FuzzerMutate.cpp
index f537fa90fd8506f13bdcab44f70fffd3ec78058b..9b2015be8691227f2cbc912473f92854a0702bbc 100644 (file)
 
 #include "FuzzerInternal.h"
 
+#include <algorithm>
+
 namespace fuzzer {
 
-static char FlipRandomBit(char X) {
-  int Bit = rand() % 8;
+typedef size_t (MutationDispatcher::*Mutator)(uint8_t *Data, size_t Size,
+                                              size_t Max);
+
+struct MutationDispatcher::Impl {
+  std::vector<Unit> Dictionary;
+  std::vector<Mutator> Mutators;
+  Impl() {
+    Mutators.push_back(&MutationDispatcher::Mutate_EraseByte);
+    Mutators.push_back(&MutationDispatcher::Mutate_InsertByte);
+    Mutators.push_back(&MutationDispatcher::Mutate_ChangeByte);
+    Mutators.push_back(&MutationDispatcher::Mutate_ChangeBit);
+    Mutators.push_back(&MutationDispatcher::Mutate_ShuffleBytes);
+  }
+  void AddWordToDictionary(const uint8_t *Word, size_t Size) {
+    if (Dictionary.empty()) {
+      Mutators.push_back(&MutationDispatcher::Mutate_AddWordFromDictionary);
+    }
+    Dictionary.push_back(Unit(Word, Word + Size));
+  }
+};
+
+
+static char FlipRandomBit(char X, FuzzerRandomBase &Rand) {
+  int Bit = Rand(8);
   char Mask = 1 << Bit;
   char R;
   if (X & (1 << Bit))
@@ -27,45 +51,101 @@ static char FlipRandomBit(char X) {
   return R;
 }
 
-static char RandCh() {
-  if (rand() % 2) return rand();
+static char RandCh(FuzzerRandomBase &Rand) {
+  if (Rand.RandBool()) return Rand(256);
   const char *Special = "!*'();:@&=+$,/?%#[]123ABCxyz-`~.";
-  return Special[rand() % (sizeof(Special) - 1)];
+  return Special[Rand(sizeof(Special) - 1)];
+}
+
+size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size,
+                                               size_t MaxSize) {
+  assert(Size);
+  size_t ShuffleAmount = Rand(std::min(Size, 8UL)) + 1;  // [1,8] and <= Size.
+  size_t ShuffleStart = Rand(Size - ShuffleAmount);
+  assert(ShuffleStart + ShuffleAmount <= Size);
+  std::random_shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount,
+                      Rand);
+  return Size;
+}
+
+size_t MutationDispatcher::Mutate_EraseByte(uint8_t *Data, size_t Size,
+                                            size_t MaxSize) {
+  assert(Size);
+  if (Size == 1) return 0;
+  size_t Idx = Rand(Size);
+  // Erase Data[Idx].
+  memmove(Data + Idx, Data + Idx + 1, Size - Idx - 1);
+  return Size - 1;
+}
+
+size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size,
+                                             size_t MaxSize) {
+  if (Size == MaxSize) return 0;
+  size_t Idx = Rand(Size + 1);
+  // Insert new value at Data[Idx].
+  memmove(Data + Idx + 1, Data + Idx, Size - Idx);
+  Data[Idx] = RandCh(Rand);
+  return Size + 1;
+}
+
+size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size,
+                                             size_t MaxSize) {
+  size_t Idx = Rand(Size);
+  Data[Idx] = RandCh(Rand);
+  return Size;
+}
+
+size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size,
+                                            size_t MaxSize) {
+  size_t Idx = Rand(Size);
+  Data[Idx] = FlipRandomBit(Data[Idx], Rand);
+  return Size;
+}
+
+size_t MutationDispatcher::Mutate_AddWordFromDictionary(uint8_t *Data,
+                                                        size_t Size,
+                                                        size_t MaxSize) {
+  auto &D = MDImpl->Dictionary;
+  assert(!D.empty());
+  if (D.empty()) return 0;
+  const Unit &Word = D[Rand(D.size())];
+  if (Size + Word.size() > MaxSize) return 0;
+  size_t Idx = Rand(Size + 1);
+  memmove(Data + Idx + Word.size(), Data + Idx, Size - Idx);
+  memcpy(Data + Idx, Word.data(), Word.size());
+  return Size + Word.size();
 }
 
 // Mutates Data in place, returns new size.
-size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
+size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
   assert(MaxSize > 0);
   assert(Size <= MaxSize);
   if (Size == 0) {
     for (size_t i = 0; i < MaxSize; i++)
-      Data[i] = RandCh();
+      Data[i] = RandCh(Rand);
     return MaxSize;
   }
   assert(Size > 0);
-  size_t Idx = rand() % Size;
-  switch (rand() % 3) {
-  case 0:
-    if (Size > 1) {
-      // Erase Data[Idx].
-      memmove(Data + Idx, Data + Idx + 1, Size - Idx - 1);
-      Size = Size - 1;
-    }
-    [[clang::fallthrough]];
-  case 1:
-    if (Size < MaxSize) {
-      // Insert new value at Data[Idx].
-      memmove(Data + Idx + 1, Data + Idx, Size - Idx);
-      Data[Idx] = RandCh();
-    }
-    Data[Idx] = RandCh();
-    break;
-  case 2:
-    Data[Idx] = FlipRandomBit(Data[Idx]);
-    break;
+  // Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize),
+  // in which case they will return 0.
+  // Try several times before returning un-mutated data.
+  for (int Iter = 0; Iter < 10; Iter++) {
+    size_t MutatorIdx = Rand(MDImpl->Mutators.size());
+    size_t NewSize =
+        (this->*(MDImpl->Mutators[MutatorIdx]))(Data, Size, MaxSize);
+    if (NewSize) return NewSize;
   }
-  assert(Size > 0);
   return Size;
 }
 
+void MutationDispatcher::AddWordToDictionary(const uint8_t *Word, size_t Size) {
+  MDImpl->AddWordToDictionary(Word, Size);
+}
+
+MutationDispatcher::MutationDispatcher(FuzzerRandomBase &Rand) : Rand(Rand) {
+  MDImpl = new Impl;
+}
+
+MutationDispatcher::~MutationDispatcher() { delete MDImpl; }
+
 }  // namespace fuzzer