[libFuzzer] add a flag close_fd_mask so that we can silence spammy targets by closing stderr/stdout

llvm-svn: 263831
diff --git a/llvm/lib/Fuzzer/FuzzerDriver.cpp b/llvm/lib/Fuzzer/FuzzerDriver.cpp
index c162adf..a31d306 100644
--- a/llvm/lib/Fuzzer/FuzzerDriver.cpp
+++ b/llvm/lib/Fuzzer/FuzzerDriver.cpp
@@ -260,6 +260,11 @@
     return 0;
   }
 
+  if (Flags.close_fd_mask & 2)
+    DupAndCloseStderr();
+  if (Flags.close_fd_mask & 1)
+    CloseStdout();
+
   if (Flags.jobs > 0 && Flags.workers == 0) {
     Flags.workers = std::min(NumberOfCpuCores() / 2, Flags.jobs);
     if (Flags.workers > 1)
diff --git a/llvm/lib/Fuzzer/FuzzerFlags.def b/llvm/lib/Fuzzer/FuzzerFlags.def
index 33389d2..20ffa32 100644
--- a/llvm/lib/Fuzzer/FuzzerFlags.def
+++ b/llvm/lib/Fuzzer/FuzzerFlags.def
@@ -75,6 +75,8 @@
 FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.")
 FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.")
 FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.")
+FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; "
+                                       "if 2, close stderr; if 3, close both.")
 
 FUZZER_DEPRECATED_FLAG(exit_on_first)
 FUZZER_DEPRECATED_FLAG(save_minimized_corpus)
diff --git a/llvm/lib/Fuzzer/FuzzerIO.cpp b/llvm/lib/Fuzzer/FuzzerIO.cpp
index 1e14cfc..358e645 100644
--- a/llvm/lib/Fuzzer/FuzzerIO.cpp
+++ b/llvm/lib/Fuzzer/FuzzerIO.cpp
@@ -20,6 +20,8 @@
 
 namespace fuzzer {
 
+static int OutputFd = 2;
+
 bool IsFile(const std::string &Path) {
   struct stat St;
   if (stat(Path.c_str(), &St))
@@ -114,11 +116,25 @@
   return DirPath + "/" + FileName;
 }
 
+void DupAndCloseStderr() {
+  assert(OutputFd == 2);
+  OutputFd = dup(OutputFd);
+  if (OutputFd < 0)
+    OutputFd = 2;
+  else
+    close(2);
+}
+
+void CloseStdout() { close(1); }
+
 void Printf(const char *Fmt, ...) {
+  char Buf[1024];
   va_list ap;
   va_start(ap, Fmt);
-  vfprintf(stderr, Fmt, ap);
+  int Formatted = vsnprintf(Buf, sizeof(Buf), Fmt, ap);
   va_end(ap);
+  if (Formatted)
+    write(OutputFd, Buf, Formatted);
 }
 
 }  // namespace fuzzer
diff --git a/llvm/lib/Fuzzer/FuzzerInternal.h b/llvm/lib/Fuzzer/FuzzerInternal.h
index ebed95c..2ff1319 100644
--- a/llvm/lib/Fuzzer/FuzzerInternal.h
+++ b/llvm/lib/Fuzzer/FuzzerInternal.h
@@ -75,6 +75,8 @@
 std::string DirPlusFile(const std::string &DirPath,
                         const std::string &FileName);
 
+void DupAndCloseStderr();
+void CloseStdout();
 void Printf(const char *Fmt, ...);
 void PrintHexArray(const Unit &U, const char *PrintAfter = "");
 void PrintHexArray(const uint8_t *Data, size_t Size,
diff --git a/llvm/lib/Fuzzer/test/CMakeLists.txt b/llvm/lib/Fuzzer/test/CMakeLists.txt
index 83dbbbc..255be9c 100644
--- a/llvm/lib/Fuzzer/test/CMakeLists.txt
+++ b/llvm/lib/Fuzzer/test/CMakeLists.txt
@@ -30,6 +30,7 @@
   SimpleFnAdapterTest
   SimpleHashTest
   SimpleTest
+  SpamyTest
   StrcmpTest
   StrncmpTest
   SwitchTest
diff --git a/llvm/lib/Fuzzer/test/SpamyTest.cpp b/llvm/lib/Fuzzer/test/SpamyTest.cpp
new file mode 100644
index 0000000..63776d6
--- /dev/null
+++ b/llvm/lib/Fuzzer/test/SpamyTest.cpp
@@ -0,0 +1,18 @@
+// The test spams to stderr and stdout.
+#include <assert.h>
+#include <cstdint>
+#include <cstdio>
+#include <cstddef>
+#include <iostream>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+  assert(Data);
+  printf("PRINTF_STDOUT\n");
+  fflush(stdout);
+  fprintf(stderr, "PRINTF_STDERR\n");
+  std::cout << "STREAM_COUT\n";
+  std::cout.flush();
+  std::cerr << "STREAM_CERR\n";
+  return 0;
+}
+
diff --git a/llvm/lib/Fuzzer/test/fuzzer.test b/llvm/lib/Fuzzer/test/fuzzer.test
index 3ab7673..240209d 100644
--- a/llvm/lib/Fuzzer/test/fuzzer.test
+++ b/llvm/lib/Fuzzer/test/fuzzer.test
@@ -91,3 +91,32 @@
 FINAL_STATS: stat::slowest_unit_time_sec:    0
 FINAL_STATS: stat::peak_rss_mb:
 
+RUN: LLVMFuzzer-SpamyTest -runs=1                  2>&1 | FileCheck %s --check-prefix=FD_MASK_0
+RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=0 2>&1 | FileCheck %s --check-prefix=FD_MASK_0
+RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=1 2>&1 | FileCheck %s --check-prefix=FD_MASK_1
+RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=2 2>&1 | FileCheck %s --check-prefix=FD_MASK_2
+RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=3 2>&1 | FileCheck %s --check-prefix=FD_MASK_3
+FD_MASK_0: PRINTF_STDOUT
+FD_MASK_0: PRINTF_STDERR
+FD_MASK_0: STREAM_COUT
+FD_MASK_0: STREAM_CERR
+FD_MASK_0: INITED
+
+FD_MASK_1-NOT: PRINTF_STDOUT
+FD_MASK_1: PRINTF_STDERR
+FD_MASK_1-NOT: STREAM_COUT
+FD_MASK_1: STREAM_CERR
+FD_MASK_1: INITED
+
+FD_MASK_2: PRINTF_STDOUT
+FD_MASK_2-NOT: PRINTF_STDERR
+FD_MASK_2: STREAM_COUT
+FD_MASK_2-NOTE: STREAM_CERR
+FD_MASK_2: INITED
+
+FD_MASK_3-NOT: PRINTF_STDOUT
+FD_MASK_3-NOT: PRINTF_STDERR
+FD_MASK_3-NOT: STREAM_COUT
+FD_MASK_3-NOT: STREAM_CERR
+FD_MASK_3: INITED
+