Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(117)

Side by Side Diff: lib/Bitcode/NaCl/TestUtils/NaClSimpleRecordFuzzer.cpp

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

Powered by Google App Engine
This is Rietveld 408576698