summaryrefslogtreecommitdiffstats
path: root/llvm/lib/FuzzMutate/IRMutator.cpp
diff options
context:
space:
mode:
authorJustin Bogner <mail@justinbogner.com>2017-08-21 17:44:36 +0000
committerJustin Bogner <mail@justinbogner.com>2017-08-21 17:44:36 +0000
commit02336370858e69cee50019f6bcea89de041c7df3 (patch)
treef78de55d532e82bf4fe7c8d351e4029d0ee91904 /llvm/lib/FuzzMutate/IRMutator.cpp
parentd5ac1812b7fa2f748b900de09b9160cf8a5c5a12 (diff)
downloadbcm5719-llvm-02336370858e69cee50019f6bcea89de041c7df3.tar.gz
bcm5719-llvm-02336370858e69cee50019f6bcea89de041c7df3.zip
Introduce FuzzMutate library
This introduces the FuzzMutate library, which provides structured fuzzing for LLVM IR, as described in my [EuroLLVM 2017 talk][1]. Most of the basic mutators to inject and delete IR are provided, with support for most basic operations. I will follow up with the instruction selection fuzzer, which is implemented in terms of this library. [1]: http://llvm.org/devmtg/2017-03//2017/02/20/accepted-sessions.html#2 llvm-svn: 311356
Diffstat (limited to 'llvm/lib/FuzzMutate/IRMutator.cpp')
-rw-r--r--llvm/lib/FuzzMutate/IRMutator.cpp183
1 files changed, 183 insertions, 0 deletions
diff --git a/llvm/lib/FuzzMutate/IRMutator.cpp b/llvm/lib/FuzzMutate/IRMutator.cpp
new file mode 100644
index 00000000000..6545446a984
--- /dev/null
+++ b/llvm/lib/FuzzMutate/IRMutator.cpp
@@ -0,0 +1,183 @@
+//===-- IRMutator.cpp -----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/FuzzMutate/IRMutator.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/FuzzMutate/Operations.h"
+#include "llvm/FuzzMutate/Random.h"
+#include "llvm/FuzzMutate/RandomIRBuilder.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Transforms/Scalar/DCE.h"
+
+using namespace llvm;
+
+static void createEmptyFunction(Module &M) {
+ // TODO: Some arguments and a return value would probably be more interesting.
+ LLVMContext &Context = M.getContext();
+ Function *F = Function::Create(FunctionType::get(Type::getVoidTy(Context), {},
+ /*isVarArg=*/false),
+ GlobalValue::ExternalLinkage, "f", &M);
+ BasicBlock *BB = BasicBlock::Create(Context, "BB", F);
+ ReturnInst::Create(Context, BB);
+}
+
+void IRMutationStrategy::mutate(Module &M, RandomIRBuilder &IB) {
+ if (M.empty())
+ createEmptyFunction(M);
+
+ auto RS = makeSampler<Function *>(IB.Rand);
+ for (Function &F : M)
+ if (!F.isDeclaration())
+ RS.sample(&F, /*Weight=*/1);
+ mutate(*RS.getSelection(), IB);
+}
+
+void IRMutationStrategy::mutate(Function &F, RandomIRBuilder &IB) {
+ mutate(*makeSampler(IB.Rand, make_pointer_range(F)).getSelection(), IB);
+}
+
+void IRMutationStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
+ mutate(*makeSampler(IB.Rand, make_pointer_range(BB)).getSelection(), IB);
+}
+
+void IRMutator::mutateModule(Module &M, int Seed, size_t CurSize,
+ size_t MaxSize) {
+ std::vector<Type *> Types;
+ for (const auto &Getter : AllowedTypes)
+ Types.push_back(Getter(M.getContext()));
+ RandomIRBuilder IB(Seed, Types);
+
+ auto RS = makeSampler<IRMutationStrategy *>(IB.Rand);
+ for (const auto &Strategy : Strategies)
+ RS.sample(Strategy.get(),
+ Strategy->getWeight(CurSize, MaxSize, RS.totalWeight()));
+ auto Strategy = RS.getSelection();
+
+ Strategy->mutate(M, IB);
+}
+
+static void eliminateDeadCode(Function &F) {
+ FunctionPassManager FPM;
+ FPM.addPass(DCEPass());
+ FunctionAnalysisManager FAM;
+ FAM.registerPass([&] { return TargetLibraryAnalysis(); });
+ FPM.run(F, FAM);
+}
+
+void InjectorIRStrategy::mutate(Function &F, RandomIRBuilder &IB) {
+ IRMutationStrategy::mutate(F, IB);
+ eliminateDeadCode(F);
+}
+
+std::vector<fuzzerop::OpDescriptor> InjectorIRStrategy::getDefaultOps() {
+ std::vector<fuzzerop::OpDescriptor> Ops;
+ describeFuzzerIntOps(Ops);
+ describeFuzzerFloatOps(Ops);
+ describeFuzzerControlFlowOps(Ops);
+ describeFuzzerPointerOps(Ops);
+ describeFuzzerAggregateOps(Ops);
+ describeFuzzerVectorOps(Ops);
+ return Ops;
+}
+
+fuzzerop::OpDescriptor
+InjectorIRStrategy::chooseOperation(Value *Src, RandomIRBuilder &IB) {
+ auto OpMatchesPred = [&Src](fuzzerop::OpDescriptor &Op) {
+ return Op.SourcePreds[0].matches({}, Src);
+ };
+ auto RS = makeSampler(IB.Rand, make_filter_range(Operations, OpMatchesPred));
+ if (RS.isEmpty())
+ report_fatal_error("No available operations for src type");
+ return *RS;
+}
+
+void InjectorIRStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
+ SmallVector<Instruction *, 32> Insts;
+ for (auto I = BB.getFirstInsertionPt(), E = BB.end(); I != E; ++I)
+ Insts.push_back(&*I);
+
+ // Choose an insertion point for our new instruction.
+ size_t IP = uniform<size_t>(IB.Rand, 0, Insts.size() - 1);
+
+ auto InstsBefore = makeArrayRef(Insts).slice(0, IP);
+ auto InstsAfter = makeArrayRef(Insts).slice(IP);
+
+ // Choose a source, which will be used to constrain the operation selection.
+ SmallVector<Value *, 2> Srcs;
+ Srcs.push_back(IB.findOrCreateSource(BB, InstsBefore));
+
+ // Choose an operation that's constrained to be valid for the type of the
+ // source, collect any other sources it needs, and then build it.
+ fuzzerop::OpDescriptor OpDesc = chooseOperation(Srcs[0], IB);
+ for (const auto &Pred : makeArrayRef(OpDesc.SourcePreds).slice(1))
+ Srcs.push_back(IB.findOrCreateSource(BB, InstsBefore, Srcs, Pred));
+ if (Value *Op = OpDesc.BuilderFunc(Srcs, Insts[IP])) {
+ // Find a sink and wire up the results of the operation.
+ IB.connectToSink(BB, InstsAfter, Op);
+ }
+}
+
+uint64_t InstDeleterIRStrategy::getWeight(size_t CurrentSize, size_t MaxSize,
+ uint64_t CurrentWeight) {
+ // If we have less than 200 bytes, panic and try to always delete.
+ if (CurrentSize > MaxSize - 200)
+ return CurrentWeight ? CurrentWeight * 100 : 1;
+ // Draw a line starting from when we only have 1k left and increasing linearly
+ // to double the current weight.
+ int Line = (-2 * CurrentWeight) * (MaxSize - CurrentSize + 1000);
+ // Clamp negative weights to zero.
+ if (Line < 0)
+ return 0;
+ return Line;
+}
+
+void InstDeleterIRStrategy::mutate(Function &F, RandomIRBuilder &IB) {
+ auto RS = makeSampler<Instruction *>(IB.Rand);
+ // Avoid terminators so we don't have to worry about keeping the CFG coherent.
+ for (Instruction &Inst : instructions(F))
+ if (!Inst.isTerminator())
+ RS.sample(&Inst, /*Weight=*/1);
+ assert(!RS.isEmpty() && "No instructions to delete");
+ // Delete the instruction.
+ mutate(*RS.getSelection(), IB);
+ // Clean up any dead code that's left over after removing the instruction.
+ eliminateDeadCode(F);
+}
+
+void InstDeleterIRStrategy::mutate(Instruction &Inst, RandomIRBuilder &IB) {
+ assert(!Inst.isTerminator() && "Deleting terminators invalidates CFG");
+
+ if (Inst.getType()->isVoidTy()) {
+ // Instructions with void type (ie, store) have no uses to worry about. Just
+ // erase it and move on.
+ Inst.eraseFromParent();
+ return;
+ }
+
+ // Otherwise we need to find some other value with the right type to keep the
+ // users happy.
+ auto Pred = fuzzerop::onlyType(Inst.getType());
+ auto RS = makeSampler<Value *>(IB.Rand);
+ SmallVector<Instruction *, 32> InstsBefore;
+ BasicBlock *BB = Inst.getParent();
+ for (auto I = BB->getFirstInsertionPt(), E = Inst.getIterator(); I != E;
+ ++I) {
+ if (Pred.matches({}, &*I))
+ RS.sample(&*I, /*Weight=*/1);
+ InstsBefore.push_back(&*I);
+ }
+ if (!RS)
+ RS.sample(IB.newSource(*BB, InstsBefore, {}, Pred), /*Weight=*/1);
+
+ Inst.replaceAllUsesWith(RS.getSelection());
+}
OpenPOWER on IntegriCloud