#include "llvm/FuzzMutate/IRMutator.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.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/InstIterator.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Transforms/Scalar/DCE.h"
using namespace llvm;
static void createEmptyFunction(Module &M) {
LLVMContext &Context = M.getContext();
Function *F = Function::Create(FunctionType::get(Type::getVoidTy(Context), {},
false),
GlobalValue::ExternalLinkage, "f", &M);
BasicBlock *BB = BasicBlock::Create(Context, "BB", F);
ReturnInst::Create(Context, BB);
}
void IRMutationStrategy::mutate(Module &M, RandomIRBuilder &IB) {
auto RS = makeSampler<Function *>(IB.Rand);
for (Function &F : M)
if (!F.isDeclaration())
RS.sample(&F, 1);
if (RS.isEmpty())
createEmptyFunction(M);
else
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(); });
FAM.registerPass([&] { return PassInstrumentationAnalysis(); });
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;
}
Optional<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())
return None;
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);
if (Insts.size() < 1)
return;
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);
SmallVector<Value *, 2> Srcs;
Srcs.push_back(IB.findOrCreateSource(BB, InstsBefore));
auto OpDesc = chooseOperation(Srcs[0], IB);
if (!OpDesc)
return;
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])) {
IB.connectToSink(BB, InstsAfter, Op);
}
}
uint64_t InstDeleterIRStrategy::getWeight(size_t CurrentSize, size_t MaxSize,
uint64_t CurrentWeight) {
if (CurrentSize > MaxSize - 200)
return CurrentWeight ? CurrentWeight * 100 : 1;
int64_t Line = (-2 * static_cast<int64_t>(CurrentWeight)) *
(static_cast<int64_t>(MaxSize) -
static_cast<int64_t>(CurrentSize) - 1000) /
1000;
if (Line < 0)
return 0;
return Line;
}
void InstDeleterIRStrategy::mutate(Function &F, RandomIRBuilder &IB) {
auto RS = makeSampler<Instruction *>(IB.Rand);
for (Instruction &Inst : instructions(F)) {
if (Inst.isTerminator() || Inst.isEHPad() ||
Inst.isSwiftError() || isa<PHINode>(Inst))
continue;
RS.sample(&Inst, 1);
}
if (RS.isEmpty())
return;
mutate(*RS.getSelection(), IB);
eliminateDeadCode(F);
}
void InstDeleterIRStrategy::mutate(Instruction &Inst, RandomIRBuilder &IB) {
assert(!Inst.isTerminator() && "Deleting terminators invalidates CFG");
if (Inst.getType()->isVoidTy()) {
Inst.eraseFromParent();
return;
}
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, 1);
InstsBefore.push_back(&*I);
}
if (!RS)
RS.sample(IB.newSource(*BB, InstsBefore, {}, Pred), 1);
Inst.replaceAllUsesWith(RS.getSelection());
Inst.eraseFromParent();
}
void InstModificationIRStrategy::mutate(Instruction &Inst,
RandomIRBuilder &IB) {
SmallVector<std::function<void()>, 8> Modifications;
CmpInst *CI = nullptr;
GetElementPtrInst *GEP = nullptr;
switch (Inst.getOpcode()) {
default:
break;
case Instruction::Add:
case Instruction::Mul:
case Instruction::Sub:
case Instruction::Shl:
Modifications.push_back([&Inst]() { Inst.setHasNoSignedWrap(true); }),
Modifications.push_back([&Inst]() { Inst.setHasNoSignedWrap(false); });
Modifications.push_back([&Inst]() { Inst.setHasNoUnsignedWrap(true); });
Modifications.push_back([&Inst]() { Inst.setHasNoUnsignedWrap(false); });
break;
case Instruction::ICmp:
CI = cast<ICmpInst>(&Inst);
Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_EQ); });
Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_NE); });
Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_UGT); });
Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_UGE); });
Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_ULT); });
Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_ULE); });
Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_SGT); });
Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_SGE); });
Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_SLT); });
Modifications.push_back([CI]() { CI->setPredicate(CmpInst::ICMP_SLE); });
break;
case Instruction::GetElementPtr:
GEP = cast<GetElementPtrInst>(&Inst);
Modifications.push_back([GEP]() { GEP->setIsInBounds(true); });
Modifications.push_back([GEP]() { GEP->setIsInBounds(false); });
break;
}
auto RS = makeSampler(IB.Rand, Modifications);
if (RS)
RS.getSelection()();
}
std::unique_ptr<Module> llvm::parseModule(const uint8_t *Data, size_t Size,
LLVMContext &Context) {
if (Size <= 1)
return std::make_unique<Module>("M", Context);
auto Buffer = MemoryBuffer::getMemBuffer(
StringRef(reinterpret_cast<const char *>(Data), Size), "Fuzzer input",
false);
SMDiagnostic Err;
auto M = parseBitcodeFile(Buffer->getMemBufferRef(), Context);
if (Error E = M.takeError()) {
errs() << toString(std::move(E)) << "\n";
return nullptr;
}
return std::move(M.get());
}
size_t llvm::writeModule(const Module &M, uint8_t *Dest, size_t MaxSize) {
std::string Buf;
{
raw_string_ostream OS(Buf);
WriteBitcodeToFile(M, OS);
}
if (Buf.size() > MaxSize)
return 0;
memcpy(Dest, Buf.data(), Buf.size());
return Buf.size();
}
std::unique_ptr<Module> llvm::parseAndVerify(const uint8_t *Data, size_t Size,
LLVMContext &Context) {
auto M = parseModule(Data, Size, Context);
if (!M || verifyModule(*M, &errs()))
return nullptr;
return M;
}