blob: 9cfe27748e9f61347409b99d13ff228e04b31e1b [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");
161
162 // Run passes which we need to test
163 //
164
165 MPM.run(*M, MAM);
166
167 // Check that passes resulted in a correct code
168 if (verifyModule(*M, &errs())) {
169 errs() << "Transformation resulted in an invalid module\n";
170 abort();
171 }
172
173 return 0;
174}
175
176static void handleLLVMFatalError(void *, const std::string &Message, bool) {
177 // TODO: Would it be better to call into the fuzzer internals directly?
178 dbgs() << "LLVM ERROR: " << Message << "\n"
179 << "Aborting to trigger fuzzer exit handling.\n";
180 abort();
181}
182
183extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize(
184 int *argc, char ***argv) {
185 EnableDebugBuffering = true;
186
187 // Make sure we print the summary and the current unit when LLVM errors out.
188 install_fatal_error_handler(handleLLVMFatalError, nullptr);
189
190 // Initialize llvm
191 //
192
193 InitializeAllTargets();
194 InitializeAllTargetMCs();
195
196 PassRegistry &Registry = *PassRegistry::getPassRegistry();
197 initializeCore(Registry);
198 initializeCoroutines(Registry);
199 initializeScalarOpts(Registry);
200 initializeObjCARCOpts(Registry);
201 initializeVectorization(Registry);
202 initializeIPO(Registry);
203 initializeAnalysis(Registry);
204 initializeTransformUtils(Registry);
205 initializeInstCombine(Registry);
206 initializeInstrumentation(Registry);
207 initializeTarget(Registry);
208
209 // Parse input options
210 //
211
212 handleExecNameEncodedOptimizerOpts(*argv[0]);
213 parseFuzzerCLOpts(*argc, *argv);
214
215 // Create TargetMachine
216 //
217
218 if (TargetTripleStr.empty()) {
219 errs() << *argv[0] << ": -mtriple must be specified\n";
220 exit(1);
221 }
222 Triple TargetTriple = Triple(Triple::normalize(TargetTripleStr));
223
224 std::string Error;
225 const Target *TheTarget =
226 TargetRegistry::lookupTarget(MArch, TargetTriple, Error);
227 if (!TheTarget) {
228 errs() << *argv[0] << ": " << Error;
229 exit(1);
230 }
231
232 TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
233 TM.reset(TheTarget->createTargetMachine(
234 TargetTriple.getTriple(), getCPUStr(), getFeaturesStr(),
235 Options, getRelocModel(), getCodeModel(), CodeGenOpt::Default));
236 assert(TM && "Could not allocate target machine!");
237
238 // Check that pass pipeline is specified and correct
239 //
240
241 if (PassPipeline.empty()) {
242 errs() << *argv[0] << ": at least one pass should be specified\n";
243 exit(1);
244 }
245
246 PassBuilder PB(TM.get());
247 ModulePassManager MPM;
248 if (!PB.parsePassPipeline(MPM, PassPipeline, false, false)) {
249 errs() << *argv[0] << ": can't parse pass pipeline\n";
250 exit(1);
251 }
252
253 // Create mutator
254 //
255
256 Mutator = createOptMutator();
257
258 return 0;
259}