OLD | NEW |
---|---|
(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 | |
OLD | NEW |