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

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
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 <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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698