xref: /llvm-project/llvm/tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp (revision 9ea7fbd1e862095dd85ebf135b53dcd52d95bfef)
1 //===--- llvm-isel-fuzzer.cpp - Fuzzer for instruction selection ----------===//
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 // Tool to fuzz instruction selection using libFuzzer.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/ADT/StringRef.h"
15 #include "llvm/Analysis/TargetLibraryInfo.h"
16 #include "llvm/Bitcode/BitcodeReader.h"
17 #include "llvm/Bitcode/BitcodeWriter.h"
18 #include "llvm/CodeGen/CommandFlags.h"
19 #include "llvm/FuzzMutate/FuzzerCLI.h"
20 #include "llvm/FuzzMutate/IRMutator.h"
21 #include "llvm/FuzzMutate/Operations.h"
22 #include "llvm/FuzzMutate/Random.h"
23 #include "llvm/IR/Constants.h"
24 #include "llvm/IR/LLVMContext.h"
25 #include "llvm/IR/LegacyPassManager.h"
26 #include "llvm/IR/Module.h"
27 #include "llvm/IR/Verifier.h"
28 #include "llvm/IRReader/IRReader.h"
29 #include "llvm/Support/DataTypes.h"
30 #include "llvm/Support/Debug.h"
31 #include "llvm/Support/SourceMgr.h"
32 #include "llvm/Support/TargetRegistry.h"
33 #include "llvm/Support/TargetSelect.h"
34 #include "llvm/Target/TargetMachine.h"
35 #include <random>
36 
37 #define DEBUG_TYPE "isel-fuzzer"
38 
39 using namespace llvm;
40 
41 static cl::opt<char>
42 OptLevel("O",
43          cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
44                   "(default = '-O2')"),
45          cl::Prefix,
46          cl::ZeroOrMore,
47          cl::init(' '));
48 
49 static cl::opt<std::string>
50 TargetTriple("mtriple", cl::desc("Override target triple for module"));
51 
52 static std::unique_ptr<TargetMachine> TM;
53 static std::unique_ptr<IRMutator> Mutator;
54 
55 static std::unique_ptr<Module> parseModule(const uint8_t *Data, size_t Size,
56                                            LLVMContext &Context) {
57   auto Buffer = MemoryBuffer::getMemBuffer(
58       StringRef(reinterpret_cast<const char *>(Data), Size), "Fuzzer input",
59       /*RequiresNullTerminator=*/false);
60 
61   SMDiagnostic Err;
62   auto M = parseBitcodeFile(Buffer->getMemBufferRef(), Context);
63   if (Error E = M.takeError()) {
64     errs() << toString(std::move(E)) << "\n";
65     return nullptr;
66   }
67   return std::move(M.get());
68 }
69 
70 static size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize) {
71   std::string Buf;
72   {
73     raw_string_ostream OS(Buf);
74     WriteBitcodeToFile(&M, OS);
75   }
76   if (Buf.size() > MaxSize)
77     return 0;
78   memcpy(Dest, Buf.data(), Buf.size());
79   return Buf.size();
80 }
81 
82 std::unique_ptr<IRMutator> createISelMutator() {
83   std::vector<TypeGetter> Types{
84       Type::getInt1Ty,  Type::getInt8Ty,  Type::getInt16Ty, Type::getInt32Ty,
85       Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy};
86 
87   std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;
88   Strategies.emplace_back(
89       new InjectorIRStrategy(InjectorIRStrategy::getDefaultOps()));
90   Strategies.emplace_back(new InstDeleterIRStrategy());
91 
92   return llvm::make_unique<IRMutator>(std::move(Types), std::move(Strategies));
93 }
94 
95 extern "C" LLVM_ATTRIBUTE_USED size_t LLVMFuzzerCustomMutator(
96     uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed) {
97   LLVMContext Context;
98   std::unique_ptr<Module> M;
99   if (Size <= 1)
100     // We get bogus data given an empty corpus - just create a new module.
101     M.reset(new Module("M", Context));
102   else
103     M = parseModule(Data, Size, Context);
104 
105   Mutator->mutateModule(*M, Seed, Size, MaxSize);
106 
107   return writeModule(*M, Data, MaxSize);
108 }
109 
110 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
111   if (Size <= 1)
112     // We get bogus data given an empty corpus - ignore it.
113     return 0;
114 
115   LLVMContext Context;
116   auto M = parseModule(Data, Size, Context);
117   if (!M || verifyModule(*M, &errs())) {
118     errs() << "error: input module is broken!\n";
119     return 1;
120   }
121 
122   // Set up the module to build for our target.
123   M->setTargetTriple(TM->getTargetTriple().normalize());
124   M->setDataLayout(TM->createDataLayout());
125 
126   // Build up a PM to do instruction selection.
127   legacy::PassManager PM;
128   TargetLibraryInfoImpl TLII(TM->getTargetTriple());
129   PM.add(new TargetLibraryInfoWrapperPass(TLII));
130   raw_null_ostream OS;
131   TM->addPassesToEmitFile(PM, OS, TargetMachine::CGFT_Null);
132   PM.run(*M);
133 
134   return 0;
135 }
136 
137 static void handleLLVMFatalError(void *, const std::string &Message, bool) {
138   // TODO: Would it be better to call into the fuzzer internals directly?
139   dbgs() << "LLVM ERROR: " << Message << "\n"
140          << "Aborting to trigger fuzzer exit handling.\n";
141   abort();
142 }
143 
144 extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize(int *argc,
145                                                         char ***argv) {
146   EnableDebugBuffering = true;
147 
148   InitializeAllTargets();
149   InitializeAllTargetMCs();
150   InitializeAllAsmPrinters();
151   InitializeAllAsmParsers();
152 
153   handleExecNameEncodedBEOpts(*argv[0]);
154   parseFuzzerCLOpts(*argc, *argv);
155 
156   if (TargetTriple.empty()) {
157     errs() << *argv[0] << ": -mtriple must be specified\n";
158     exit(1);
159   }
160 
161   Triple TheTriple = Triple(Triple::normalize(TargetTriple));
162 
163   // Get the target specific parser.
164   std::string Error;
165   const Target *TheTarget =
166       TargetRegistry::lookupTarget(MArch, TheTriple, Error);
167   if (!TheTarget) {
168     errs() << argv[0] << ": " << Error;
169     return 1;
170   }
171 
172   // Set up the pipeline like llc does.
173   std::string CPUStr = getCPUStr(), FeaturesStr = getFeaturesStr();
174 
175   CodeGenOpt::Level OLvl = CodeGenOpt::Default;
176   switch (OptLevel) {
177   default:
178     errs() << argv[0] << ": invalid optimization level.\n";
179     return 1;
180   case ' ': break;
181   case '0': OLvl = CodeGenOpt::None; break;
182   case '1': OLvl = CodeGenOpt::Less; break;
183   case '2': OLvl = CodeGenOpt::Default; break;
184   case '3': OLvl = CodeGenOpt::Aggressive; break;
185   }
186 
187   TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
188   TM.reset(TheTarget->createTargetMachine(TheTriple.getTriple(), CPUStr,
189                                           FeaturesStr, Options, getRelocModel(),
190                                           getCodeModel(), OLvl));
191   assert(TM && "Could not allocate target machine!");
192 
193   // Make sure we print the summary and the current unit when LLVM errors out.
194   install_fatal_error_handler(handleLLVMFatalError, nullptr);
195 
196   // Finally, create our mutator.
197   Mutator = createISelMutator();
198   return 0;
199 }
200