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

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

Powered by Google App Engine
This is Rietveld 408576698