Bläddra i källkod

Teach fuzzers to initialize their installs from runfiles. (#4030)

This also allows us to default construct installs in an error state,
which is useful for cases like fuzzers where we want to default
construct a global, but then re-initilaize it That said, happy to
consider alternative designs here.
Chandler Carruth 1 år sedan
förälder
incheckning
268dd04511

+ 5 - 0
testing/fuzzing/libfuzzer.h

@@ -17,6 +17,11 @@ namespace Carbon::Testing {
 extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data,
                                       std::size_t size);
 
+// Optional API that can be implemented but isn't required. This allows fuzzers
+// to observe the `argv` during initialization.
+// NOLINTNEXTLINE: Match the documented fuzzer entry point declaration style.
+extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv);
+
 }  // namespace Carbon::Testing
 
 #endif  // CARBON_TESTING_FUZZING_LIBFUZZER_H_

+ 1 - 0
toolchain/check/BUILD

@@ -152,6 +152,7 @@ cc_fuzz_test(
     srcs = ["check_fuzzer.cpp"],
     corpus = glob(["fuzzer_corpus/*"]),
     deps = [
+        "//common:exe_path",
         "//testing/fuzzing:libfuzzer_header",
         "//toolchain/driver",
         "@llvm-project//llvm:Support",

+ 12 - 3
toolchain/check/check_fuzzer.cpp

@@ -2,6 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
+#include "common/exe_path.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/VirtualFileSystem.h"
@@ -10,6 +11,16 @@
 
 namespace Carbon::Testing {
 
+static const InstallPaths* install_paths = nullptr;
+
+// NOLINTNEXTLINE(readability-non-const-parameter): External API required types.
+extern "C" auto LLVMFuzzerInitialize(int* argc, char*** argv) -> int {
+  CARBON_CHECK(*argc >= 1) << "Need the `argv[0]` value to initialize!";
+  install_paths = new InstallPaths(
+      InstallPaths::MakeForBazelRunfiles(FindExecutablePath((*argv)[0])));
+  return 0;
+}
+
 // NOLINTNEXTLINE: Match the documented fuzzer entry point declaration style.
 extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data,
                                       std::size_t size) {
@@ -27,10 +38,8 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data,
       llvm::MemoryBuffer::getMemBuffer(data_ref, /*BufferName=*/TestFileName,
                                        /*RequiresNullTerminator=*/false)));
 
-  // TODO: We should try to thread the executable path into here.
-  const auto install_paths = InstallPaths::Make("");
   llvm::raw_null_ostream null_ostream;
-  Driver driver(fs, &install_paths, null_ostream, null_ostream);
+  Driver driver(fs, install_paths, null_ostream, null_ostream);
 
   // TODO: Get checking to a point where it can handle invalid parse trees
   // without crashing.

+ 1 - 0
toolchain/driver/BUILD

@@ -109,6 +109,7 @@ cc_fuzz_test(
     corpus = glob(["fuzzer_corpus/*"]),
     deps = [
         ":driver",
+        "//common:exe_path",
         "//testing/base:test_raw_ostream",
         "//testing/fuzzing:libfuzzer_header",
         "//toolchain/install:install_paths",

+ 12 - 3
toolchain/driver/driver_fuzzer.cpp

@@ -5,6 +5,7 @@
 #include <cstring>
 #include <string>
 
+#include "common/exe_path.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/raw_ostream.h"
@@ -15,6 +16,16 @@
 
 namespace Carbon::Testing {
 
+static const InstallPaths* install_paths = nullptr;
+
+// NOLINTNEXTLINE(readability-non-const-parameter): External API required types.
+extern "C" auto LLVMFuzzerInitialize(int* argc, char*** argv) -> int {
+  CARBON_CHECK(*argc >= 1) << "Need the `argv[0]` value to initialize!";
+  install_paths = new InstallPaths(
+      InstallPaths::MakeForBazelRunfiles(FindExecutablePath((*argv)[0])));
+  return 0;
+}
+
 static auto Read(const unsigned char*& data, size_t& size, int& output)
     -> bool {
   if (size < sizeof(output)) {
@@ -68,11 +79,9 @@ extern "C" auto LLVMFuzzerTestOneInput(const unsigned char* data, size_t size)
   }
 
   llvm::vfs::InMemoryFileSystem fs;
-  // TODO: We should try to thread the executable path into here.
-  const auto install_paths = InstallPaths::Make("");
   TestRawOstream error_stream;
   llvm::raw_null_ostream dest;
-  Driver d(fs, &install_paths, dest, error_stream);
+  Driver d(fs, install_paths, dest, error_stream);
   if (!d.RunCommand(args).success) {
     if (error_stream.TakeStr().find("ERROR:") == std::string::npos) {
       llvm::errs() << "No error message on a failure!\n";

+ 1 - 1
toolchain/install/install_paths.h

@@ -105,7 +105,7 @@ class InstallPaths {
   auto llvm_install_bin() const -> std::string;
 
  private:
-  InstallPaths() : error_("No prefix provided!") {}
+  InstallPaths() { SetError("No prefix provided!"); }
   explicit InstallPaths(llvm::StringRef prefix) : prefix_(prefix) {}
 
   // Set an error message on the install paths and reset the prefix to empty,