blob: 8c812512fd175e597a5eb89725ce8c5e521cdfd5 [file] [log] [blame]
Kostya Serebryany22526252015-05-11 21:16:27 +00001//===- FuzzerTraceState.cpp - Trace-based fuzzer mutator ------------------===//
Kostya Serebryany16d03bd2015-03-30 22:09:51 +00002//
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//===----------------------------------------------------------------------===//
Kostya Serebryany4820cc92016-10-04 06:08:46 +00009// Data tracing.
Kostya Serebryany16d03bd2015-03-30 22:09:51 +000010//===----------------------------------------------------------------------===//
11
Kostya Serebryany6f5a8042016-09-21 01:50:50 +000012#include "FuzzerDictionary.h"
Zachary Turner24a148b2016-11-30 19:06:14 +000013#include "FuzzerInternal.h"
14#include "FuzzerIO.h"
Kostya Serebryany6f5a8042016-09-21 01:50:50 +000015#include "FuzzerMutate.h"
16#include "FuzzerRandom.h"
Kostya Serebryanyab73c692016-09-23 00:46:18 +000017#include "FuzzerTracePC.h"
Kostya Serebryanybeb24c32015-05-07 21:02:11 +000018#include <algorithm>
Kostya Serebryany16d03bd2015-03-30 22:09:51 +000019#include <cstring>
Kostya Serebryany859e86d2016-01-12 02:08:37 +000020#include <map>
Kostya Serebryanyc135b552016-07-15 23:27:19 +000021#include <set>
Zachary Turner24a148b2016-11-30 19:06:14 +000022#include <thread>
Kostya Serebryany16d03bd2015-03-30 22:09:51 +000023
Kostya Serebryany5a99ecb2015-05-11 20:51:19 +000024namespace fuzzer {
25
Kostya Serebryanybeb24c32015-05-07 21:02:11 +000026// For now, very simple: put Size bytes of Data at position Pos.
27struct TraceBasedMutation {
Kostya Serebryany476f0ce2016-01-16 03:53:32 +000028 uint32_t Pos;
29 Word W;
Kostya Serebryanybeb24c32015-05-07 21:02:11 +000030};
31
Kostya Serebryanyae5b9562016-01-15 06:24:05 +000032// Declared as static globals for faster checks inside the hooks.
Kostya Serebryanyae5b9562016-01-15 06:24:05 +000033static bool RecordingMemcmp = false;
Kostya Serebryanyc135b552016-07-15 23:27:19 +000034static bool RecordingMemmem = false;
Kostya Serebryany6b08be92016-07-19 18:29:06 +000035static bool DoingMyOwnMemmem = false;
36
Kostya Serebryanya5f94fb2016-10-14 20:20:33 +000037ScopedDoingMyOwnMemmem::ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = true; }
38ScopedDoingMyOwnMemmem::~ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = false; }
Kostya Serebryanyae5b9562016-01-15 06:24:05 +000039
Kostya Serebryany22526252015-05-11 21:16:27 +000040class TraceState {
Mike Aizatskyf0b3e852016-06-23 20:44:48 +000041public:
42 TraceState(MutationDispatcher &MD, const FuzzingOptions &Options,
Richard Smithb62e7e32016-05-27 21:05:35 +000043 const Fuzzer *F)
Kostya Serebryanyf26017b2016-05-26 21:32:30 +000044 : MD(MD), Options(Options), F(F) {}
Kostya Serebryany16d03bd2015-03-30 22:09:51 +000045
Kostya Serebryanyc5733162016-01-09 01:39:55 +000046 void TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
47 const uint8_t *Data2);
Kostya Serebryanyfb7d8d92015-07-31 01:33:06 +000048
Kostya Serebryanyc5733162016-01-09 01:39:55 +000049 int TryToAddDesiredData(const uint8_t *PresentData,
50 const uint8_t *DesiredData, size_t DataSize);
Kostya Serebryanybeb24c32015-05-07 21:02:11 +000051
52 void StartTraceRecording() {
Kostya Serebryany1d8c2ce2017-01-17 23:09:05 +000053 if (!Options.UseMemcmp && !Options.UseMemmem)
Kostya Serebryanyf26017b2016-05-26 21:32:30 +000054 return;
Kostya Serebryanyae5b9562016-01-15 06:24:05 +000055 RecordingMemcmp = Options.UseMemcmp;
Kostya Serebryanyc135b552016-07-15 23:27:19 +000056 RecordingMemmem = Options.UseMemmem;
Kostya Serebryanye7583d22016-01-09 00:38:40 +000057 NumMutations = 0;
Kostya Serebryanyc135b552016-07-15 23:27:19 +000058 InterestingWords.clear();
Kostya Serebryany7ec0c562016-02-13 03:25:16 +000059 MD.ClearAutoDictionary();
Kostya Serebryanybeb24c32015-05-07 21:02:11 +000060 }
61
Kostya Serebryanyb65805a2016-01-09 03:08:58 +000062 void StopTraceRecording() {
Kostya Serebryany1d8c2ce2017-01-17 23:09:05 +000063 if (!RecordingMemcmp && !RecordingMemmem)
Mike Aizatskyf0b3e852016-06-23 20:44:48 +000064 return;
Kostya Serebryanyae5b9562016-01-15 06:24:05 +000065 RecordingMemcmp = false;
Kostya Serebryanyb65805a2016-01-09 03:08:58 +000066 for (size_t i = 0; i < NumMutations; i++) {
67 auto &M = Mutations[i];
Kostya Serebryany859e86d2016-01-12 02:08:37 +000068 if (Options.Verbosity >= 2) {
Kostya Serebryany476f0ce2016-01-16 03:53:32 +000069 AutoDictUnitCounts[M.W]++;
Kostya Serebryany859e86d2016-01-12 02:08:37 +000070 AutoDictAdds++;
71 if ((AutoDictAdds & (AutoDictAdds - 1)) == 0) {
Kostya Serebryany476f0ce2016-01-16 03:53:32 +000072 typedef std::pair<size_t, Word> CU;
Kostya Serebryany859e86d2016-01-12 02:08:37 +000073 std::vector<CU> CountedUnits;
74 for (auto &I : AutoDictUnitCounts)
75 CountedUnits.push_back(std::make_pair(I.second, I.first));
76 std::sort(CountedUnits.begin(), CountedUnits.end(),
77 [](const CU &a, const CU &b) { return a.first > b.first; });
78 Printf("AutoDict:\n");
79 for (auto &I : CountedUnits) {
80 Printf(" %zd ", I.first);
Kostya Serebryany6f5a8042016-09-21 01:50:50 +000081 PrintASCII(I.second.data(), I.second.size());
Kostya Serebryany859e86d2016-01-12 02:08:37 +000082 Printf("\n");
83 }
84 }
85 }
Kostya Serebryanyc135b552016-07-15 23:27:19 +000086 MD.AddWordToAutoDictionary({M.W, M.Pos});
Kostya Serebryanyb65805a2016-01-09 03:08:58 +000087 }
Kostya Serebryanyc135b552016-07-15 23:27:19 +000088 for (auto &W : InterestingWords)
89 MD.AddWordToAutoDictionary({W});
Kostya Serebryanybeb24c32015-05-07 21:02:11 +000090 }
91
Kostya Serebryanyc5733162016-01-09 01:39:55 +000092 void AddMutation(uint32_t Pos, uint32_t Size, const uint8_t *Data) {
Kostya Serebryanye7583d22016-01-09 00:38:40 +000093 if (NumMutations >= kMaxMutations) return;
94 auto &M = Mutations[NumMutations++];
95 M.Pos = Pos;
Kostya Serebryany476f0ce2016-01-16 03:53:32 +000096 M.W.Set(Data, Size);
Kostya Serebryanyc5733162016-01-09 01:39:55 +000097 }
98
99 void AddMutation(uint32_t Pos, uint32_t Size, uint64_t Data) {
100 assert(Size <= sizeof(Data));
101 AddMutation(Pos, Size, reinterpret_cast<uint8_t*>(&Data));
Kostya Serebryanye7583d22016-01-09 00:38:40 +0000102 }
103
Kostya Serebryanyc135b552016-07-15 23:27:19 +0000104 void AddInterestingWord(const uint8_t *Data, size_t Size) {
105 if (!RecordingMemmem || !F->InFuzzingThread()) return;
106 if (Size <= 1) return;
107 Size = std::min(Size, Word::GetMaxSize());
108 Word W(Data, Size);
109 InterestingWords.insert(W);
110 }
111
Kostya Serebryany16d03bd2015-03-30 22:09:51 +0000112 private:
Kostya Serebryany5a99ecb2015-05-11 20:51:19 +0000113 bool IsTwoByteData(uint64_t Data) {
114 int64_t Signed = static_cast<int64_t>(Data);
115 Signed >>= 16;
116 return Signed == 0 || Signed == -1L;
117 }
Kostya Serebryanyd88d1302016-02-02 23:17:45 +0000118
119 // We don't want to create too many trace-based mutations as it is both
120 // expensive and useless. So after some number of mutations is collected,
121 // start rejecting some of them. The more there are mutations the more we
122 // reject.
123 bool WantToHandleOneMoreMutation() {
124 const size_t FirstN = 64;
125 // Gladly handle first N mutations.
126 if (NumMutations <= FirstN) return true;
127 size_t Diff = NumMutations - FirstN;
128 size_t DiffLog = sizeof(long) * 8 - __builtin_clzl((long)Diff);
129 assert(DiffLog > 0 && DiffLog < 64);
Kostya Serebryany7ec0c562016-02-13 03:25:16 +0000130 bool WantThisOne = MD.GetRand()(1 << DiffLog) == 0; // 1 out of DiffLog.
Kostya Serebryanyd88d1302016-02-02 23:17:45 +0000131 return WantThisOne;
132 }
133
Kostya Serebryanye7583d22016-01-09 00:38:40 +0000134 static const size_t kMaxMutations = 1 << 16;
135 size_t NumMutations;
136 TraceBasedMutation Mutations[kMaxMutations];
Kostya Serebryanyc135b552016-07-15 23:27:19 +0000137 // TODO: std::set is too inefficient, need to have a custom DS here.
138 std::set<Word> InterestingWords;
Kostya Serebryany7ec0c562016-02-13 03:25:16 +0000139 MutationDispatcher &MD;
Mike Aizatskyf0b3e852016-06-23 20:44:48 +0000140 const FuzzingOptions Options;
Kostya Serebryanyf26017b2016-05-26 21:32:30 +0000141 const Fuzzer *F;
Kostya Serebryany476f0ce2016-01-16 03:53:32 +0000142 std::map<Word, size_t> AutoDictUnitCounts;
Kostya Serebryany859e86d2016-01-12 02:08:37 +0000143 size_t AutoDictAdds = 0;
Kostya Serebryany16d03bd2015-03-30 22:09:51 +0000144};
145
Kostya Serebryanyc5733162016-01-09 01:39:55 +0000146int TraceState::TryToAddDesiredData(const uint8_t *PresentData,
147 const uint8_t *DesiredData,
148 size_t DataSize) {
Kostya Serebryanyd88d1302016-02-02 23:17:45 +0000149 if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0;
Kostya Serebryany6b08be92016-07-19 18:29:06 +0000150 ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem;
Kostya Serebryanyf26017b2016-05-26 21:32:30 +0000151 const uint8_t *UnitData;
152 auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData);
Kostya Serebryanyc5733162016-01-09 01:39:55 +0000153 int Res = 0;
Kostya Serebryanyf26017b2016-05-26 21:32:30 +0000154 const uint8_t *Beg = UnitData;
155 const uint8_t *End = Beg + UnitSize;
Kostya Serebryanyc5733162016-01-09 01:39:55 +0000156 for (const uint8_t *Cur = Beg; Cur < End; Cur++) {
Zachary Turner6fa57ad2016-12-02 23:02:01 +0000157 Cur = (uint8_t *)SearchMemory(Cur, End - Cur, PresentData, DataSize);
Kostya Serebryanyc5733162016-01-09 01:39:55 +0000158 if (!Cur)
159 break;
160 size_t Pos = Cur - Beg;
Kostya Serebryanyf26017b2016-05-26 21:32:30 +0000161 assert(Pos < UnitSize);
Kostya Serebryanyc5733162016-01-09 01:39:55 +0000162 AddMutation(Pos, DataSize, DesiredData);
163 Res++;
164 }
165 return Res;
166}
167
Kostya Serebryanyc5733162016-01-09 01:39:55 +0000168void TraceState::TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
169 const uint8_t *Data2) {
Kostya Serebryanyf26017b2016-05-26 21:32:30 +0000170 if (!RecordingMemcmp || !F->InFuzzingThread()) return;
Kostya Serebryany476f0ce2016-01-16 03:53:32 +0000171 CmpSize = std::min(CmpSize, Word::GetMaxSize());
Kostya Serebryany1f9c40d2016-01-09 03:46:08 +0000172 int Added2 = TryToAddDesiredData(Data1, Data2, CmpSize);
173 int Added1 = TryToAddDesiredData(Data2, Data1, CmpSize);
174 if ((Added1 || Added2) && Options.Verbosity >= 3) {
175 Printf("MemCmp Added %d%d: ", Added1, Added2);
Kostya Serebryany41740052016-01-12 02:36:59 +0000176 if (Added1) PrintASCII(Data1, CmpSize);
177 if (Added2) PrintASCII(Data2, CmpSize);
Kostya Serebryany1f9c40d2016-01-09 03:46:08 +0000178 Printf("\n");
179 }
Kostya Serebryanyc5733162016-01-09 01:39:55 +0000180}
181
Kostya Serebryany22526252015-05-11 21:16:27 +0000182static TraceState *TS;
Kostya Serebryany16d03bd2015-03-30 22:09:51 +0000183
Kostya Serebryanybeb24c32015-05-07 21:02:11 +0000184void Fuzzer::StartTraceRecording() {
Kostya Serebryany22526252015-05-11 21:16:27 +0000185 if (!TS) return;
186 TS->StartTraceRecording();
Kostya Serebryanybeb24c32015-05-07 21:02:11 +0000187}
188
Kostya Serebryanyb65805a2016-01-09 03:08:58 +0000189void Fuzzer::StopTraceRecording() {
190 if (!TS) return;
191 TS->StopTraceRecording();
Kostya Serebryany16d03bd2015-03-30 22:09:51 +0000192}
193
Kostya Serebryany22526252015-05-11 21:16:27 +0000194void Fuzzer::InitializeTraceState() {
Kostya Serebryany1d8c2ce2017-01-17 23:09:05 +0000195 if (!Options.UseMemcmp && !Options.UseMemmem) return;
Kostya Serebryanyf26017b2016-05-26 21:32:30 +0000196 TS = new TraceState(MD, Options, this);
Kostya Serebryany16d03bd2015-03-30 22:09:51 +0000197}
198
Kostya Serebryanyc9dc96b2015-07-30 21:22:22 +0000199static size_t InternalStrnlen(const char *S, size_t MaxLen) {
200 size_t Len = 0;
201 for (; Len < MaxLen && S[Len]; Len++) {}
202 return Len;
203}
204
Kostya Serebryany16d03bd2015-03-30 22:09:51 +0000205} // namespace fuzzer
206
Kostya Serebryany22526252015-05-11 21:16:27 +0000207using fuzzer::TS;
Kostya Serebryanyae5b9562016-01-15 06:24:05 +0000208using fuzzer::RecordingMemcmp;
Kostya Serebryany5a99ecb2015-05-11 20:51:19 +0000209
Kostya Serebryany16d03bd2015-03-30 22:09:51 +0000210extern "C" {
Kostya Serebryany7f4227d2015-08-05 18:23:01 +0000211
Kostya Serebryany4b83a4f2016-01-12 16:50:18 +0000212// We may need to avoid defining weak hooks to stay compatible with older clang.
213#ifndef LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
214# define LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS 1
215#endif
216
217#if LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
Kostya Serebryany0e776a22015-07-30 01:34:58 +0000218void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
Kostya Serebryanye3580952016-01-12 00:43:42 +0000219 const void *s2, size_t n, int result) {
Kostya Serebryanye3580952016-01-12 00:43:42 +0000220 if (result == 0) return; // No reason to mutate.
Kostya Serebryanyc5733162016-01-09 01:39:55 +0000221 if (n <= 1) return; // Not interesting.
Kostya Serebryany1d8c2ce2017-01-17 23:09:05 +0000222 fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/false);
223 if (!RecordingMemcmp) return;
Kostya Serebryanyc5733162016-01-09 01:39:55 +0000224 TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
225 reinterpret_cast<const uint8_t *>(s2));
Kostya Serebryanyb74ba422015-07-30 02:33:45 +0000226}
227
228void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1,
Kostya Serebryanye3580952016-01-12 00:43:42 +0000229 const char *s2, size_t n, int result) {
Kostya Serebryanye3580952016-01-12 00:43:42 +0000230 if (result == 0) return; // No reason to mutate.
Kostya Serebryanycd6a4662015-07-31 17:05:05 +0000231 size_t Len1 = fuzzer::InternalStrnlen(s1, n);
232 size_t Len2 = fuzzer::InternalStrnlen(s2, n);
233 n = std::min(n, Len1);
234 n = std::min(n, Len2);
235 if (n <= 1) return; // Not interesting.
Kostya Serebryany1d8c2ce2017-01-17 23:09:05 +0000236 fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/true);
237 if (!RecordingMemcmp) return;
Kostya Serebryanyc5733162016-01-09 01:39:55 +0000238 TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
239 reinterpret_cast<const uint8_t *>(s2));
Kostya Serebryany0e776a22015-07-30 01:34:58 +0000240}
241
Kostya Serebryany7f4227d2015-08-05 18:23:01 +0000242void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
Kostya Serebryanye3580952016-01-12 00:43:42 +0000243 const char *s2, int result) {
Kostya Serebryanye3580952016-01-12 00:43:42 +0000244 if (result == 0) return; // No reason to mutate.
Kostya Serebryany7f4227d2015-08-05 18:23:01 +0000245 size_t Len1 = strlen(s1);
246 size_t Len2 = strlen(s2);
247 size_t N = std::min(Len1, Len2);
248 if (N <= 1) return; // Not interesting.
Kostya Serebryany1d8c2ce2017-01-17 23:09:05 +0000249 fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, N, /*StopAtZero*/true);
250 if (!RecordingMemcmp) return;
Kostya Serebryanyc5733162016-01-09 01:39:55 +0000251 TS->TraceMemcmpCallback(N, reinterpret_cast<const uint8_t *>(s1),
252 reinterpret_cast<const uint8_t *>(s2));
Kostya Serebryany7f4227d2015-08-05 18:23:01 +0000253}
254
Kostya Serebryanyc135b552016-07-15 23:27:19 +0000255void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1,
256 const char *s2, size_t n, int result) {
257 return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result);
258}
259void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1,
260 const char *s2, int result) {
261 return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result);
262}
263void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1,
264 const char *s2, char *result) {
265 TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
266}
267void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1,
268 const char *s2, char *result) {
269 TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
270}
271void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1,
272 const void *s2, size_t len2, void *result) {
Kostya Serebryany6b08be92016-07-19 18:29:06 +0000273 if (fuzzer::DoingMyOwnMemmem) return;
274 TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), len2);
Kostya Serebryanyc135b552016-07-15 23:27:19 +0000275}
276
Kostya Serebryany4b83a4f2016-01-12 16:50:18 +0000277#endif // LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
Kostya Serebryany16d03bd2015-03-30 22:09:51 +0000278} // extern "C"