1 | //===--- llvm-isel-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 instruction selection using libFuzzer. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "llvm/ADT/StringRef.h" |
14 | #include "llvm/Analysis/TargetLibraryInfo.h" |
15 | #include "llvm/Bitcode/BitcodeReader.h" |
16 | #include "llvm/Bitcode/BitcodeWriter.h" |
17 | #include "llvm/CodeGen/CommandFlags.h" |
18 | #include "llvm/FuzzMutate/FuzzerCLI.h" |
19 | #include "llvm/FuzzMutate/IRMutator.h" |
20 | #include "llvm/FuzzMutate/Operations.h" |
21 | #include "llvm/IR/Constants.h" |
22 | #include "llvm/IR/LLVMContext.h" |
23 | #include "llvm/IR/LegacyPassManager.h" |
24 | #include "llvm/IR/Module.h" |
25 | #include "llvm/IR/Verifier.h" |
26 | #include "llvm/IRReader/IRReader.h" |
27 | #include "llvm/MC/TargetRegistry.h" |
28 | #include "llvm/Support/CommandLine.h" |
29 | #include "llvm/Support/DataTypes.h" |
30 | #include "llvm/Support/Debug.h" |
31 | #include "llvm/Support/SourceMgr.h" |
32 | #include "llvm/Support/TargetSelect.h" |
33 | #include "llvm/Target/TargetMachine.h" |
34 | |
35 | #define DEBUG_TYPE "isel-fuzzer" |
36 | |
37 | using namespace llvm; |
38 | |
39 | static codegen::RegisterCodeGenFlags CGF; |
40 | |
41 | static cl::opt<char> |
42 | OptLevel("O" , |
43 | cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " |
44 | "(default = '-O2')" ), |
45 | cl::Prefix, cl::init(Val: '2')); |
46 | |
47 | static cl::opt<std::string> |
48 | TargetTriple("mtriple" , cl::desc("Override target triple for module" )); |
49 | |
50 | static std::unique_ptr<TargetMachine> TM; |
51 | static std::unique_ptr<IRMutator> Mutator; |
52 | |
53 | std::unique_ptr<IRMutator> createISelMutator() { |
54 | std::vector<TypeGetter> Types{ |
55 | Type::getInt1Ty, Type::getInt8Ty, Type::getInt16Ty, Type::getInt32Ty, |
56 | Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy}; |
57 | |
58 | std::vector<std::unique_ptr<IRMutationStrategy>> Strategies; |
59 | Strategies.emplace_back( |
60 | args: new InjectorIRStrategy(InjectorIRStrategy::getDefaultOps())); |
61 | Strategies.emplace_back(args: new InstDeleterIRStrategy()); |
62 | |
63 | return std::make_unique<IRMutator>(args: std::move(Types), args: std::move(Strategies)); |
64 | } |
65 | |
66 | extern "C" LLVM_ATTRIBUTE_USED size_t LLVMFuzzerCustomMutator( |
67 | uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed) { |
68 | LLVMContext Context; |
69 | std::unique_ptr<Module> M; |
70 | if (Size <= 1) |
71 | // We get bogus data given an empty corpus - just create a new module. |
72 | M.reset(p: new Module("M" , Context)); |
73 | else |
74 | M = parseModule(Data, Size, Context); |
75 | |
76 | Mutator->mutateModule(M&: *M, Seed, MaxSize); // use max bitcode size as a guide |
77 | |
78 | return writeModule(M: *M, Dest: Data, MaxSize); |
79 | } |
80 | |
81 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { |
82 | if (Size <= 1) |
83 | // We get bogus data given an empty corpus - ignore it. |
84 | return 0; |
85 | |
86 | LLVMContext Context; |
87 | auto M = parseAndVerify(Data, Size, Context); |
88 | if (!M) { |
89 | errs() << "error: input module is broken!\n" ; |
90 | return 0; |
91 | } |
92 | |
93 | // Set up the module to build for our target. |
94 | M->setTargetTriple(TM->getTargetTriple().normalize()); |
95 | M->setDataLayout(TM->createDataLayout()); |
96 | |
97 | // Build up a PM to do instruction selection. |
98 | legacy::PassManager PM; |
99 | TargetLibraryInfoImpl TLII(TM->getTargetTriple()); |
100 | PM.add(P: new TargetLibraryInfoWrapperPass(TLII)); |
101 | raw_null_ostream OS; |
102 | TM->addPassesToEmitFile(PM, OS, nullptr, CodeGenFileType::Null); |
103 | PM.run(M&: *M); |
104 | |
105 | return 0; |
106 | } |
107 | |
108 | static void handleLLVMFatalError(void *, const char *Message, bool) { |
109 | // TODO: Would it be better to call into the fuzzer internals directly? |
110 | dbgs() << "LLVM ERROR: " << Message << "\n" |
111 | << "Aborting to trigger fuzzer exit handling.\n" ; |
112 | abort(); |
113 | } |
114 | |
115 | extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize(int *argc, |
116 | char ***argv) { |
117 | EnableDebugBuffering = true; |
118 | |
119 | InitializeAllTargets(); |
120 | InitializeAllTargetMCs(); |
121 | InitializeAllAsmPrinters(); |
122 | InitializeAllAsmParsers(); |
123 | |
124 | handleExecNameEncodedBEOpts(ExecName: *argv[0]); |
125 | parseFuzzerCLOpts(ArgC: *argc, ArgV: *argv); |
126 | |
127 | if (TargetTriple.empty()) { |
128 | errs() << *argv[0] << ": -mtriple must be specified\n" ; |
129 | exit(status: 1); |
130 | } |
131 | |
132 | // Set up the pipeline like llc does. |
133 | |
134 | CodeGenOptLevel OLvl; |
135 | if (auto Level = CodeGenOpt::parseLevel(C: OptLevel)) { |
136 | OLvl = *Level; |
137 | } else { |
138 | errs() << argv[0] << ": invalid optimization level.\n" ; |
139 | return 1; |
140 | } |
141 | ExitOnError ExitOnErr(std::string(*argv[0]) + ": error:" ); |
142 | TM = ExitOnErr(codegen::createTargetMachineForTriple( |
143 | TargetTriple: Triple::normalize(Str: TargetTriple), OptLevel: OLvl)); |
144 | assert(TM && "Could not allocate target machine!" ); |
145 | |
146 | // Make sure we print the summary and the current unit when LLVM errors out. |
147 | install_fatal_error_handler(handler: handleLLVMFatalError, user_data: nullptr); |
148 | |
149 | // Finally, create our mutator. |
150 | Mutator = createISelMutator(); |
151 | return 0; |
152 | } |
153 | |