Make system bazelrc configurable

The master bazelrc is now a member of OptionProcessor and is
configurable by the constructor. This fixes a heap-use-after-free,
introduced in bccf9c63ff0863e3ffb4fb24085a37a03db10aaa. Fixes #6054.

TESTED: bazel test //src/test/cpp:rc_file_test \
            --copt="-fsanitize=address" \
            --linkopt="-fsanitize=address"

Closes #6055.

PiperOrigin-RevId: 213060526
diff --git a/src/main/cpp/option_processor.cc b/src/main/cpp/option_processor.cc
index d3ff63f..f0557ea 100644
--- a/src/main/cpp/option_processor.cc
+++ b/src/main/cpp/option_processor.cc
@@ -49,16 +49,20 @@
 static constexpr const char* kRcBasename = ".bazelrc";
 static std::vector<std::string> GetProcessedEnv();
 
-// Path to the system-wide bazelrc configuration file.
-// This is a mutable global for testing purposes only.
-const char* system_bazelrc_path = BAZEL_SYSTEM_BAZELRC_PATH;
-
 OptionProcessor::OptionProcessor(
     const WorkspaceLayout* workspace_layout,
     std::unique_ptr<StartupOptions> default_startup_options)
     : workspace_layout_(workspace_layout),
-      parsed_startup_options_(std::move(default_startup_options)) {
-}
+      parsed_startup_options_(std::move(default_startup_options)),
+      system_bazelrc_path_(BAZEL_SYSTEM_BAZELRC_PATH) {}
+
+OptionProcessor::OptionProcessor(
+    const WorkspaceLayout* workspace_layout,
+    std::unique_ptr<StartupOptions> default_startup_options,
+    const std::string& system_bazelrc_path)
+    : workspace_layout_(workspace_layout),
+      parsed_startup_options_(std::move(default_startup_options)),
+      system_bazelrc_path_(system_bazelrc_path) {}
 
 std::unique_ptr<CommandLine> OptionProcessor::SplitCommandLine(
     const vector<string>& args, string* error) const {
@@ -169,7 +173,8 @@
 std::set<std::string> GetOldRcPaths(
     const WorkspaceLayout* workspace_layout, const std::string& workspace,
     const std::string& cwd, const std::string& path_to_binary,
-    const std::vector<std::string>& startup_args) {
+    const std::vector<std::string>& startup_args,
+    const std::string& system_bazelrc_path) {
   // Find the old list of rc files that would have been loaded here, so we can
   // provide a useful warning about old rc files that might no longer be read.
   std::vector<std::string> candidate_bazelrc_paths;
@@ -178,8 +183,7 @@
         workspace_layout->GetWorkspaceRcPath(workspace, startup_args);
     const std::string binary_rc =
         internal::FindRcAlongsideBinary(cwd, path_to_binary);
-    const std::string system_rc = internal::FindSystemWideRc();
-    candidate_bazelrc_paths = {workspace_rc, binary_rc, system_rc};
+    candidate_bazelrc_paths = {workspace_rc, binary_rc, system_bazelrc_path};
   }
   const std::vector<std::string> deduped_blazerc_paths =
       internal::DedupeBlazercPaths(candidate_bazelrc_paths);
@@ -210,7 +214,7 @@
   return result;
 }
 
