llvm.org GIT mirror llvm / 089f886
[llvm-opt-fuzzer] Introduce llvm-opt-fuzzer for fuzzing optimization passes This change adds generic fuzzing tools capable of running libFuzzer tests on any optimization pass or combination of them. Differential Revision: https://reviews.llvm.org/D39555 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@317883 91177308-0d34-0410-b5e6-96231b3b80d8 Igor Laevsky 1 year, 9 months ago
6 changed file(s) with 364 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
9999
100100 % bin/llvm-isel-fuzzer--aarch64-O0-gisel
101101
102 llvm-opt-fuzzer
103 ---------------
104
105 A |LLVM IR fuzzer| aimed at finding bugs in optimization passes.
106
107 It receives optimzation pipeline and runs it for each fuzzer input.
108
109 Interface of this fuzzer almost directly mirrors ``llvm-isel-fuzzer``. Both
110 ``mtriple`` and ``passes`` arguments are required. Passes are specified in a
111 format suitable for the new pass manager.
112
113 .. code-block:: shell
114
115 % bin/llvm-opt-fuzzer -ignore_remaining_args=1 -mtriple x86_64 -passes instcombine
116
117 Similarly to the ``llvm-isel-fuzzer`` arguments in some predefined configurations
118 might be embedded directly into the binary file name:
119
120 .. code-block:: shell
121
122 % bin/llvm-opt-fuzzer--x86_64-instcombine
123
102124 llvm-mc-assemble-fuzzer
103125 -----------------------
104126
3535 /// of passing in command line arguments in the normal way.
3636 void handleExecNameEncodedBEOpts(StringRef ExecName);
3737
38 /// Handle optimizer options which are encoded in the executable name.
39 /// Same semantics as in 'handleExecNameEncodedBEOpts'.
40 void handleExecNameEncodedOptimizerOpts(StringRef ExecName);
41
3842 using FuzzerTestFun = int (*)(const uint8_t *Data, size_t Size);
3943 using FuzzerInitFun = int (*)(int *argc, char ***argv);
4044
6666 cl::ParseCommandLineOptions(CLArgs.size(), CLArgs.data());
6767 }
6868
69 void llvm::handleExecNameEncodedOptimizerOpts(StringRef ExecName) {
70 // TODO: Refactor parts common with the 'handleExecNameEncodedBEOpts'
71 std::vector Args{ExecName};
72
73 auto NameAndArgs = ExecName.split("--");
74 if (NameAndArgs.second.empty())
75 return;
76
77 SmallVector Opts;
78 NameAndArgs.second.split(Opts, '-');
79 for (StringRef Opt : Opts) {
80 if (Opt.startswith("instcombine")) {
81 Args.push_back("-passes=instcombine");
82 } else if (Triple(Opt).getArch()) {
83 Args.push_back("-mtriple=" + Opt.str());
84 } else {
85 errs() << ExecName << ": Unknown option: " << Opt << ".\n";
86 exit(1);
87 }
88 }
89
90 errs() << NameAndArgs.first << ": Injected args:";
91 for (int I = 1, E = Args.size(); I < E; ++I)
92 errs() << " " << Args[I];
93 errs() << "\n";
94
95 std::vector CLArgs;
96 CLArgs.reserve(Args.size());
97 for (std::string &S : Args)
98 CLArgs.push_back(S.c_str());
99
100 cl::ParseCommandLineOptions(CLArgs.size(), CLArgs.data());
101 }
102
69103 int llvm::runFuzzerOnInputs(int ArgC, char *ArgV[], FuzzerTestFun TestOne,
70104 FuzzerInitFun Init) {
71105 errs() << "*** This tool was not linked to libFuzzer.\n"
0 set(LLVM_LINK_COMPONENTS
1 ${LLVM_TARGETS_TO_BUILD}
2 Analysis
3 BitWriter
4 CodeGen
5 Core
6 Coroutines
7 IPO
8 IRReader
9 InstCombine
10 Instrumentation
11 FuzzMutate
12 MC
13 ObjCARCOpts
14 ScalarOpts
15 Support
16 Target
17 TransformUtils
18 Vectorize
19 Passes
20 )
21
22 add_llvm_fuzzer(llvm-opt-fuzzer llvm-opt-fuzzer.cpp
23 DUMMY_MAIN DummyOptFuzzer.cpp)
0 //===--- DummyOptFuzzer.cpp - Entry point to sanity check the fuzzer ------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Implementation of main so we can build and test without linking libFuzzer.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "llvm/FuzzMutate/FuzzerCLI.h"
14
15 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
16 extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv);
17 int main(int argc, char *argv[]) {
18 return llvm::runFuzzerOnInputs(argc, argv, LLVMFuzzerTestOneInput,
19 LLVMFuzzerInitialize);
20 }
0 //===--- llvm-opt-fuzzer.cpp - Fuzzer for instruction selection ----------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Tool to fuzz optimization passes using libFuzzer.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "llvm/Bitcode/BitcodeReader.h"
14 #include "llvm/Bitcode/BitcodeWriter.h"
15 #include "llvm/CodeGen/CommandFlags.h"
16 #include "llvm/FuzzMutate/FuzzerCLI.h"
17 #include "llvm/FuzzMutate/IRMutator.h"
18 #include "llvm/FuzzMutate/Operations.h"
19 #include "llvm/FuzzMutate/Random.h"
20 #include "llvm/IR/Verifier.h"
21 #include "llvm/Passes/PassBuilder.h"
22 #include "llvm/Support/SourceMgr.h"
23 #include "llvm/Support/TargetRegistry.h"
24 #include "llvm/Support/TargetSelect.h"
25
26 using namespace llvm;
27
28 static cl::opt
29 TargetTripleStr("mtriple", cl::desc("Override target triple for module"));
30
31 // Passes to run for this fuzzer instance. Expects new pass manager syntax.
32 static cl::opt PassPipeline(
33 "passes",
34 cl::desc("A textual description of the pass pipeline for testing"));
35
36 static std::unique_ptr Mutator;
37 static std::unique_ptr TM;
38
39 // This function is mostly copied from the llvm-isel-fuzzer.
40 // TODO: Move this into FuzzMutate library and reuse.
41 static std::unique_ptr parseModule(const uint8_t *Data, size_t Size,
42 LLVMContext &Context) {
43
44 if (Size <= 1)
45 // We get bogus data given an empty corpus - just create a new module.
46 return llvm::make_unique("M", Context);
47
48 auto Buffer = MemoryBuffer::getMemBuffer(
49 StringRef(reinterpret_cast(Data), Size), "Fuzzer input",
50 /*RequiresNullTerminator=*/false);
51
52 SMDiagnostic Err;
53 auto M = parseBitcodeFile(Buffer->getMemBufferRef(), Context);
54 if (Error E = M.takeError()) {
55 errs() << toString(std::move(E)) << "\n";
56 return nullptr;
57 }
58 return std::move(M.get());
59 }
60
61 // This function is copied from the llvm-isel-fuzzer.
62 // TODO: Move this into FuzzMutate library and reuse.
63 static size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize) {
64 std::string Buf;
65 {
66 raw_string_ostream OS(Buf);
67 WriteBitcodeToFile(&M, OS);
68 }
69 if (Buf.size() > MaxSize)
70 return 0;
71 memcpy(Dest, Buf.data(), Buf.size());
72 return Buf.size();
73 }
74
75 std::unique_ptr createOptMutator() {
76 std::vector Types{
77 Type::getInt1Ty, Type::getInt8Ty, Type::getInt16Ty, Type::getInt32Ty,
78 Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy};
79
80 std::vector> Strategies;
81 Strategies.push_back(
82 llvm::make_unique(
83 InjectorIRStrategy::getDefaultOps()));
84 Strategies.push_back(
85 llvm::make_unique());
86
87 return llvm::make_unique(std::move(Types), std::move(Strategies));
88 }
89
90 extern "C" LLVM_ATTRIBUTE_USED size_t LLVMFuzzerCustomMutator(
91 uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed) {
92
93 assert(Mutator &&
94 "IR mutator should have been created during fuzzer initialization");
95
96 LLVMContext Context;
97 auto M = parseModule(Data, Size, Context);
98 if (!M || verifyModule(*M, &errs())) {
99 errs() << "error: mutator input module is broken!\n";
100 return 0;
101 }
102
103 Mutator->mutateModule(*M, Seed, Size, MaxSize);
104
105 #ifndef NDEBUG
106 if (verifyModule(*M, &errs())) {
107 errs() << "mutation result doesn't pass verification\n";
108 M->dump();
109 abort();
110 }
111 #endif
112
113 return writeModule(*M, Data, MaxSize);
114 }
115
116 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
117 assert(TM && "Should have been created during fuzzer initialization");
118
119 if (Size <= 1)
120 // We get bogus data given an empty corpus - ignore it.
121 return 0;
122
123 // Parse module
124 //
125
126 LLVMContext Context;
127 auto M = parseModule(Data, Size, Context);
128 if (!M || verifyModule(*M, &errs())) {
129 errs() << "error: input module is broken!\n";
130 return 0;
131 }
132
133 // Set up target dependant options
134 //
135
136 M->setTargetTriple(TM->getTargetTriple().normalize());
137 M->setDataLayout(TM->createDataLayout());
138 setFunctionAttributes(TM->getTargetCPU(), TM->getTargetFeatureString(), *M);
139
140 // Create pass pipeline
141 //
142
143 PassBuilder PB(TM.get());
144
145 LoopAnalysisManager LAM;
146 FunctionAnalysisManager FAM;
147 CGSCCAnalysisManager CGAM;
148 ModulePassManager MPM;
149 ModuleAnalysisManager MAM;
150
151 FAM.registerPass([&] { return PB.buildDefaultAAPipeline(); });
152 PB.registerModuleAnalyses(MAM);
153 PB.registerCGSCCAnalyses(CGAM);
154 PB.registerFunctionAnalyses(FAM);
155 PB.registerLoopAnalyses(LAM);
156 PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
157
158 bool Ok = PB.parsePassPipeline(MPM, PassPipeline, false, false);
159 assert(Ok && "Should have been checked during fuzzer initialization");
160
161 // Run passes which we need to test
162 //
163
164 MPM.run(*M, MAM);
165
166 // Check that passes resulted in a correct code
167 if (verifyModule(*M, &errs())) {
168 errs() << "Transformation resulted in an invalid module\n";
169 abort();
170 }
171
172 return 0;
173 }
174
175 static void handleLLVMFatalError(void *, const std::string &Message, bool) {
176 // TODO: Would it be better to call into the fuzzer internals directly?
177 dbgs() << "LLVM ERROR: " << Message << "\n"
178 << "Aborting to trigger fuzzer exit handling.\n";
179 abort();
180 }
181
182 extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize(
183 int *argc, char ***argv) {
184 EnableDebugBuffering = true;
185
186 // Make sure we print the summary and the current unit when LLVM errors out.
187 install_fatal_error_handler(handleLLVMFatalError, nullptr);
188
189 // Initialize llvm
190 //
191
192 InitializeAllTargets();
193 InitializeAllTargetMCs();
194
195 PassRegistry &Registry = *PassRegistry::getPassRegistry();
196 initializeCore(Registry);
197 initializeCoroutines(Registry);
198 initializeScalarOpts(Registry);
199 initializeObjCARCOpts(Registry);
200 initializeVectorization(Registry);
201 initializeIPO(Registry);
202 initializeAnalysis(Registry);
203 initializeTransformUtils(Registry);
204 initializeInstCombine(Registry);
205 initializeInstrumentation(Registry);
206 initializeTarget(Registry);
207
208 // Parse input options
209 //
210
211 handleExecNameEncodedOptimizerOpts(*argv[0]);
212 parseFuzzerCLOpts(*argc, *argv);
213
214 // Create TargetMachine
215 //
216
217 if (TargetTripleStr.empty()) {
218 errs() << *argv[0] << ": -mtriple must be specified\n";
219 exit(1);
220 }
221 Triple TargetTriple = Triple(Triple::normalize(TargetTripleStr));
222
223 std::string Error;
224 const Target *TheTarget =
225 TargetRegistry::lookupTarget(MArch, TargetTriple, Error);
226 if (!TheTarget) {
227 errs() << *argv[0] << ": " << Error;
228 exit(1);
229 }
230
231 TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
232 TM.reset(TheTarget->createTargetMachine(
233 TargetTriple.getTriple(), getCPUStr(), getFeaturesStr(),
234 Options, getRelocModel(), getCodeModel(), CodeGenOpt::Default));
235 assert(TM && "Could not allocate target machine!");
236
237 // Check that pass pipeline is specified and correct
238 //
239
240 if (PassPipeline.empty()) {
241 errs() << *argv[0] << ": at least one pass should be specified\n";
242 exit(1);
243 }
244
245 PassBuilder PB(TM.get());
246 ModulePassManager MPM;
247 if (!PB.parsePassPipeline(MPM, PassPipeline, false, false)) {
248 errs() << *argv[0] << ": can't parse pass pipeline\n";
249 exit(1);
250 }
251
252 // Create mutator
253 //
254
255 Mutator = createOptMutator();
256
257 return 0;
258 }