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