-std::string FindSystemWideRc() {
+std::string FindSystemWideRc(const std::string& system_bazelrc_path) {
   const std::string path =
       blaze_util::MakeAbsoluteAndResolveWindowsEnvvars(system_bazelrc_path);
   if (blaze_util::CanReadFile(path)) {
@@ -307,7 +311,7 @@
     // provided path. This also means we accept relative paths, which is
     // is convenient for testing.
     const std::string system_rc =
-        blaze_util::MakeAbsoluteAndResolveWindowsEnvvars(system_bazelrc_path);
+        blaze_util::MakeAbsoluteAndResolveWindowsEnvvars(system_bazelrc_path_);
     rc_files.push_back(system_rc);
   }
 
@@ -389,9 +393,9 @@
   // TODO(b/36168162): Remove this warning along with
   // internal::GetOldRcPaths and internal::FindLegacyUserBazelrc after
   // the transition period has passed.
-  const std::set<std::string> old_files =
-      internal::GetOldRcPaths(workspace_layout, workspace, cwd,
-                              cmd_line->path_to_binary, cmd_line->startup_args);
+  const std::set<std::string> old_files = internal::GetOldRcPaths(
+      workspace_layout, workspace, cwd, cmd_line->path_to_binary,
+      cmd_line->startup_args, internal::FindSystemWideRc(system_bazelrc_path_));
 
   //   std::vector<std::string> old_files = internal::GetOldRcPathsInOrder(
   //       workspace_layout, workspace, cwd, cmd_line->path_to_binary,
diff --git a/src/main/cpp/option_processor.h b/src/main/cpp/option_processor.h
index f1e3970..08a05dc 100644
--- a/src/main/cpp/option_processor.h
+++ b/src/main/cpp/option_processor.h
@@ -56,6 +56,10 @@
   OptionProcessor(const WorkspaceLayout* workspace_layout,
                   std::unique_ptr<StartupOptions> default_startup_options);
 
+  OptionProcessor(const WorkspaceLayout* workspace_layout,
+                  std::unique_ptr<StartupOptions> default_startup_options,
+                  const std::string& system_bazelrc_path);
+
   virtual ~OptionProcessor() {}
 
   // Splits the arguments of a command line invocation.
@@ -144,6 +148,10 @@
   // The startup options parsed from args, this field is initialized by
   // ParseOptions.
   std::unique_ptr<StartupOptions> parsed_startup_options_;
+
+  // Path to the system-wide bazelrc configuration file.
+  // This is configurable for testing purposes only.
+  const std::string system_bazelrc_path_;
 };
 
 // Parses and returns the contents of the rc file.
diff --git a/src/test/cpp/rc_file_test.cc b/src/test/cpp/rc_file_test.cc
index 816535d..711e7b6 100644
--- a/src/test/cpp/rc_file_test.cc
+++ b/src/test/cpp/rc_file_test.cc
@@ -38,8 +38,6 @@
 constexpr const char* kNullDevice = "/dev/null";
 #endif
 
-extern const char* system_bazelrc_path;
-
 class RcFileTest : public ::testing::Test {
  protected:
   RcFileTest()
@@ -49,20 +47,9 @@
         binary_dir_(
             blaze_util::JoinPath(blaze::GetEnv("TEST_TMPDIR"), "bazeldir")),
         binary_path_(blaze_util::JoinPath(binary_dir_, "bazel")),
-        workspace_layout_(new WorkspaceLayout()),
-        old_system_bazelrc_path_(system_bazelrc_path) {}
+        workspace_layout_(new WorkspaceLayout()) {}
 
   void SetUp() override {
-    // We modify the global system_bazelrc_path to be a relative path.
-    // This test allows us to verify that the global bazelrc is read correctly,
-    // in the right order relative to the other files.
-    //
-    // However, this does not test the default path of this file, nor does it
-    // test that absolute paths are accepted properly. This is an unfortunate
-    // limitation of our testing - within the sandboxed environment of a test,
-    // we cannot place a file in arbitrary locations.
-    system_bazelrc_path = "bazel.bazelrc";
-
     ASSERT_TRUE(blaze_util::MakeDirectories(workspace_, 0755));
     ASSERT_TRUE(blaze_util::MakeDirectories(cwd_, 0755));
     ASSERT_TRUE(blaze_util::ChangeDirectory(cwd_));
@@ -81,7 +68,8 @@
     option_processor_.reset(new OptionProcessor(
         workspace_layout_.get(),
         std::unique_ptr<StartupOptions>(
-            new BazelStartupOptions(workspace_layout_.get()))));
+            new BazelStartupOptions(workspace_layout_.get())),
+        "bazel.bazelrc"));
   }
 
   void TearDown() override {
@@ -102,7 +90,6 @@
     for (const std::string& file : files) {
       blaze_util::UnlinkPath(file);
     }
-    system_bazelrc_path = old_system_bazelrc_path_.c_str();
   }
 
   bool SetUpSystemRcFile(const std::string& contents,