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