1 //===-- llvm-mc-assemble-fuzzer.cpp - Fuzzer for the MC layer -------------===// 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 //===----------------------------------------------------------------------===// 10 11 #include "llvm-c/Target.h" 12 #include "llvm/MC/MCAsmBackend.h" 13 #include "llvm/MC/MCAsmInfo.h" 14 #include "llvm/MC/MCCodeEmitter.h" 15 #include "llvm/MC/MCContext.h" 16 #include "llvm/MC/MCInstPrinter.h" 17 #include "llvm/MC/MCInstrInfo.h" 18 #include "llvm/MC/MCObjectFileInfo.h" 19 #include "llvm/MC/MCObjectWriter.h" 20 #include "llvm/MC/MCParser/AsmLexer.h" 21 #include "llvm/MC/MCParser/MCTargetAsmParser.h" 22 #include "llvm/MC/MCRegisterInfo.h" 23 #include "llvm/MC/MCSectionMachO.h" 24 #include "llvm/MC/MCStreamer.h" 25 #include "llvm/MC/MCSubtargetInfo.h" 26 #include "llvm/MC/MCTargetOptionsCommandFlags.h" 27 #include "llvm/MC/SubtargetFeature.h" 28 #include "llvm/Support/CommandLine.h" 29 #include "llvm/Support/FileUtilities.h" 30 #include "llvm/Support/Host.h" 31 #include "llvm/Support/MemoryBuffer.h" 32 #include "llvm/Support/SourceMgr.h" 33 #include "llvm/Support/TargetRegistry.h" 34 #include "llvm/Support/TargetSelect.h" 35 #include "llvm/Support/ToolOutputFile.h" 36 #include "llvm/Support/raw_ostream.h" 37 38 using namespace llvm; 39 40 static cl::opt<std::string> 41 TripleName("triple", cl::desc("Target triple to assemble for, " 42 "see -version for available targets")); 43 44 static cl::opt<std::string> 45 MCPU("mcpu", 46 cl::desc("Target a specific cpu type (-mcpu=help for details)"), 47 cl::value_desc("cpu-name"), cl::init("")); 48 49 // This is useful for variable-length instruction sets. 50 static cl::opt<unsigned> InsnLimit( 51 "insn-limit", 52 cl::desc("Limit the number of instructions to process (0 for no limit)"), 53 cl::value_desc("count"), cl::init(0)); 54 55 static cl::list<std::string> 56 MAttrs("mattr", cl::CommaSeparated, 57 cl::desc("Target specific attributes (-mattr=help for details)"), 58 cl::value_desc("a1,+a2,-a3,...")); 59 // The feature string derived from -mattr's values. 60 std::string FeaturesStr; 61 62 static cl::list<std::string> 63 FuzzerArgs("fuzzer-args", cl::Positional, 64 cl::desc("Options to pass to the fuzzer"), cl::ZeroOrMore, 65 cl::PositionalEatsArgs); 66 static std::vector<char *> ModifiedArgv; 67 68 enum OutputFileType { 69 OFT_Null, 70 OFT_AssemblyFile, 71 OFT_ObjectFile 72 }; 73 static cl::opt<OutputFileType> 74 FileType("filetype", cl::init(OFT_AssemblyFile), 75 cl::desc("Choose an output file type:"), 76 cl::values( 77 clEnumValN(OFT_AssemblyFile, "asm", 78 "Emit an assembly ('.s') file"), 79 clEnumValN(OFT_Null, "null", 80 "Don't emit anything (for timing purposes)"), 81 clEnumValN(OFT_ObjectFile, "obj", 82 "Emit a native object ('.o') file"))); 83 84 85 class LLVMFuzzerInputBuffer : public MemoryBuffer 86 { 87 public: 88 LLVMFuzzerInputBuffer(const uint8_t *data_, size_t size_) 89 : Data(reinterpret_cast<const char *>(data_)), 90 Size(size_) { 91 init(Data, Data+Size, false); 92 } 93 94 95 virtual BufferKind getBufferKind() const { 96 return MemoryBuffer_Malloc; // it's not disk-backed so I think that's 97 // the intent ... though AFAIK it 98 // probably came from an mmap or sbrk 99 } 100 101 private: 102 const char *Data; 103 size_t Size; 104 }; 105 106 static int AssembleInput(const char *ProgName, const Target *TheTarget, 107 SourceMgr &SrcMgr, MCContext &Ctx, MCStreamer &Str, 108 MCAsmInfo &MAI, MCSubtargetInfo &STI, 109 MCInstrInfo &MCII, MCTargetOptions &MCOptions) { 110 static const bool NoInitialTextSection = false; 111 112 std::unique_ptr<MCAsmParser> Parser( 113 createMCAsmParser(SrcMgr, Ctx, Str, MAI)); 114 115 std::unique_ptr<MCTargetAsmParser> TAP( 116 TheTarget->createMCAsmParser(STI, *Parser, MCII, MCOptions)); 117 118 if (!TAP) { 119 errs() << ProgName 120 << ": error: this target '" << TripleName 121 << "', does not support assembly parsing.\n"; 122 abort(); 123 } 124 125 Parser->setTargetParser(*TAP); 126 127 return Parser->Run(NoInitialTextSection); 128 } 129 130 131 int AssembleOneInput(const uint8_t *Data, size_t Size) { 132 const bool ShowInst = false; 133 const bool AsmVerbose = false; 134 const bool UseDwarfDirectory = true; 135 136 Triple TheTriple(Triple::normalize(TripleName)); 137 138 SourceMgr SrcMgr; 139 140 std::unique_ptr<MemoryBuffer> BufferPtr(new LLVMFuzzerInputBuffer(Data, Size)); 141 142 // Tell SrcMgr about this buffer, which is what the parser will pick up. 143 SrcMgr.AddNewSourceBuffer(std::move(BufferPtr), SMLoc()); 144 145 static const std::vector<std::string> NoIncludeDirs; 146 SrcMgr.setIncludeDirs(NoIncludeDirs); 147 148 static std::string ArchName; 149 std::string Error; 150 const Target *TheTarget = TargetRegistry::lookupTarget(ArchName, TheTriple, 151 Error); 152 if (!TheTarget) { 153 errs() << "error: this target '" << TheTriple.normalize() 154 << "/" << ArchName << "', was not found: '" << Error << "'\n"; 155 156 abort(); 157 } 158 159 std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); 160 if (!MRI) { 161 errs() << "Unable to create target register info!"; 162 abort(); 163 } 164 165 MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags(); 166 std::unique_ptr<MCAsmInfo> MAI( 167 TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); 168 if (!MAI) { 169 errs() << "Unable to create target asm info!"; 170 abort(); 171 } 172 173 std::unique_ptr<MCSubtargetInfo> STI( 174 TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); 175 176 MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); 177 std::unique_ptr<MCObjectFileInfo> MOFI( 178 TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false)); 179 Ctx.setObjectFileInfo(MOFI.get()); 180 181 const unsigned OutputAsmVariant = 0; 182 std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); 183 MCInstPrinter *IP = TheTarget->createMCInstPrinter(Triple(TripleName), OutputAsmVariant, 184 *MAI, *MCII, *MRI); 185 if (!IP) { 186 errs() 187 << "error: unable to create instruction printer for target triple '" 188 << TheTriple.normalize() << "' with assembly variant " 189 << OutputAsmVariant << ".\n"; 190 191 abort(); 192 } 193 194 const char *ProgName = "llvm-mc-fuzzer"; 195 std::unique_ptr<MCCodeEmitter> CE = nullptr; 196 std::unique_ptr<MCAsmBackend> MAB = nullptr; 197 198 std::string OutputString; 199 raw_string_ostream Out(OutputString); 200 auto FOut = std::make_unique<formatted_raw_ostream>(Out); 201 202 std::unique_ptr<MCStreamer> Str; 203 204 if (FileType == OFT_AssemblyFile) { 205 Str.reset(TheTarget->createAsmStreamer(Ctx, std::move(FOut), AsmVerbose, 206 UseDwarfDirectory, IP, std::move(CE), 207 std::move(MAB), ShowInst)); 208 } else { 209 assert(FileType == OFT_ObjectFile && "Invalid file type!"); 210 211 std::error_code EC; 212 const std::string OutputFilename = "-"; 213 auto Out = 214 std::make_unique<ToolOutputFile>(OutputFilename, EC, sys::fs::OF_None); 215 if (EC) { 216 errs() << EC.message() << '\n'; 217 abort(); 218 } 219 220 // Don't waste memory on names of temp labels. 221 Ctx.setUseNamesOnTempLabels(false); 222 223 std::unique_ptr<buffer_ostream> BOS; 224 raw_pwrite_stream *OS = &Out->os(); 225 if (!Out->os().supportsSeeking()) { 226 BOS = std::make_unique<buffer_ostream>(Out->os()); 227 OS = BOS.get(); 228 } 229 230 MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx); 231 MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions); 232 Str.reset(TheTarget->createMCObjectStreamer( 233 TheTriple, Ctx, std::unique_ptr<MCAsmBackend>(MAB), 234 MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(CE), *STI, 235 MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, 236 /*DWARFMustBeAtTheEnd*/ false)); 237 } 238 const int Res = AssembleInput(ProgName, TheTarget, SrcMgr, Ctx, *Str, *MAI, *STI, 239 *MCII, MCOptions); 240 241 (void) Res; 242 243 return 0; 244 } 245 246 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { 247 return AssembleOneInput(Data, Size); 248 } 249 250 extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize(int *argc, 251 char ***argv) { 252 // The command line is unusual compared to other fuzzers due to the need to 253 // specify the target. Options like -triple, -mcpu, and -mattr work like 254 // their counterparts in llvm-mc, while -fuzzer-args collects options for the 255 // fuzzer itself. 256 // 257 // Examples: 258 // 259 // Fuzz the big-endian MIPS32R6 disassembler using 100,000 inputs of up to 260 // 4-bytes each and use the contents of ./corpus as the test corpus: 261 // llvm-mc-fuzzer -triple mips-linux-gnu -mcpu=mips32r6 -disassemble \ 262 // -fuzzer-args -max_len=4 -runs=100000 ./corpus 263 // 264 // Infinitely fuzz the little-endian MIPS64R2 disassembler with the MSA 265 // feature enabled using up to 64-byte inputs: 266 // llvm-mc-fuzzer -triple mipsel-linux-gnu -mcpu=mips64r2 -mattr=msa \ 267 // -disassemble -fuzzer-args ./corpus 268 // 269 // If your aim is to find instructions that are not tested, then it is 270 // advisable to constrain the maximum input size to a single instruction 271 // using -max_len as in the first example. This results in a test corpus of 272 // individual instructions that test unique paths. Without this constraint, 273 // there will be considerable redundancy in the corpus. 274 275 char **OriginalArgv = *argv; 276 277 LLVMInitializeAllTargetInfos(); 278 LLVMInitializeAllTargetMCs(); 279 LLVMInitializeAllAsmParsers(); 280 281 cl::ParseCommandLineOptions(*argc, OriginalArgv); 282 283 // Rebuild the argv without the arguments llvm-mc-fuzzer consumed so that 284 // the driver can parse its arguments. 285 // 286 // FuzzerArgs cannot provide the non-const pointer that OriginalArgv needs. 287 // Re-use the strings from OriginalArgv instead of copying FuzzerArg to a 288 // non-const buffer to avoid the need to clean up when the fuzzer terminates. 289 ModifiedArgv.push_back(OriginalArgv[0]); 290 for (const auto &FuzzerArg : FuzzerArgs) { 291 for (int i = 1; i < *argc; ++i) { 292 if (FuzzerArg == OriginalArgv[i]) 293 ModifiedArgv.push_back(OriginalArgv[i]); 294 } 295 } 296 *argc = ModifiedArgv.size(); 297 *argv = ModifiedArgv.data(); 298 299 // Package up features to be passed to target/subtarget 300 // We have to pass it via a global since the callback doesn't 301 // permit any user data. 302 if (MAttrs.size()) { 303 SubtargetFeatures Features; 304 for (unsigned i = 0; i != MAttrs.size(); ++i) 305 Features.AddFeature(MAttrs[i]); 306 FeaturesStr = Features.getString(); 307 } 308 309 if (TripleName.empty()) 310 TripleName = sys::getDefaultTargetTriple(); 311 312 return 0; 313 } 314