| Index: lib/Bitcode/NaCl/TestUtils/NaClSimpleRecordFuzzer.cpp
|
| diff --git a/lib/Bitcode/NaCl/TestUtils/NaClSimpleRecordFuzzer.cpp b/lib/Bitcode/NaCl/TestUtils/NaClSimpleRecordFuzzer.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..40557a5cff123ba3293b4b9a54be354e0bc7e8bd
|
| --- /dev/null
|
| +++ b/lib/Bitcode/NaCl/TestUtils/NaClSimpleRecordFuzzer.cpp
|
| @@ -0,0 +1,496 @@
|
| +//===- NaClSimpleRecordFuzzer.cpp - Simple fuzzer of bitcode --------------===//
|
| +//
|
| +// The LLVM Compiler Infrastructure
|
| +//
|
| +// This file is distributed under the University of Illinois Open Source
|
| +// License. See LICENSE.TXT for details.
|
| +//
|
| +//===----------------------------------------------------------------------===//
|
| +//
|
| +// This file implements a simple fuzzer for a list of PNaCl bitcode records.
|
| +//
|
| +//===----------------------------------------------------------------------===//
|
| +
|
| +#include "llvm/ADT/STLExtras.h"
|
| +#include "llvm/Bitcode/NaCl/NaClFuzz.h"
|
| +#include "llvm/Support/Format.h"
|
| +
|
| +#include <set>
|
| +
|
| +using namespace llvm;
|
| +using namespace naclfuzz;
|
| +
|
| +namespace {
|
| +
|
| +// Counts the number of times each value in a range [0..N) is used (based
|
| +// on the number of calls to method increment()).
|
| +class DistCounter {
|
| + DistCounter(const DistCounter&) = delete;
|
| + void operator=(const DistCounter&) = delete;
|
| +public:
|
| + explicit DistCounter(size_t DistSize)
|
| + : Dist(DistSize, 0), Total(0) {}
|
| +
|
| + // Increments the count for the given value
|
| + size_t increment(size_t Value) {
|
| + ++Dist[Value];
|
| + ++Total;
|
| + return Value;
|
| + }
|
| +
|
| + // Returns the end of the range being checked.
|
| + size_t size() const {
|
| + return Dist.size();
|
| + }
|
| +
|
| + // Returns the number of times value was incremented.
|
| + size_t operator[](size_t Value) const {
|
| + return Dist[Value];
|
| + }
|
| +
|
| + // Retrns the number of times any value in the distribution was
|
| + // incremented.
|
| + size_t getTotal() const {
|
| + return Total;
|
| + }
|
| +private:
|
| + // The number of times each value was incremented.
|
| + std::vector<size_t> Dist;
|
| + // The total number of times any value was incremented.
|
| + size_t Total;
|
| +};
|
| +
|
| +// Model weights when randomly choosing values.
|
| +typedef unsigned WeightType;
|
| +
|
| +// Associates weights with values. Used to generate weighted
|
| +// distributions (see class WeightedDistribution below).
|
| +template<typename T>
|
| +struct WeightedValue {
|
| + T Value;
|
| + WeightType Weight;
|
| +};
|
| +
|
| +// Weighted distribution for a set of values in [0..DistSize).
|
| +template<typename T>
|
| +class WeightedDistribution {
|
| + WeightedDistribution(const WeightedDistribution&) = delete;
|
| + void operator=(const WeightedDistribution&) = delete;
|
| +public:
|
| + typedef const WeightedValue<T> *const_iterator;
|
| +
|
| + WeightedDistribution(const WeightedValue<T> Dist[],
|
| + size_t DistSize,
|
| + RandomNumberGenerator &Generator)
|
| + : Dist(Dist), DistSize(DistSize), TotalWeight(0), Generator(Generator) {
|
| + for (size_t i = 0; i < DistSize; ++i)
|
| + TotalWeight += Dist[i].Weight;
|
| + }
|
| +
|
| + virtual ~WeightedDistribution() {}
|
| +
|
| + /// Returns the number of weighted values in the distribution.
|
| + size_t size() const {
|
| + return DistSize;
|
| + }
|
| +
|
| + /// Returns const iterator at beginning of weighted distribution.
|
| + const_iterator begin() const {
|
| + return Dist;
|
| + }
|
| +
|
| + /// Returns const iterator at end of weighted distribution.
|
| + const_iterator end() const {
|
| + return Dist + DistSize;
|
| + }
|
| +
|
| + /// Randomly chooses a weighted value in the distribution, based on
|
| + /// the weights of the distrubution.
|
| + virtual const WeightedValue<T> &choose() {
|
| + return Dist[chooseIndex()];
|
| + }
|
| +
|
| + /// Returns the total weight associated with the distribution.
|
| + size_t getTotalWeight() const {
|
| + return TotalWeight;
|
| + }
|
| +
|
| +protected:
|
| + // The weighted values defining the distribution.
|
| + const WeightedValue<T> *Dist;
|
| + // The number of weighed values.
|
| + size_t DistSize;
|
| + // The total weight of the distribution (sum of weights in weighted values).
|
| + size_t TotalWeight;
|
| + // The random number generator to use.
|
| + RandomNumberGenerator &Generator;
|
| +
|
| + // Randomly chooses an index of a weighed value in the distribution,
|
| + // based on the weights of the distribution.
|
| + size_t chooseIndex() {
|
| + /// TODO(kschimpf) Speed this up?
|
| + WeightType WeightedSum = Generator.chooseInRange(TotalWeight);
|
| + assert(WeightedSum < TotalWeight);
|
| + for (size_t Choice = 0; Choice < DistSize; ++Choice) {
|
| + WeightType NextWeight = Dist[Choice].Weight;
|
| + if (WeightedSum < NextWeight)
|
| + return Choice;
|
| + WeightedSum -= NextWeight;
|
| + }
|
| + llvm_unreachable("no index for WeightedDistribution.chooseIndex()");
|
| + }
|
| +};
|
| +
|
| +// Defines a range [min..max].
|
| +template<typename T>
|
| +struct RangeType {
|
| + T min;
|
| + T max;
|
| +};
|
| +
|
| +// Weighted distribution of a set of value ranges.
|
| +template<typename T>
|
| +class WeightedRangeDistribution : public WeightedDistribution<RangeType<T>> {
|
| + WeightedRangeDistribution(const WeightedRangeDistribution<T>&) = delete;
|
| + void operator=(const WeightedRangeDistribution<T>&) = delete;
|
| +public:
|
| + WeightedRangeDistribution(const WeightedValue<RangeType<T>> Dist[],
|
| + size_t DistSize,
|
| + RandomNumberGenerator &Generator)
|
| + : WeightedDistribution<RangeType<T>>(Dist, DistSize, Generator) {}
|
| +
|
| + // Choose a value from one of the value ranges.
|
| + T chooseValue() {
|
| + const RangeType<T> Range = this->choose().Value;
|
| + return Range.min +
|
| + this->Generator.chooseInRange(Range.max - Range.min + 1);
|
| + }
|
| +};
|
| +
|
| +/// Weighted distribution with a counter, capturing the distribution
|
| +/// of random choices for each weighted value.
|
| +template<typename T>
|
| +class CountedWeightedDistribution : public WeightedDistribution<T> {
|
| + CountedWeightedDistribution(const CountedWeightedDistribution&) = delete;
|
| + void operator=(const CountedWeightedDistribution&) = delete;
|
| + public:
|
| + CountedWeightedDistribution(const WeightedValue<T> Dist[],
|
| + size_t DistSize,
|
| + RandomNumberGenerator &Generator)
|
| + : WeightedDistribution<T>(Dist, DistSize, Generator), Counter(DistSize) {}
|
| +
|
| + const WeightedValue<T> &choose() final {
|
| + return this->Dist[Counter.increment(
|
| + WeightedDistribution<T>::chooseIndex())];
|
| + }
|
| +
|
| + /// Returns the number of times the Index-th weighted value was
|
| + /// chosen.
|
| + size_t getChooseCount(size_t Index) const {
|
| + return Counter[Index];
|
| + }
|
| +
|
| + /// Returns the total number of times method choose was called.
|
| + size_t getTotalChooseCount() const {
|
| + return Counter.getTotal();
|
| + }
|
| +
|
| +private:
|
| + DistCounter Counter;
|
| +};
|
| +
|
| +#define ARRAY(array) array, array_lengthof(array)
|
| +
|
| +// Weighted distribution used to select edit actions.
|
| +const WeightedValue<RecordFuzzer::EditAction> ActionDist[] = {
|
| + {RecordFuzzer::InsertRecord, 3},
|
| + {RecordFuzzer::MutateRecord, 5},
|
| + {RecordFuzzer::RemoveRecord, 1},
|
| + {RecordFuzzer::ReplaceRecord, 1},
|
| + {RecordFuzzer::SwapRecord, 1}
|
| +};
|
| +
|
| +// Type of values in bitcode records.
|
| +typedef uint64_t ValueType;
|
| +
|
| +// Weighted ranges for non-negative values in records.
|
| +const WeightedValue<RangeType<ValueType>> PosValueDist[] = {
|
| + {{0, 6}, 100},
|
| + {{7, 20}, 50},
|
| + {{21, 40}, 10},
|
| + {{41, 100}, 2},
|
| + {{101, 4096}, 1}
|
| +};
|
| +
|
| +// Distribution used to decide when use negative values in records.
|
| +WeightedValue<bool> NegValueDist[] = {
|
| + {true, 1}, // i.e. make value negative.
|
| + {false, 100} // i.e. leave value positive.
|
| +};
|
| +
|
| +// Range distribution for records sizes (must be greater than 0).
|
| +const WeightedValue<RangeType<size_t>> RecordSizeDist[] = {
|
| + {{1, 3}, 1000},
|
| + {{4, 7}, 100},
|
| + {{7, 100}, 1}
|
| +};
|
| +
|
| +// Defines valid record codes.
|
| +typedef unsigned RecordCodeType;
|
| +
|
| +// Special code to signify adding random other record codes.
|
| +static const RecordCodeType OtherRecordCode = 575757575;
|
| +
|
| +// List of record codes we can generate. The weights are based
|
| +// on record counts in pnacl-llc.pexe, using how many thousand of
|
| +// each record code appeared (or 1 if less than 1 thousand).
|
| +WeightedValue<RecordCodeType> RecordCodeDist[] = {
|
| + {naclbitc::BLOCKINFO_CODE_SETBID, 1},
|
| + {naclbitc::MODULE_CODE_VERSION, 1},
|
| + {naclbitc::MODULE_CODE_FUNCTION, 7},
|
| + {naclbitc::TYPE_CODE_NUMENTRY, 1},
|
| + {naclbitc::TYPE_CODE_VOID, 1},
|
| + {naclbitc::TYPE_CODE_FLOAT, 1},
|
| + {naclbitc::TYPE_CODE_DOUBLE, 1},
|
| + {naclbitc::TYPE_CODE_INTEGER, 1},
|
| + {naclbitc::TYPE_CODE_VECTOR, 1},
|
| + {naclbitc::TYPE_CODE_FUNCTION, 1},
|
| + {naclbitc::VST_CODE_ENTRY, 1},
|
| + {naclbitc::VST_CODE_BBENTRY, 1},
|
| + {naclbitc::CST_CODE_SETTYPE, 15},
|
| + {naclbitc::CST_CODE_UNDEF, 1},
|
| + {naclbitc::CST_CODE_INTEGER, 115},
|
| + {naclbitc::CST_CODE_FLOAT, 1},
|
| + {naclbitc::GLOBALVAR_VAR, 14},
|
| + {naclbitc::GLOBALVAR_COMPOUND, 1},
|
| + {naclbitc::GLOBALVAR_ZEROFILL, 2},
|
| + {naclbitc::GLOBALVAR_DATA, 18},
|
| + {naclbitc::GLOBALVAR_RELOC, 20},
|
| + {naclbitc::GLOBALVAR_COUNT, 1},
|
| + {naclbitc::FUNC_CODE_DECLAREBLOCKS, 6},
|
| + {naclbitc::FUNC_CODE_INST_BINOP, 402},
|
| + {naclbitc::FUNC_CODE_INST_CAST, 61},
|
| + {naclbitc::FUNC_CODE_INST_EXTRACTELT, 1},
|
| + {naclbitc::FUNC_CODE_INST_INSERTELT, 1},
|
| + {naclbitc::FUNC_CODE_INST_RET, 7},
|
| + {naclbitc::FUNC_CODE_INST_BR, 223},
|
| + {naclbitc::FUNC_CODE_INST_SWITCH, 7},
|
| + {naclbitc::FUNC_CODE_INST_UNREACHABLE, 1},
|
| + {naclbitc::FUNC_CODE_INST_PHI, 84},
|
| + {naclbitc::FUNC_CODE_INST_ALLOCA, 34},
|
| + {naclbitc::FUNC_CODE_INST_LOAD, 225},
|
| + {naclbitc::FUNC_CODE_INST_STORE, 461},
|
| + {naclbitc::FUNC_CODE_INST_CMP2, 140},
|
| + {naclbitc::FUNC_CODE_INST_VSELECT, 10},
|
| + {naclbitc::FUNC_CODE_INST_CALL, 80},
|
| + {naclbitc::FUNC_CODE_INST_FORWARDTYPEREF, 36},
|
| + {naclbitc::FUNC_CODE_INST_CALL_INDIRECT, 5},
|
| + {naclbitc::BLK_CODE_ENTER, 1},
|
| + {naclbitc::BLK_CODE_EXIT, 1},
|
| + {naclbitc::BLK_CODE_DEFINE_ABBREV, 1},
|
| + {OtherRecordCode, 1}
|
| +};
|
| +
|
| +// *Warning* The current implementation does not work on empty bitcode
|
| +// record lists.
|
| +class SimpleRecordFuzzer : public RecordFuzzer {
|
| +public:
|
| + SimpleRecordFuzzer(NaClMungedBitcode &Bitcode,
|
| + RandomNumberGenerator &Generator)
|
| + : RecordFuzzer(Bitcode, Generator),
|
| + RecordCounter(Bitcode.getBaseRecords().size()),
|
| + ActionWeight(ARRAY(ActionDist), Generator),
|
| + RecordSizeWeight(ARRAY(RecordSizeDist), Generator),
|
| + PosValueWeight(ARRAY(PosValueDist), Generator),
|
| + NegValueWeight(ARRAY(NegValueDist), Generator),
|
| + RecordCodeWeight(ARRAY(RecordCodeDist), Generator) {
|
| +
|
| + assert(!Bitcode.getBaseRecords().empty()
|
| + && "Can't fuzz empty list of records");
|
| +
|
| + for (const auto &RecordCode : RecordCodeWeight)
|
| + UsedRecordCodes.insert(RecordCode.Value);
|
| + }
|
| +
|
| + ~SimpleRecordFuzzer() final;
|
| +
|
| + bool fuzz(unsigned Count, unsigned Base) final;
|
| +
|
| + void showRecordDistribution(raw_ostream &Out) const final;
|
| +
|
| + void showEditDistribution(raw_ostream &Out) const final;
|
| +
|
| +private:
|
| + // Count how many edits are applied to each record in the bitcode.
|
| + DistCounter RecordCounter;
|
| + // Distribution used to randomly choose edit actions.
|
| + CountedWeightedDistribution<RecordFuzzer::EditAction> ActionWeight;
|
| + // Distribution used to randomly choose the size of created records.
|
| + WeightedRangeDistribution<size_t> RecordSizeWeight;
|
| + // Distribution used to randomly choose positive values for records.
|
| + WeightedRangeDistribution<ValueType> PosValueWeight;
|
| + // Distribution of value sign used to randomly choose value for records.
|
| + WeightedDistribution<bool> NegValueWeight;
|
| + // Distribution used to choose record codes for records.
|
| + WeightedDistribution<RecordCodeType> RecordCodeWeight;
|
| + // Filter to make sure the "other" choice for record codes will not
|
| + // choose any other record code in RecordCodeWeight.
|
| + std::set<size_t> UsedRecordCodes;
|
| +
|
| + // Randomly choose an edit action.
|
| + RecordFuzzer::EditAction chooseAction() {
|
| + return ActionWeight.choose().Value;
|
| + }
|
| +
|
| + // Randomly choose a record index from the list of records to edit.
|
| + size_t chooseRecordIndex() {
|
| + return RecordCounter.increment(
|
| + Generator.chooseInRange(Bitcode.getBaseRecords().size()));
|
| + }
|
| +
|
| + // Randomly choose a record code.
|
| + RecordCodeType chooseRecordCode() {
|
| + RecordCodeType Code = RecordCodeWeight.choose().Value;
|
| + if (Code != OtherRecordCode)
|
| + return Code;
|
| + Code = Generator.chooseInRange(UINT_MAX);
|
| + while (UsedRecordCodes.count(Code)) // don't use predefined values.
|
| + ++Code;
|
| + return Code;
|
| + }
|
| +
|
| + // Randomly choose a positive value for use in a record.
|
| + ValueType choosePositiveValue() {
|
| + return PosValueWeight.chooseValue();
|
| + }
|
| +
|
| + // Randomly choose a positive/negative value for use in a record.
|
| + ValueType chooseValue() {
|
| + ValueType Value = choosePositiveValue();
|
| + if (NegValueWeight.choose().Value) {
|
| + // Use two's complement negation.
|
| + Value = ~Value + 1;
|
| + }
|
| + return Value;
|
| + }
|
| +
|
| + // Randomly fill in a record with record values.
|
| + void chooseRecordValues(NaClBitcodeAbbrevRecord &Record) {
|
| + Record.Abbrev = naclbitc::UNABBREV_RECORD;
|
| + Record.Code = chooseRecordCode();
|
| + Record.Values.clear();
|
| + size_t NumValues = RecordSizeWeight.chooseValue();
|
| + for (size_t i = 0; i < NumValues; ++i) {
|
| + Record.Values.push_back(chooseValue());
|
| + }
|
| + }
|
| +
|
| + // Apply the given edit action to a random record.
|
| + void applyAction(EditAction Action);
|
| +
|
| + // Randomly mutate a record.
|
| + void mutateRecord(NaClBitcodeAbbrevRecord &Record) {
|
| + // TODO(kschimpf) Do something smarter than just changing a value
|
| + // in the record.
|
| + size_t Index = Generator.chooseInRange(Record.Values.size() + 1);
|
| + if (Index == 0)
|
| + Record.Code = chooseRecordCode();
|
| + else
|
| + Record.Values[Index - 1] = chooseValue();
|
| + }
|
| +};
|
| +
|
| +SimpleRecordFuzzer::~SimpleRecordFuzzer() {}
|
| +
|
| +bool SimpleRecordFuzzer::fuzz(unsigned Count, unsigned Base) {
|
| + // TODO(kschimpf): Add some randomness in the number of actions selected.
|
| + clear();
|
| + size_t NumRecords = Bitcode.getBaseRecords().size();
|
| + size_t NumActions = NumRecords * Count / Base;
|
| + if (NumActions == 0)
|
| + NumActions = 1;
|
| + for (size_t i = 0; i < NumActions; ++i)
|
| + applyAction(chooseAction());
|
| + return true;
|
| +}
|
| +
|
| +void SimpleRecordFuzzer::applyAction(EditAction Action) {
|
| + size_t Index = chooseRecordIndex();
|
| + switch(Action) {
|
| + case InsertRecord: {
|
| + NaClBitcodeAbbrevRecord Record;
|
| + chooseRecordValues(Record);
|
| + if (Generator.chooseInRange(2))
|
| + Bitcode.addBefore(Index, Record);
|
| + else
|
| + Bitcode.addAfter(Index, Record);
|
| + return;
|
| + }
|
| + case RemoveRecord:
|
| + Bitcode.remove(Index);
|
| + return;
|
| + case ReplaceRecord: {
|
| + NaClBitcodeAbbrevRecord Record;
|
| + chooseRecordValues(Record);
|
| + Bitcode.replace(Index, Record);
|
| + return;
|
| + }
|
| + case MutateRecord: {
|
| + NaClBitcodeAbbrevRecord Copy(*Bitcode.getBaseRecords()[Index]);
|
| + mutateRecord(Copy);
|
| + Bitcode.replace(Index, Copy);
|
| + return;
|
| + }
|
| + case SwapRecord: {
|
| + size_t Index2 = chooseRecordIndex();
|
| + Bitcode.replace(Index, *Bitcode.getBaseRecords()[Index2]);
|
| + Bitcode.replace(Index2, *Bitcode.getBaseRecords()[Index]);
|
| + return;
|
| + }
|
| + }
|
| +}
|
| +
|
| +// Returns corresponding percentage defined by Count/Total, in a form
|
| +// that can be printed to a raw_ostream.
|
| +format_object<float> percentage(size_t Count, size_t Total) {
|
| + float percent = Total == 0.0 ? 0.0 : 100.0 * Count / Total;
|
| + return format("%1.0f", nearbyintf(percent));
|
| +}
|
| +
|
| +void SimpleRecordFuzzer::showRecordDistribution(raw_ostream &Out) const {
|
| + Out << "Edit Record Distribution (Total: " << RecordCounter.getTotal()
|
| + << "):\n";
|
| + size_t Total = RecordCounter.getTotal();
|
| + for (size_t i = 0; i < Bitcode.getBaseRecords().size(); ++i) {
|
| + size_t Count = RecordCounter[i];
|
| + Out << " " << format("%zd", i) << ": "
|
| + << Count << " (" << percentage(Count, Total) << "%)\n";
|
| + }
|
| +}
|
| +
|
| +void SimpleRecordFuzzer::showEditDistribution(raw_ostream &Out) const {
|
| + size_t TotalWeight = ActionWeight.getTotalWeight();
|
| + size_t TotalCount = ActionWeight.getTotalChooseCount();
|
| + Out << "Edit Action Distribution(Total: " << TotalCount << "):\n";
|
| + size_t ActionIndex = 0;
|
| + for (const auto &Action : ActionWeight) {
|
| + size_t ActionCount = ActionWeight.getChooseCount(ActionIndex);
|
| + Out << " " << actionName(Action.Value) << " - Wanted: "
|
| + << percentage(Action.Weight, TotalWeight) << "%, Applied: "
|
| + << ActionCount << " (" << percentage(ActionCount, TotalCount)
|
| + << "%)\n";
|
| + ++ActionIndex;
|
| + }
|
| +}
|
| +
|
| +} // end of anonymous namespace
|
| +
|
| +namespace naclfuzz {
|
| +
|
| +RecordFuzzer *RecordFuzzer::createSimpleRecordFuzzer(
|
| + NaClMungedBitcode &Bitcode,
|
| + RandomNumberGenerator &Generator) {
|
| + return new SimpleRecordFuzzer(Bitcode, Generator);
|
| +}
|
| +
|
| +} // end of namespace naclfuzz
|
|
|