[libFuzzer] communicate through pipe to subprocess for MinimizeCrashInput

For CleanseCrashInput, discards stdout output anyway since it is not used.

These changes are to defend against aggressive PID recycle on windows to reduce the chance of contention on files.

Using pipe instead of file also workaround the problem that when the
process is spawned by llvm-lit, the aborted process keeps a handle to the
output file such that the output file can not be removed. This will
cause random test failures.

https://devblogs.microsoft.com/oldnewthing/20110107-00/?p=11803

Reviewers: kcc, vitalybuka

Reviewed By: vitalybuka

Differential Revision: https://reviews.llvm.org/D73329

GitOrigin-RevId: 85515c7fd53c0cb77ccf46eaa5246ac61ae08ee8
diff --git a/FuzzerDriver.cpp b/FuzzerDriver.cpp
index 0063cde..0f50450 100644
--- a/FuzzerDriver.cpp
+++ b/FuzzerDriver.cpp
@@ -306,8 +306,7 @@
   return true;
 }
 
-static std::string GetDedupTokenFromFile(const std::string &Path) {
-  auto S = FileToString(Path);
+static std::string GetDedupTokenFromCmdOutput(const std::string &S) {
   auto Beg = S.find("DEDUP_TOKEN:");
   if (Beg == std::string::npos)
     return "";
@@ -317,6 +316,20 @@
   return S.substr(Beg, End - Beg);
 }
 
+// Return true on success, false otherwise.
+static bool ExecuteCommandWithPopen(const Command &Cmd, std::string *CmdOutput) {
+  FILE *Pipe = OpenProcessPipe(Cmd.toString().c_str(), "r");
+  if (!Pipe)
+    return false;
+
+  if (CmdOutput) {
+    char TmpBuffer[128];
+    while (fgets(TmpBuffer, sizeof(TmpBuffer), Pipe))
+      CmdOutput->append(TmpBuffer);
+  }
+  return CloseProcessPipe(Pipe) == 0;
+}
+
 int CleanseCrashInput(const Vector<std::string> &Args,
                        const FuzzingOptions &Options) {
   if (Inputs->size() != 1 || !Flags.exact_artifact_path) {
@@ -332,10 +345,9 @@
   assert(Cmd.hasArgument(InputFilePath));
   Cmd.removeArgument(InputFilePath);
 
-  auto LogFilePath = TempPath(".txt");
   auto TmpFilePath = TempPath(".repro");
   Cmd.addArgument(TmpFilePath);
-  Cmd.setOutputFile(LogFilePath);
+  Cmd.setOutputFile(getDevNull());
   Cmd.combineOutAndErr();
 
   std::string CurrentFilePath = InputFilePath;
@@ -370,7 +382,6 @@
     }
     if (!Changed) break;
   }
-  RemoveFile(LogFilePath);
   return 0;
 }
 
@@ -393,8 +404,6 @@
     BaseCmd.addFlag("max_total_time", "600");
   }
 
-  auto LogFilePath = TempPath(".txt");
-  BaseCmd.setOutputFile(LogFilePath);
   BaseCmd.combineOutAndErr();
 
   std::string CurrentFilePath = InputFilePath;
@@ -406,17 +415,17 @@
     Command Cmd(BaseCmd);
     Cmd.addArgument(CurrentFilePath);
 
-    std::string CommandLine = Cmd.toString();
-    Printf("CRASH_MIN: executing: %s\n", CommandLine.c_str());
-    int ExitCode = ExecuteCommand(Cmd);
-    if (ExitCode == 0) {
+    Printf("CRASH_MIN: executing: %s\n", Cmd.toString().c_str());
+    std::string CmdOutput;
+    bool Success = ExecuteCommandWithPopen(Cmd, &CmdOutput);
+    if (Success) {
       Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str());
       exit(1);
     }
     Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize "
            "it further\n",
            CurrentFilePath.c_str(), U.size());
-    auto DedupToken1 = GetDedupTokenFromFile(LogFilePath);
+    auto DedupToken1 = GetDedupTokenFromCmdOutput(CmdOutput);
     if (!DedupToken1.empty())
       Printf("CRASH_MIN: DedupToken1: %s\n", DedupToken1.c_str());
 
@@ -426,11 +435,11 @@
             : Options.ArtifactPrefix + "minimized-from-" + Hash(U);
     Cmd.addFlag("minimize_crash_internal_step", "1");
     Cmd.addFlag("exact_artifact_path", ArtifactPath);
-    CommandLine = Cmd.toString();
-    Printf("CRASH_MIN: executing: %s\n", CommandLine.c_str());
-    ExitCode = ExecuteCommand(Cmd);
-    CopyFileToErr(LogFilePath);
-    if (ExitCode == 0) {
+    Printf("CRASH_MIN: executing: %s\n", Cmd.toString().c_str());
+    CmdOutput.clear();
+    Success = ExecuteCommandWithPopen(Cmd, &CmdOutput);
+    Printf("%s", CmdOutput.c_str());
+    if (Success) {
       if (Flags.exact_artifact_path) {
         CurrentFilePath = Flags.exact_artifact_path;
         WriteToFile(U, CurrentFilePath);
@@ -439,7 +448,7 @@
              CurrentFilePath.c_str(), U.size());
       break;
     }
-    auto DedupToken2 = GetDedupTokenFromFile(LogFilePath);
+    auto DedupToken2 = GetDedupTokenFromCmdOutput(CmdOutput);
     if (!DedupToken2.empty())
       Printf("CRASH_MIN: DedupToken2: %s\n", DedupToken2.c_str());
 
@@ -456,7 +465,6 @@
     CurrentFilePath = ArtifactPath;
     Printf("*********************************\n");
   }
-  RemoveFile(LogFilePath);
   return 0;
 }
 
diff --git a/FuzzerUtil.h b/FuzzerUtil.h
index 00ea655..344f209 100644
--- a/FuzzerUtil.h
+++ b/FuzzerUtil.h
@@ -59,6 +59,7 @@
 int ExecuteCommand(const Command &Cmd);
 
 FILE *OpenProcessPipe(const char *Command, const char *Mode);
+int CloseProcessPipe(FILE *F);
 
 const void *SearchMemory(const void *haystack, size_t haystacklen,
                          const void *needle, size_t needlelen);
diff --git a/FuzzerUtilPosix.cpp b/FuzzerUtilPosix.cpp
index 8048e6a..a574119 100644
--- a/FuzzerUtilPosix.cpp
+++ b/FuzzerUtilPosix.cpp
@@ -149,6 +149,10 @@
   return popen(Command, Mode);
 }
 
+int CloseProcessPipe(FILE *F) {
+  return pclose(F);
+}
+
 const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
                          size_t PattLen) {
   return memmem(Data, DataLen, Patt, PattLen);
diff --git a/FuzzerUtilWindows.cpp b/FuzzerUtilWindows.cpp
index 527e7db..9d0d372 100644
--- a/FuzzerUtilWindows.cpp
+++ b/FuzzerUtilWindows.cpp
@@ -152,6 +152,10 @@
   return _popen(Command, Mode);
 }
 
+int CloseProcessPipe(FILE *F) {
+  return _pclose(F);
+}
+
 int ExecuteCommand(const Command &Cmd) {
   std::string CmdLine = Cmd.toString();
   return system(CmdLine.c_str());