xref: /llvm-project/llvm/tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp (revision 2ebcca2323e1cb037393f7d0ac41247c148a8e03)
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 "FuzzerInterface.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/Analysis/TargetLibraryInfo.h"
17 #include "llvm/Bitcode/BitcodeReader.h"
18 #include "llvm/Bitcode/BitcodeWriter.h"
19 #include "llvm/CodeGen/CommandFlags.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 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 /// Parse command line options, but ignore anything before '--'.
138 static void parseCLOptsAfterDashDash(int argc, char *argv[]) {
139   std::vector<const char *> CLArgs;
140   CLArgs.push_back(argv[0]);
141 
142   int I = 1;
143   while (I < argc)
144     if (StringRef(argv[I++]).equals("-ignore_remaining_args=1"))
145       break;
146   while (I < argc)
147     CLArgs.push_back(argv[I++]);
148 
149   cl::ParseCommandLineOptions(CLArgs.size(), CLArgs.data());
150 }
151 
152 static void handleLLVMFatalError(void *, const std::string &Message, bool) {
153   // TODO: Would it be better to call into the fuzzer internals directly?
154   dbgs() << "LLVM ERROR: " << Message << "\n"
155          << "Aborting to trigger fuzzer exit handling.\n";
156   abort();
157 }
158 
159 extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize(int *argc,
160                                                         char ***argv) {
161   EnableDebugBuffering = true;
162 
163   InitializeAllTargets();
164   InitializeAllTargetMCs();
165   InitializeAllAsmPrinters();
166   InitializeAllAsmParsers();
167 
168   parseCLOptsAfterDashDash(*argc, *argv);
169 
170   if (TargetTriple.empty()) {
171     errs() << *argv[0] << ": -mtriple must be specified\n";
172     return 1;
173   }
174 
175   Triple TheTriple = Triple(Triple::normalize(TargetTriple));
176 
177   // Get the target specific parser.
178   std::string Error;
179   const Target *TheTarget =
180       TargetRegistry::lookupTarget(MArch, TheTriple, Error);
181   if (!TheTarget) {
182     errs() << argv[0] << ": " << Error;
183     return 1;
184   }
185 
186   // Set up the pipeline like llc does.
187   std::string CPUStr = getCPUStr(), FeaturesStr = getFeaturesStr();
188 
189   CodeGenOpt::Level OLvl = CodeGenOpt::Default;
190   switch (OptLevel) {
191   default:
192     errs() << argv[0] << ": invalid optimization level.\n";
193     return 1;
194   case ' ': break;
195   case '0': OLvl = CodeGenOpt::None; break;
196   case '1': OLvl = CodeGenOpt::Less; break;
197   case '2': OLvl = CodeGenOpt::Default; break;
198   case '3': OLvl = CodeGenOpt::Aggressive; break;
199   }
200 
201   TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
202   TM.reset(TheTarget->createTargetMachine(TheTriple.getTriple(), CPUStr,
203                                           FeaturesStr, Options, getRelocModel(),
204                                           getCodeModel(), OLvl));
205   assert(TM && "Could not allocate target machine!");
206 
207   // Make sure we print the summary and the current unit when LLVM errors out.
208   install_fatal_error_handler(handleLLVMFatalError, nullptr);
209 
210   // Finally, create our mutator.
211   Mutator = createISelMutator();
212   return 0;
213 }
214