llvm-mc-assemble-fuzzer.cpp 11 KB

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