OLD | NEW |
---|---|
(Empty) | |
1 //===- NaClSimpleRecordFuzzer.cpp - Simple fuzzer of bitcode --------------===// | |
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 // | |
10 // This file implements a simple fuzzer for a list of PNaCl bitcode records. | |
11 // | |
12 //===----------------------------------------------------------------------===// | |
13 | |
14 #include "llvm/ADT/STLExtras.h" | |
15 #include "llvm/Bitcode/NaCl/NaClFuzz.h" | |
16 #include "llvm/Support/Format.h" | |
17 | |
18 #include <array> | |
jvoung (off chromium)
2015/06/02 16:14:03
std::array isn't actually used?
Karl
2015/06/02 16:40:23
Done.
| |
19 #include <cmath> | |
20 #include <cstdlib> | |
21 #include <map> | |
jvoung (off chromium)
2015/06/02 16:14:03
where is <map> used?
Karl
2015/06/02 16:40:23
Got rid of all <...> but <set>
| |
22 #include <set> | |
23 | |
24 | |
25 using namespace llvm; | |
26 using namespace naclfuzz; | |
27 | |
28 namespace { | |
29 | |
30 // Counts the number of times each value in a range [0..N) is used (based | |
31 // on the number of calls to method increment()). | |
32 class DistCounter { | |
33 DistCounter(const DistCounter&) = delete; | |
34 void operator=(const DistCounter&) = delete; | |
35 public: | |
36 explicit DistCounter(size_t DistSize) | |
37 : Dist(DistSize, 0), Total(0) {} | |
38 | |
39 // Increments the count for the given value | |
40 size_t increment(size_t Value) { | |
41 ++Dist[Value]; | |
42 ++Total; | |
43 return Value; | |
44 } | |
45 | |
46 // Returns the end of the range being checked. | |
47 size_t size() const { | |
48 return Dist.size(); | |
49 } | |
50 | |
51 // Returns the number of times value was incremented. | |
52 size_t operator[](size_t Value) const { | |
53 return Dist[Value]; | |
54 } | |
55 | |
56 // Retrns the number of times any value in the distribution was | |
57 // incremented. | |
58 size_t getTotal() const { | |
59 return Total; | |
60 } | |
61 private: | |
62 // The number of times each value was incremented. | |
63 std::vector<size_t> Dist; | |
64 // The total number of times any value was incremented. | |
65 size_t Total; | |
66 }; | |
67 | |
68 // Model weights when randomly choosing values. | |
69 typedef unsigned WeightType; | |
70 | |
71 // Associates weights with values. Used to generate weighted | |
72 // distributions (see class WeightedDistribution below). | |
73 template<typename T> | |
74 struct WeightedValue { | |
75 T Value; | |
76 WeightType Weight; | |
77 }; | |
78 | |
79 // Weighted distribution for a set of values in [0..DistSize). | |
80 template<typename T> | |
81 class WeightedDistribution { | |
82 WeightedDistribution(const WeightedDistribution&) = delete; | |
83 void operator=(const WeightedDistribution&) = delete; | |
84 public: | |
85 typedef const WeightedValue<T> *const_iterator; | |
86 | |
87 WeightedDistribution(const WeightedValue<T> Dist[], | |
88 size_t DistSize, | |
89 RandomNumberGenerator &Generator) | |
90 : Dist(Dist), DistSize(DistSize), TotalWeight(0), Generator(Generator) { | |
91 for (size_t i = 0; i < DistSize; ++i) | |
92 TotalWeight += Dist[i].Weight; | |
93 } | |
94 | |
95 virtual ~WeightedDistribution() {} | |
96 | |
97 /// Returns the number of weighted values in the distribution. | |
98 size_t size() const { | |
99 return DistSize; | |
100 } | |
101 | |
102 /// Returns const iterator at beginning of weighted distribution. | |
103 const_iterator begin() const { | |
104 return Dist; | |
105 } | |
106 | |
107 /// Returns const iterator at end of weighted distribution. | |
108 const_iterator end() const { | |
109 return Dist + DistSize; | |
110 } | |
111 | |
112 /// Randomly chooses a weighted value in the distribution, based on | |
113 /// the weights of the distrubution. | |
114 virtual const WeightedValue<T> &choose() { | |
115 return Dist[chooseIndex()]; | |
116 } | |
117 | |
118 /// Returns the total weight associated with the distribution. | |
119 size_t getTotalWeight() const { | |
120 return TotalWeight; | |
121 } | |
122 | |
123 protected: | |
124 // The weighted values defining the distribution. | |
125 const WeightedValue<T> *Dist; | |
126 // The number of weighed values. | |
127 size_t DistSize; | |
128 // The total weight of the distribution (sum of weights in weighted values). | |
129 size_t TotalWeight; | |
130 // The random number generator to use. | |
131 RandomNumberGenerator &Generator; | |
132 | |
133 // Randomly chooses an index of a weighed value in the distribution, | |
134 // based on the weights of the distribution. | |
135 size_t chooseIndex() { | |
136 /// TODO(kschimpf) Speed this up? | |
137 WeightType WeightedSum = Generator.chooseInRange(TotalWeight); | |
138 assert(WeightedSum < TotalWeight); | |
139 for (size_t Choice = 0; Choice < DistSize; ++Choice) { | |
140 WeightType NextWeight = Dist[Choice].Weight; | |
141 if (WeightedSum < NextWeight) | |
142 return Choice; | |
143 WeightedSum -= NextWeight; | |
144 } | |
145 llvm_unreachable("no index for WeightedDistribution.chooseIndex()"); | |
146 } | |
147 }; | |
148 | |
149 // Defines a range [min..max]. | |
150 template<typename T> | |
151 struct RangeType { | |
152 T min; | |
153 T max; | |
154 }; | |
155 | |
156 // Weighted distribution of a set of value ranges. | |
157 template<typename T> | |
158 class WeightedRangeDistribution : public WeightedDistribution<RangeType<T>> { | |
159 WeightedRangeDistribution(const WeightedRangeDistribution<T>&) = delete; | |
160 void operator=(const WeightedRangeDistribution<T>&) = delete; | |
161 public: | |
162 WeightedRangeDistribution(const WeightedValue<RangeType<T>> Dist[], | |
163 size_t DistSize, | |
164 RandomNumberGenerator &Generator) | |
165 : WeightedDistribution<RangeType<T>>(Dist, DistSize, Generator) {} | |
166 | |
167 // Choose a value from one of the value ranges. | |
168 T chooseValue() { | |
169 const RangeType<T> Range = this->choose().Value; | |
170 return Range.min + | |
171 this->Generator.chooseInRange(Range.max - Range.min + 1); | |
172 } | |
173 }; | |
174 | |
175 /// Weighted distribution with a counter, capturing the distribution | |
176 /// of random choices for each weighted value. | |
177 template<typename T> | |
178 class CountedWeightedDistribution : public WeightedDistribution<T> { | |
179 CountedWeightedDistribution(const CountedWeightedDistribution&) = delete; | |
180 void operator=(const CountedWeightedDistribution&) = delete; | |
181 public: | |
182 CountedWeightedDistribution(const WeightedValue<T> Dist[], | |
183 size_t DistSize, | |
184 RandomNumberGenerator &Generator) | |
185 : WeightedDistribution<T>(Dist, DistSize, Generator), Counter(DistSize) {} | |
186 | |
187 const WeightedValue<T> &choose() final { | |
188 return this->Dist[Counter.increment( | |
189 WeightedDistribution<T>::chooseIndex())]; | |
190 } | |
191 | |
192 /// Returns the number of times the Index-th weighted value was | |
193 /// chosen. | |
194 size_t getChooseCount(size_t Index) const { | |
195 return Counter[Index]; | |
196 } | |
197 | |
198 /// Returns the total number of times method choose was called. | |
199 size_t getTotalChooseCount() const { | |
200 return Counter.getTotal(); | |
201 } | |
202 | |
203 private: | |
204 DistCounter Counter; | |
205 }; | |
206 | |
207 #define ARRAY(array) array, array_lengthof(array) | |
208 | |
209 // Weighted distribution used to select edit actions. | |
210 const WeightedValue<RecordFuzzer::EditAction> ActionDist[] = { | |
211 {RecordFuzzer::InsertRecord, 3}, | |
212 {RecordFuzzer::MutateRecord, 5}, | |
213 {RecordFuzzer::RemoveRecord, 1}, | |
214 {RecordFuzzer::ReplaceRecord, 1}, | |
215 {RecordFuzzer::SwapRecord, 1} | |
216 }; | |
217 | |
218 // Type of values in bitcode records. | |
219 typedef uint64_t ValueType; | |
220 | |
221 // Weighted ranges for non-negative values in records. | |
222 const WeightedValue<RangeType<ValueType>> PosValueDist[] = { | |
223 {{0, 6}, 100}, | |
224 {{7, 20}, 50}, | |
225 {{21, 40}, 10}, | |
226 {{41, 100}, 2}, | |
227 {{101, 4096}, 1} | |
228 }; | |
229 | |
230 // Distribution used to decide when use negative values in records. | |
231 WeightedValue<bool> NegValueDist[] = { | |
232 {true, 1}, // i.e. make value negative. | |
233 {false, 100} // i.e. leave value positive. | |
234 }; | |
235 | |
236 // Range distribution for records sizes (must be greater than 0). | |
237 const WeightedValue<RangeType<size_t>> RecordSizeDist[] = { | |
238 {{1, 3}, 1000}, | |
239 {{4, 7}, 100}, | |
240 {{7, 100}, 1} | |
241 }; | |
242 | |
243 // Defines valid record codes. | |
244 typedef unsigned RecordCodeType; | |
245 | |
246 // Special code to signify adding random other record codes. | |
247 static const RecordCodeType OtherRecordCode = 575757575; | |
248 | |
249 // List of record codes we can generate. The weights are based | |
250 // on record counts in pnacl-llc.pexe, using how many thousand of | |
251 // each record code appeared (or 1 if less than 1 thousand). | |
252 WeightedValue<RecordCodeType> RecordCodeDist[] = { | |
253 {naclbitc::BLOCKINFO_CODE_SETBID, 1}, | |
254 {naclbitc::MODULE_CODE_VERSION, 1}, | |
255 {naclbitc::MODULE_CODE_FUNCTION, 7}, | |
256 {naclbitc::TYPE_CODE_NUMENTRY, 1}, | |
257 {naclbitc::TYPE_CODE_VOID, 1}, | |
258 {naclbitc::TYPE_CODE_FLOAT, 1}, | |
259 {naclbitc::TYPE_CODE_DOUBLE, 1}, | |
260 {naclbitc::TYPE_CODE_INTEGER, 1}, | |
261 {naclbitc::TYPE_CODE_VECTOR, 1}, | |
262 {naclbitc::TYPE_CODE_FUNCTION, 1}, | |
263 {naclbitc::VST_CODE_ENTRY, 1}, | |
264 {naclbitc::VST_CODE_BBENTRY, 1}, | |
265 {naclbitc::CST_CODE_SETTYPE, 15}, | |
266 {naclbitc::CST_CODE_UNDEF, 1}, | |
267 {naclbitc::CST_CODE_INTEGER, 115}, | |
268 {naclbitc::CST_CODE_FLOAT, 1}, | |
269 {naclbitc::GLOBALVAR_VAR, 14}, | |
270 {naclbitc::GLOBALVAR_COMPOUND, 1}, | |
271 {naclbitc::GLOBALVAR_ZEROFILL, 2}, | |
272 {naclbitc::GLOBALVAR_DATA, 18}, | |
273 {naclbitc::GLOBALVAR_RELOC, 20}, | |
274 {naclbitc::GLOBALVAR_COUNT, 1}, | |
275 {naclbitc::FUNC_CODE_DECLAREBLOCKS, 6}, | |
276 {naclbitc::FUNC_CODE_INST_BINOP, 402}, | |
277 {naclbitc::FUNC_CODE_INST_CAST, 61}, | |
278 {naclbitc::FUNC_CODE_INST_EXTRACTELT, 1}, | |
279 {naclbitc::FUNC_CODE_INST_INSERTELT, 1}, | |
280 {naclbitc::FUNC_CODE_INST_RET, 7}, | |
281 {naclbitc::FUNC_CODE_INST_BR, 223}, | |
282 {naclbitc::FUNC_CODE_INST_SWITCH, 7}, | |
283 {naclbitc::FUNC_CODE_INST_UNREACHABLE, 1}, | |
284 {naclbitc::FUNC_CODE_INST_PHI, 84}, | |
285 {naclbitc::FUNC_CODE_INST_ALLOCA, 34}, | |
286 {naclbitc::FUNC_CODE_INST_LOAD, 225}, | |
287 {naclbitc::FUNC_CODE_INST_STORE, 461}, | |
288 {naclbitc::FUNC_CODE_INST_CMP2, 140}, | |
289 {naclbitc::FUNC_CODE_INST_VSELECT, 10}, | |
290 {naclbitc::FUNC_CODE_INST_CALL, 80}, | |
291 {naclbitc::FUNC_CODE_INST_FORWARDTYPEREF, 36}, | |
292 {naclbitc::FUNC_CODE_INST_CALL_INDIRECT, 5}, | |
293 {naclbitc::BLK_CODE_ENTER, 1}, | |
294 {naclbitc::BLK_CODE_EXIT, 1}, | |
295 {naclbitc::BLK_CODE_DEFINE_ABBREV, 1}, | |
296 {OtherRecordCode, 1} | |
297 }; | |
298 | |
299 // *Warning* The current implementation does not work on empty bitcode | |
300 // record lists. | |
301 class SimpleRecordFuzzer : public RecordFuzzer { | |
302 public: | |
303 SimpleRecordFuzzer(NaClMungedBitcode &Bitcode, | |
304 RandomNumberGenerator &Generator) | |
305 : RecordFuzzer(Bitcode, Generator), | |
306 RecordCounter(Bitcode.getBaseRecords().size()), | |
307 ActionWeight(ARRAY(ActionDist), Generator), | |
308 RecordSizeWeight(ARRAY(RecordSizeDist), Generator), | |
309 PosValueWeight(ARRAY(PosValueDist), Generator), | |
310 NegValueWeight(ARRAY(NegValueDist), Generator), | |
311 RecordCodeWeight(ARRAY(RecordCodeDist), Generator) { | |
312 | |
313 assert(!Bitcode.getBaseRecords().empty() | |
314 && "Can't fuzz empty list of records"); | |
315 | |
316 for (const auto &RecordCode : RecordCodeWeight) | |
317 UsedRecordCodes.insert(RecordCode.Value); | |
318 } | |
319 | |
320 ~SimpleRecordFuzzer() final; | |
321 | |
322 bool fuzz(unsigned Count, unsigned Base) final; | |
323 | |
324 void showRecordDistribution(raw_ostream &Out) const final; | |
325 | |
326 void showEditDistribution(raw_ostream &Out) const final; | |
327 | |
328 private: | |
329 // Count how many edits are applied to each record in the bitcode. | |
330 DistCounter RecordCounter; | |
331 // Distribution used to randomly choose edit actions. | |
332 CountedWeightedDistribution<RecordFuzzer::EditAction> ActionWeight; | |
333 // Distribution used to randomly choose the size of created records. | |
334 WeightedRangeDistribution<size_t> RecordSizeWeight; | |
335 // Distribution used to randomly choose positive values for records. | |
336 WeightedRangeDistribution<ValueType> PosValueWeight; | |
337 // Distribution of value sign used to randomly choose value for records. | |
338 WeightedDistribution<bool> NegValueWeight; | |
339 // Distribution used to choose record codes for records. | |
340 WeightedDistribution<RecordCodeType> RecordCodeWeight; | |
341 // Filter to make sure the "other" choice for record codes will not | |
342 // choose any other record code in RecordCodeWeight. | |
343 std::set<size_t> UsedRecordCodes; | |
344 | |
345 // Randomly choose an edit action. | |
346 RecordFuzzer::EditAction chooseAction() { | |
347 return ActionWeight.choose().Value; | |
348 } | |
349 | |
350 // Randomly choose a record index from the list of records to edit. | |
351 size_t chooseRecordIndex() { | |
352 return RecordCounter.increment( | |
353 Generator.chooseInRange(Bitcode.getBaseRecords().size())); | |
354 } | |
355 | |
356 // Randomly choose a record code. | |
357 RecordCodeType chooseRecordCode() { | |
358 RecordCodeType Code = RecordCodeWeight.choose().Value; | |
359 if (Code != OtherRecordCode) | |
360 return Code; | |
361 Code = Generator.chooseInRange(UINT_MAX); | |
362 while (UsedRecordCodes.count(Code)) // don't use predefined values. | |
363 ++Code; | |
364 return Code; | |
365 } | |
366 | |
367 // Randomly choose a positive value for use in a record. | |
368 ValueType choosePositiveValue() { | |
369 return PosValueWeight.chooseValue(); | |
370 } | |
371 | |
372 // Randomly choose a positive/negative value for use in a record. | |
373 ValueType chooseValue() { | |
374 ValueType Value = choosePositiveValue(); | |
375 if (NegValueWeight.choose().Value) { | |
376 // Use two's complement negation. | |
377 Value = ~Value + 1; | |
378 } | |
379 return Value; | |
380 } | |
381 | |
382 // Randomly fill in a record with record values. | |
383 void chooseRecordValues(NaClBitcodeAbbrevRecord &Record) { | |
384 Record.Abbrev = naclbitc::UNABBREV_RECORD; | |
385 Record.Code = chooseRecordCode(); | |
386 Record.Values.clear(); | |
387 size_t NumValues = RecordSizeWeight.chooseValue(); | |
388 for (size_t i = 0; i < NumValues; ++i) { | |
389 Record.Values.push_back(chooseValue()); | |
390 } | |
391 } | |
392 | |
393 // Apply the given edit action to a random record. | |
394 void applyAction(EditAction Action); | |
395 | |
396 // Randomly mutate a record. | |
397 void mutateRecord(NaClBitcodeAbbrevRecord &Record) { | |
398 // TODO(kschimpf) Do something smarter than just changing a value | |
399 // in the record. | |
400 size_t Index = Generator.chooseInRange(Record.Values.size() + 1); | |
401 if (Index == 0) | |
402 Record.Code = chooseRecordCode(); | |
403 else | |
404 Record.Values[Index - 1] = chooseValue(); | |
405 } | |
406 }; | |
407 | |
408 SimpleRecordFuzzer::~SimpleRecordFuzzer() {} | |
409 | |
410 bool SimpleRecordFuzzer::fuzz(unsigned Count, unsigned Base) { | |
411 // TODO(kschimpf): Add some randomness in the number of actions selected. | |
412 clear(); | |
413 size_t NumRecords = Bitcode.getBaseRecords().size(); | |
414 size_t NumActions = NumRecords * Count / Base; | |
415 if (NumActions == 0) | |
416 NumActions = 1; | |
417 for (size_t i = 0; i < NumActions; ++i) | |
418 applyAction(chooseAction()); | |
419 return true; | |
420 } | |
421 | |
422 void SimpleRecordFuzzer::applyAction(EditAction Action) { | |
423 size_t Index = chooseRecordIndex(); | |
424 switch(Action) { | |
425 case InsertRecord: { | |
426 NaClBitcodeAbbrevRecord Record; | |
427 chooseRecordValues(Record); | |
428 if (Generator.chooseInRange(2)) | |
429 Bitcode.addBefore(Index, Record); | |
430 else | |
431 Bitcode.addAfter(Index, Record); | |
432 return; | |
433 } | |
434 case RemoveRecord: | |
435 Bitcode.remove(Index); | |
436 return; | |
437 case ReplaceRecord: { | |
438 NaClBitcodeAbbrevRecord Record; | |
439 chooseRecordValues(Record); | |
440 Bitcode.replace(Index, Record); | |
441 return; | |
442 } | |
443 case MutateRecord: { | |
444 NaClBitcodeAbbrevRecord Copy(*Bitcode.getBaseRecords()[Index]); | |
445 mutateRecord(Copy); | |
446 Bitcode.replace(Index, Copy); | |
447 return; | |
448 } | |
449 case SwapRecord: { | |
450 size_t Index2 = chooseRecordIndex(); | |
451 Bitcode.replace(Index, *Bitcode.getBaseRecords()[Index2]); | |
452 Bitcode.replace(Index2, *Bitcode.getBaseRecords()[Index]); | |
453 return; | |
454 } | |
455 } | |
456 } | |
457 | |
458 // Returns corresponding percentage defined by Count/Total, in a form | |
459 // that can be printed to a raw_ostream. | |
460 format_object<float> percentage(size_t Count, size_t Total) { | |
461 float percent = Total == 0.0 ? 0.0 : 100.0 * Count / Total; | |
462 return format("%1.0f", nearbyintf(percent)); | |
463 } | |
464 | |
465 void SimpleRecordFuzzer::showRecordDistribution(raw_ostream &Out) const { | |
466 Out << "Edit Record Distribution (Total: " << RecordCounter.getTotal() | |
467 << "):\n"; | |
468 size_t Total = RecordCounter.getTotal(); | |
469 for (size_t i = 0; i < Bitcode.getBaseRecords().size(); ++i) { | |
470 size_t Count = RecordCounter[i]; | |
471 Out << " " << format("%zd", i) << ": " | |
472 << Count << " (" << percentage(Count, Total) << "%)\n"; | |
473 } | |
474 } | |
475 | |
476 void SimpleRecordFuzzer::showEditDistribution(raw_ostream &Out) const { | |
477 size_t TotalWeight = ActionWeight.getTotalWeight(); | |
478 size_t TotalCount = ActionWeight.getTotalChooseCount(); | |
479 Out << "Edit Action Distribution(Total: " << TotalCount << "):\n"; | |
480 size_t ActionIndex = 0; | |
481 for (const auto &Action : ActionWeight) { | |
482 size_t ActionCount = ActionWeight.getChooseCount(ActionIndex); | |
483 Out << " " << actionName(Action.Value) << " - Wanted: " | |
484 << percentage(Action.Weight, TotalWeight) << "%, Applied: " | |
485 << ActionCount << " (" << percentage(ActionCount, TotalCount) | |
486 << "%)\n"; | |
487 ++ActionIndex; | |
488 } | |
489 } | |
490 | |
491 } // end of anonymous namespace | |
492 | |
493 namespace naclfuzz { | |
494 | |
495 RecordFuzzer *RecordFuzzer::createSimpleRecordFuzzer( | |
496 NaClMungedBitcode &Bitcode, | |
497 RandomNumberGenerator &Generator) { | |
498 return new SimpleRecordFuzzer(Bitcode, Generator); | |
499 } | |
500 | |
501 } // end of namespace naclfuzz | |
OLD | NEW |