driver_fuzzer.cpp 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include <cstring>
  5. #include <string>
  6. #include "common/exe_path.h"
  7. #include "common/raw_string_ostream.h"
  8. #include "llvm/ADT/SmallVector.h"
  9. #include "llvm/ADT/StringRef.h"
  10. #include "llvm/Support/raw_ostream.h"
  11. #include "testing/fuzzing/libfuzzer.h"
  12. #include "toolchain/driver/driver.h"
  13. #include "toolchain/install/install_paths.h"
  14. namespace Carbon::Testing {
  15. static const InstallPaths* install_paths = nullptr;
  16. // NOLINTNEXTLINE(readability-non-const-parameter): External API required types.
  17. extern "C" auto LLVMFuzzerInitialize(int* argc, char*** argv) -> int {
  18. CARBON_CHECK(*argc >= 1, "Need the `argv[0]` value to initialize!");
  19. install_paths = new InstallPaths(
  20. InstallPaths::MakeForBazelRunfiles(FindExecutablePath((*argv)[0])));
  21. return 0;
  22. }
  23. static auto Read(const unsigned char*& data, size_t& size, int& output)
  24. -> bool {
  25. if (size < sizeof(output)) {
  26. return false;
  27. }
  28. std::memcpy(&output, data, sizeof(output));
  29. size -= sizeof(output);
  30. data += sizeof(output);
  31. return true;
  32. }
  33. extern "C" auto LLVMFuzzerTestOneInput(const unsigned char* data, size_t size)
  34. -> int {
  35. // First use the data to compute the number of arguments. Note that for
  36. // scaling reasons we don't allow 2^31 arguments, even empty ones. Simply
  37. // creating the vector of those won't work. We limit this to 2^20 arguments
  38. // total.
  39. int num_args;
  40. if (!Read(data, size, num_args) || num_args < 0 || num_args > (1 << 20)) {
  41. return 0;
  42. }
  43. // Now use the data to compute the length of each argument. We don't want to
  44. // exhaust all memory, so bound the search space to using 2^17 bytes of
  45. // memory for the argument text itself.
  46. size_t arg_length_sum = 0;
  47. llvm::SmallVector<int> arg_lengths(num_args);
  48. for (int& arg_length : arg_lengths) {
  49. if (!Read(data, size, arg_length) || arg_length < 0) {
  50. return 0;
  51. }
  52. arg_length_sum += arg_length;
  53. if (arg_length_sum > (1 << 17)) {
  54. return 0;
  55. }
  56. }
  57. // Ensure we have enough data for all the arguments.
  58. if (size < arg_length_sum) {
  59. return 0;
  60. }
  61. // Lastly, read the contents of each argument out of the data.
  62. llvm::SmallVector<llvm::StringRef> args;
  63. args.reserve(num_args);
  64. for (int arg_length : arg_lengths) {
  65. args.push_back(
  66. llvm::StringRef(reinterpret_cast<const char*>(data), arg_length));
  67. data += arg_length;
  68. size -= arg_length;
  69. }
  70. llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> fs =
  71. new llvm::vfs::InMemoryFileSystem;
  72. RawStringOstream error_stream;
  73. llvm::raw_null_ostream null_ostream;
  74. Driver driver(fs, install_paths, /*input_stream=*/nullptr, &null_ostream,
  75. &error_stream, /*fuzzing=*/true);
  76. if (!driver.RunCommand(args).success) {
  77. auto str = error_stream.TakeStr();
  78. if (llvm::StringRef(str).find("error:") == llvm::StringRef::npos) {
  79. llvm::errs() << "No error message on a failure!\n";
  80. return 1;
  81. }
  82. }
  83. return 0;
  84. }
  85. } // namespace Carbon::Testing