1 | //===--- llvm-opt-fuzzer.cpp - Fuzzer for instruction selection ----------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
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/IR/Verifier.h" |
19 | #include "llvm/MC/TargetRegistry.h" |
20 | #include "llvm/Passes/PassBuilder.h" |
21 | #include "llvm/Support/CommandLine.h" |
22 | #include "llvm/Support/SourceMgr.h" |
23 | #include "llvm/Support/TargetSelect.h" |
24 | #include "llvm/Target/TargetMachine.h" |
25 | |
26 | using namespace llvm; |
27 | |
28 | static codegen::RegisterCodeGenFlags CGF; |
29 | |
30 | static cl::opt<std::string> |
31 | TargetTripleStr("mtriple" , cl::desc("Override target triple for module" )); |
32 | |
33 | // Passes to run for this fuzzer instance. Expects new pass manager syntax. |
34 | static cl::opt<std::string> PassPipeline( |
35 | "passes" , |
36 | cl::desc("A textual description of the pass pipeline for testing" )); |
37 | |
38 | static std::unique_ptr<IRMutator> Mutator; |
39 | static std::unique_ptr<TargetMachine> TM; |
40 | |
41 | std::unique_ptr<IRMutator> createOptMutator() { |
42 | std::vector<TypeGetter> Types{ |
43 | Type::getInt1Ty, Type::getInt8Ty, Type::getInt16Ty, Type::getInt32Ty, |
44 | Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy}; |
45 | |
46 | std::vector<std::unique_ptr<IRMutationStrategy>> Strategies; |
47 | Strategies.push_back(x: std::make_unique<InjectorIRStrategy>( |
48 | args: InjectorIRStrategy::getDefaultOps())); |
49 | Strategies.push_back(x: std::make_unique<InstDeleterIRStrategy>()); |
50 | Strategies.push_back(x: std::make_unique<InstModificationIRStrategy>()); |
51 | |
52 | return std::make_unique<IRMutator>(args: std::move(Types), args: std::move(Strategies)); |
53 | } |
54 | |
55 | extern "C" LLVM_ATTRIBUTE_USED size_t LLVMFuzzerCustomMutator( |
56 | uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed) { |
57 | |
58 | assert(Mutator && |
59 | "IR mutator should have been created during fuzzer initialization" ); |
60 | |
61 | LLVMContext Context; |
62 | auto M = parseAndVerify(Data, Size, Context); |
63 | if (!M) { |
64 | errs() << "error: mutator input module is broken!\n" ; |
65 | return 0; |
66 | } |
67 | |
68 | Mutator->mutateModule(M&: *M, Seed, MaxSize); |
69 | |
70 | if (verifyModule(M: *M, OS: &errs())) { |
71 | errs() << "mutation result doesn't pass verification\n" ; |
72 | #ifndef NDEBUG |
73 | M->dump(); |
74 | #endif |
75 | // Avoid adding incorrect test cases to the corpus. |
76 | return 0; |
77 | } |
78 | |
79 | std::string Buf; |
80 | { |
81 | raw_string_ostream OS(Buf); |
82 | WriteBitcodeToFile(M: *M, Out&: OS); |
83 | } |
84 | if (Buf.size() > MaxSize) |
85 | return 0; |
86 | |
87 | // There are some invariants which are not checked by the verifier in favor |
88 | // of having them checked by the parser. They may be considered as bugs in the |
89 | // verifier and should be fixed there. However until all of those are covered |
90 | // we want to check for them explicitly. Otherwise we will add incorrect input |
91 | // to the corpus and this is going to confuse the fuzzer which will start |
92 | // exploration of the bitcode reader error handling code. |
93 | auto NewM = parseAndVerify(Data: reinterpret_cast<const uint8_t *>(Buf.data()), |
94 | Size: Buf.size(), Context); |
95 | if (!NewM) { |
96 | errs() << "mutator failed to re-read the module\n" ; |
97 | #ifndef NDEBUG |
98 | M->dump(); |
99 | #endif |
100 | return 0; |
101 | } |
102 | |
103 | memcpy(dest: Data, src: Buf.data(), n: Buf.size()); |
104 | return Buf.size(); |
105 | } |
106 | |
107 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { |
108 | assert(TM && "Should have been created during fuzzer initialization" ); |
109 | |
110 | if (Size <= 1) |
111 | // We get bogus data given an empty corpus - ignore it. |
112 | return 0; |
113 | |
114 | // Parse module |
115 | // |
116 | |
117 | LLVMContext Context; |
118 | auto M = parseAndVerify(Data, Size, Context); |
119 | if (!M) { |
120 | errs() << "error: input module is broken!\n" ; |
121 | return 0; |
122 | } |
123 | |
124 | // Set up target dependant options |
125 | // |
126 | |
127 | M->setTargetTriple(TM->getTargetTriple().normalize()); |
128 | M->setDataLayout(TM->createDataLayout()); |
129 | codegen::setFunctionAttributes(CPU: TM->getTargetCPU(), |
130 | Features: TM->getTargetFeatureString(), M&: *M); |
131 | |
132 | // Create pass pipeline |
133 | // |
134 | |
135 | PassBuilder PB(TM.get()); |
136 | |
137 | LoopAnalysisManager LAM; |
138 | FunctionAnalysisManager FAM; |
139 | CGSCCAnalysisManager CGAM; |
140 | ModulePassManager MPM; |
141 | ModuleAnalysisManager MAM; |
142 | |
143 | PB.registerModuleAnalyses(MAM); |
144 | PB.registerCGSCCAnalyses(CGAM); |
145 | PB.registerFunctionAnalyses(FAM); |
146 | PB.registerLoopAnalyses(LAM); |
147 | PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); |
148 | |
149 | auto Err = PB.parsePassPipeline(MPM, PipelineText: PassPipeline); |
150 | assert(!Err && "Should have been checked during fuzzer initialization" ); |
151 | // Only fail with assert above, otherwise ignore the parsing error. |
152 | consumeError(Err: std::move(Err)); |
153 | |
154 | // Run passes which we need to test |
155 | // |
156 | |
157 | MPM.run(IR&: *M, AM&: MAM); |
158 | |
159 | // Check that passes resulted in a correct code |
160 | if (verifyModule(M: *M, OS: &errs())) { |
161 | errs() << "Transformation resulted in an invalid module\n" ; |
162 | abort(); |
163 | } |
164 | |
165 | return 0; |
166 | } |
167 | |
168 | static void handleLLVMFatalError(void *, const char *Message, bool) { |
169 | // TODO: Would it be better to call into the fuzzer internals directly? |
170 | dbgs() << "LLVM ERROR: " << Message << "\n" |
171 | << "Aborting to trigger fuzzer exit handling.\n" ; |
172 | abort(); |
173 | } |
174 | |
175 | extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize(int *argc, |
176 | char ***argv) { |
177 | EnableDebugBuffering = true; |
178 | |
179 | // Make sure we print the summary and the current unit when LLVM errors out. |
180 | install_fatal_error_handler(handler: handleLLVMFatalError, user_data: nullptr); |
181 | |
182 | // Initialize llvm |
183 | // |
184 | |
185 | InitializeAllTargets(); |
186 | InitializeAllTargetMCs(); |
187 | |
188 | // Parse input options |
189 | // |
190 | |
191 | handleExecNameEncodedOptimizerOpts(ExecName: *argv[0]); |
192 | parseFuzzerCLOpts(ArgC: *argc, ArgV: *argv); |
193 | |
194 | // Create TargetMachine |
195 | // |
196 | |
197 | if (TargetTripleStr.empty()) { |
198 | errs() << *argv[0] << ": -mtriple must be specified\n" ; |
199 | exit(status: 1); |
200 | } |
201 | ExitOnError ExitOnErr(std::string(*argv[0]) + ": error:" ); |
202 | TM = ExitOnErr(codegen::createTargetMachineForTriple( |
203 | TargetTriple: Triple::normalize(Str: TargetTripleStr))); |
204 | |
205 | // Check that pass pipeline is specified and correct |
206 | // |
207 | |
208 | if (PassPipeline.empty()) { |
209 | errs() << *argv[0] << ": at least one pass should be specified\n" ; |
210 | exit(status: 1); |
211 | } |
212 | |
213 | PassBuilder PB(TM.get()); |
214 | ModulePassManager MPM; |
215 | if (auto Err = PB.parsePassPipeline(MPM, PipelineText: PassPipeline)) { |
216 | errs() << *argv[0] << ": " << toString(E: std::move(Err)) << "\n" ; |
217 | exit(status: 1); |
218 | } |
219 | |
220 | // Create mutator |
221 | // |
222 | |
223 | Mutator = createOptMutator(); |
224 | |
225 | return 0; |
226 | } |
227 | |