//===-- IRMutator.cpp -----------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/FuzzMutate/IRMutator.h" #include "llvm/ADT/Optional.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/InstIterator.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/Support/Debug.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(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 Types; for (const auto &Getter : AllowedTypes) Types.push_back(Getter(M.getContext())); RandomIRBuilder IB(Seed, Types); auto RS = makeSampler(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 InjectorIRStrategy::getDefaultOps() { std::vector Ops; describeFuzzerIntOps(Ops); describeFuzzerFloatOps(Ops); describeFuzzerControlFlowOps(Ops); describeFuzzerPointerOps(Ops); describeFuzzerAggregateOps(Ops); describeFuzzerVectorOps(Ops); return Ops; } Optional 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 Insts; for (auto I = BB.getFirstInsertionPt(), E = BB.end(); I != E; ++I) Insts.push_back(&*I); if (Insts.size() < 1) return; // Choose an insertion point for our new instruction. size_t IP = uniform(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 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. auto OpDesc = chooseOperation(Srcs[0], IB); // Bail if no operation was found 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])) { // 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. int64_t Line = (-2 * static_cast(CurrentWeight)) * (static_cast(MaxSize) - static_cast(CurrentSize) - 1000) / 1000; // Clamp negative weights to zero. if (Line < 0) return 0; return Line; } void InstDeleterIRStrategy::mutate(Function &F, RandomIRBuilder &IB) { auto RS = makeSampler(IB.Rand); for (Instruction &Inst : instructions(F)) { // TODO: We can't handle these instructions. if (Inst.isTerminator() || Inst.isEHPad() || Inst.isSwiftError() || isa(Inst)) continue; RS.sample(&Inst, /*Weight=*/1); } if (RS.isEmpty()) return; // 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(IB.Rand); SmallVector 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()); Inst.eraseFromParent(); } void InstModificationIRStrategy::mutate(Instruction &Inst, RandomIRBuilder &IB) { SmallVector, 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(&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(&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()(); }