blob: 1bbfa5b148b727f114c955b8106ff117696e0cdc [file] [log] [blame]
Igor Laevsky13cc9952017-11-10 12:19:08 +00001//===--- llvm-opt-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 optimization passes using libFuzzer.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Bitcode/BitcodeReader.h"
15#include "llvm/Bitcode/BitcodeWriter.h"
16#include "llvm/CodeGen/CommandFlags.h"
17#include "llvm/FuzzMutate/FuzzerCLI.h"
18#include "llvm/FuzzMutate/IRMutator.h"
19#include "llvm/FuzzMutate/Operations.h"
20#include "llvm/FuzzMutate/Random.h"
21#include "llvm/IR/Verifier.h"
22#include "llvm/Passes/PassBuilder.h"
23#include "llvm/Support/SourceMgr.h"
24#include "llvm/Support/TargetRegistry.h"
25#include "llvm/Support/TargetSelect.h"
26
27using namespace llvm;
28
29static cl::opt<std::string>
30 TargetTripleStr("mtriple", cl::desc("Override target triple for module"));
31
32// Passes to run for this fuzzer instance. Expects new pass manager syntax.
33static cl::opt<std::string> PassPipeline(
34 "passes",
35 cl::desc("A textual description of the pass pipeline for testing"));
36
37static std::unique_ptr<IRMutator> Mutator;
38static std::unique_ptr<TargetMachine> TM;
39
40// This function is mostly copied from the llvm-isel-fuzzer.
41// TODO: Move this into FuzzMutate library and reuse.
42static std::unique_ptr<Module> parseModule(const uint8_t *Data, size_t Size,
43 LLVMContext &Context) {
44
45 if (Size <= 1)
46 // We get bogus data given an empty corpus - just create a new module.
47 return llvm::make_unique<Module>("M", Context);
48
49 auto Buffer = MemoryBuffer::getMemBuffer(
50 StringRef(reinterpret_cast<const char *>(Data), Size), "Fuzzer input",
51 /*RequiresNullTerminator=*/false);
52
53 SMDiagnostic Err;
54 auto M = parseBitcodeFile(Buffer->getMemBufferRef(), Context);
55 if (Error E = M.takeError()) {
56 errs() << toString(std::move(E)) << "\n";
57 return nullptr;
58 }
59 return std::move(M.get());
60}
61
62// This function is copied from the llvm-isel-fuzzer.
63// TODO: Move this into FuzzMutate library and reuse.
64static size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize) {
65 std::string Buf;
66 {
67 raw_string_ostream OS(Buf);
68 WriteBitcodeToFile(&M, OS);
69 }
70 if (Buf.size() > MaxSize)
71 return 0;
72 memcpy(Dest, Buf.data(), Buf.size());
73 return Buf.size();
74}
75
76std::unique_ptr<IRMutator> createOptMutator() {
77 std::vector<TypeGetter> Types{
78 Type::getInt1Ty, Type::getInt8Ty, Type::getInt16Ty, Type::getInt32Ty,
79 Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy};
80
81 std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;
82 Strategies.push_back(
83 llvm::make_unique<InjectorIRStrategy>(
84 InjectorIRStrategy::getDefaultOps()));
85 Strategies.push_back(
86 llvm::make_unique<InstDeleterIRStrategy>());
87
88 return llvm::make_unique<IRMutator>(std::move(Types), std::move(Strategies));
89}
90
91extern "C" LLVM_ATTRIBUTE_USED size_t LLVMFuzzerCustomMutator(
92 uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed) {
93
94 assert(Mutator &&
95 "IR mutator should have been created during fuzzer initialization");
96
97 LLVMContext Context;
98 auto M = parseModule(Data, Size, Context);
99 if (!M || verifyModule(*M, &errs())) {
100 errs() << "error: mutator input module is broken!\n";
101 return 0;
102 }
103
104 Mutator->mutateModule(*M, Seed, Size, MaxSize);
105
106#ifndef NDEBUG
107 if (verifyModule(*M, &errs())) {
108 errs() << "mutation result doesn't pass verification\n";
109 M->dump();
110 abort();
111 }
112#endif
113
114 return writeModule(*M, Data, MaxSize);
115}
116
117extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
118 assert(TM && "Should have been created during fuzzer initialization");
119
120 if (Size <= 1)
121 // We get bogus data given an empty corpus - ignore it.
122 return 0;
123
124 // Parse module
125 //
126
127 LLVMContext Context;
128 auto M = parseModule(Data, Size, Context);
129 if (!M || verifyModule(*M, &errs())) {
130 errs() << "error: input module is broken!\n";
131 return 0;
132 }
133
134 // Set up target dependant options
135 //
136
137 M->setTargetTriple(TM->getTargetTriple().normalize());
138 M->setDataLayout(TM->createDataLayout());
139 setFunctionAttributes(TM->getTargetCPU(), TM->getTargetFeatureString(), *M);
140
141 // Create pass pipeline
142 //
143
144 PassBuilder PB(TM.get());
145
146 LoopAnalysisManager LAM;
147 FunctionAnalysisManager FAM;
148 CGSCCAnalysisManager CGAM;
149 ModulePassManager MPM;
150 ModuleAnalysisManager MAM;
151
152 FAM.registerPass([&] { return PB.buildDefaultAAPipeline(); });
153 PB.registerModuleAnalyses(MAM);
154 PB.registerCGSCCAnalyses(CGAM);
155 PB.registerFunctionAnalyses(FAM);
156 PB.registerLoopAnalyses(LAM);
157 PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
158
159 bool Ok = PB.parsePassPipeline(MPM, PassPipeline, false, false);
160 assert(Ok && "Should have been checked during fuzzer initialization");
Igor Laevskyc05ee9d2017-11-10 13:19:14 +0000161 (void)Ok; // silence unused variable warning on release builds
Igor Laevsky13cc9952017-11-10 12:19:08 +0000162
163 // Run passes which we need to test
164 //
165
166 MPM.run(*M, MAM);
167
168 // Check that passes resulted in a correct code
169 if (verifyModule(*M, &errs())) {
170 errs() << "Transformation resulted in an invalid module\n";
171 abort();
172 }
173
174 return 0;
175}
176
177static void handleLLVMFatalError(void *, const std::string &Message, bool) {
178 // TODO: Would it be better to call into the fuzzer internals directly?
179 dbgs() << "LLVM ERROR: " << Message << "\n"
180 << "Aborting to trigger fuzzer exit handling.\n";
181 abort();
182}
183
184extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize(
185 int *argc, char ***argv) {
186 EnableDebugBuffering = true;
187
188 // Make sure we print the summary and the current unit when LLVM errors out.
189 install_fatal_error_handler(handleLLVMFatalError, nullptr);
190
191 // Initialize llvm
192 //
193
194 InitializeAllTargets();
195 InitializeAllTargetMCs();
196
197 PassRegistry &Registry = *PassRegistry::getPassRegistry();
198 initializeCore(Registry);
199 initializeCoroutines(Registry);
200 initializeScalarOpts(Registry);
201 initializeObjCARCOpts(Registry);
202 initializeVectorization(Registry);
203 initializeIPO(Registry);
204 initializeAnalysis(Registry);
205 initializeTransformUtils(Registry);
206 initializeInstCombine(Registry);
207 initializeInstrumentation(Registry);
208 initializeTarget(Registry);
209
210 // Parse input options
211 //
212
213 handleExecNameEncodedOptimizerOpts(*argv[0]);
214 parseFuzzerCLOpts(*argc, *argv);
215
216 // Create TargetMachine
217 //
218
219 if (TargetTripleStr.empty()) {
220 errs() << *argv[0] << ": -mtriple must be specified\n";
221 exit(1);
222 }
223 Triple TargetTriple = Triple(Triple::normalize(TargetTripleStr));
224
225 std::string Error;
226 const Target *TheTarget =
227 TargetRegistry::lookupTarget(MArch, TargetTriple, Error);
228 if (!TheTarget) {
229 errs() << *argv[0] << ": " << Error;
230 exit(1);
231 }
232
233 TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
234 TM.reset(TheTarget->createTargetMachine(
235 TargetTriple.getTriple(), getCPUStr(), getFeaturesStr(),
236 Options, getRelocModel(), getCodeModel(), CodeGenOpt::Default));
237 assert(TM && "Could not allocate target machine!");
238
239 // Check that pass pipeline is specified and correct
240 //
241
242 if (PassPipeline.empty()) {
243 errs() << *argv[0] << ": at least one pass should be specified\n";
244 exit(1);
245 }
246
247 PassBuilder PB(TM.get());
248 ModulePassManager MPM;
249 if (!PB.parsePassPipeline(MPM, PassPipeline, false, false)) {
250 errs() << *argv[0] << ": can't parse pass pipeline\n";
251 exit(1);
252 }
253
254 // Create mutator
255 //
256
257 Mutator = createOptMutator();
258
259 return 0;
260}