1 //===--- llvm-mc-fuzzer.cpp - Fuzzer for the MC layer ---------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 //===----------------------------------------------------------------------===// 11 12 #include "llvm-c/Disassembler.h" 13 #include "llvm-c/Target.h" 14 #include "llvm/ADT/ArrayRef.h" 15 #include "llvm/MC/SubtargetFeature.h" 16 #include "llvm/Support/CommandLine.h" 17 #include "llvm/Support/raw_ostream.h" 18 #include "FuzzerInterface.h" 19 20 using namespace llvm; 21 22 const unsigned AssemblyTextBufSize = 80; 23 24 enum ActionType { 25 AC_Assemble, 26 AC_Disassemble 27 }; 28 29 static cl::opt<ActionType> 30 Action(cl::desc("Action to perform:"), 31 cl::init(AC_Assemble), 32 cl::values(clEnumValN(AC_Assemble, "assemble", 33 "Assemble a .s file (default)"), 34 clEnumValN(AC_Disassemble, "disassemble", 35 "Disassemble strings of hex bytes"), 36 clEnumValEnd)); 37 38 static cl::opt<std::string> 39 TripleName("triple", cl::desc("Target triple to assemble for, " 40 "see -version for available targets")); 41 42 static cl::opt<std::string> 43 MCPU("mcpu", 44 cl::desc("Target a specific cpu type (-mcpu=help for details)"), 45 cl::value_desc("cpu-name"), cl::init("")); 46 47 // This is useful for variable-length instruction sets. 48 static cl::opt<unsigned> InsnLimit( 49 "insn-limit", 50 cl::desc("Limit the number of instructions to process (0 for no limit)"), 51 cl::value_desc("count"), cl::init(0)); 52 53 static cl::list<std::string> 54 MAttrs("mattr", cl::CommaSeparated, 55 cl::desc("Target specific attributes (-mattr=help for details)"), 56 cl::value_desc("a1,+a2,-a3,...")); 57 // The feature string derived from -mattr's values. 58 std::string FeaturesStr; 59 60 static cl::list<std::string> 61 FuzzerArgv("fuzzer-args", cl::Positional, 62 cl::desc("Options to pass to the fuzzer"), cl::ZeroOrMore, 63 cl::PositionalEatsArgs); 64 65 int DisassembleOneInput(const uint8_t *Data, size_t Size) { 66 char AssemblyText[AssemblyTextBufSize]; 67 68 std::vector<uint8_t> DataCopy(Data, Data + Size); 69 70 LLVMDisasmContextRef Ctx = LLVMCreateDisasmCPUFeatures( 71 TripleName.c_str(), MCPU.c_str(), FeaturesStr.c_str(), nullptr, 0, 72 nullptr, nullptr); 73 assert(Ctx); 74 uint8_t *p = DataCopy.data(); 75 unsigned Consumed; 76 unsigned InstructionsProcessed = 0; 77 do { 78 Consumed = LLVMDisasmInstruction(Ctx, p, Size, 0, AssemblyText, 79 AssemblyTextBufSize); 80 Size -= Consumed; 81 p += Consumed; 82 83 InstructionsProcessed ++; 84 if (InsnLimit != 0 && InstructionsProcessed < InsnLimit) 85 break; 86 } while (Consumed != 0); 87 LLVMDisasmDispose(Ctx); 88 return 0; 89 } 90 91 int main(int argc, char **argv) { 92 // The command line is unusual compared to other fuzzers due to the need to 93 // specify the target. Options like -triple, -mcpu, and -mattr work like 94 // their counterparts in llvm-mc, while -fuzzer-args collects options for the 95 // fuzzer itself. 96 // 97 // Examples: 98 // 99 // Fuzz the big-endian MIPS32R6 disassembler using 100,000 inputs of up to 100 // 4-bytes each and use the contents of ./corpus as the test corpus: 101 // llvm-mc-fuzzer -triple mips-linux-gnu -mcpu=mips32r6 -disassemble \ 102 // -fuzzer-args -max_len=4 -runs=100000 ./corpus 103 // 104 // Infinitely fuzz the little-endian MIPS64R2 disassembler with the MSA 105 // feature enabled using up to 64-byte inputs: 106 // llvm-mc-fuzzer -triple mipsel-linux-gnu -mcpu=mips64r2 -mattr=msa \ 107 // -disassemble -fuzzer-args ./corpus 108 // 109 // If your aim is to find instructions that are not tested, then it is 110 // advisable to constrain the maximum input size to a single instruction 111 // using -max_len as in the first example. This results in a test corpus of 112 // individual instructions that test unique paths. Without this constraint, 113 // there will be considerable redundancy in the corpus. 114 115 LLVMInitializeAllTargetInfos(); 116 LLVMInitializeAllTargetMCs(); 117 LLVMInitializeAllDisassemblers(); 118 119 cl::ParseCommandLineOptions(argc, argv); 120 121 // Package up features to be passed to target/subtarget 122 // We have to pass it via a global since the callback doesn't 123 // permit any user data. 124 if (MAttrs.size()) { 125 SubtargetFeatures Features; 126 for (unsigned i = 0; i != MAttrs.size(); ++i) 127 Features.AddFeature(MAttrs[i]); 128 FeaturesStr = Features.getString(); 129 } 130 131 if (Action == AC_Assemble) 132 errs() << "error: -assemble is not implemented\n"; 133 else if (Action == AC_Disassemble) 134 return fuzzer::FuzzerDriver(argc, argv, DisassembleOneInput); 135 136 llvm_unreachable("Unknown action"); 137 return 1; 138 } 139