[libFuzzer][Windows] Port fork mode to Windows
authorJonathan Metzman <metzman@chromium.org>
Wed, 27 Feb 2019 19:27:16 +0000 (19:27 +0000)
committerJonathan Metzman <metzman@chromium.org>
Wed, 27 Feb 2019 19:27:16 +0000 (19:27 +0000)
Summary:
Port libFuzzer's fork mode to Windows.
Implement Windows versions of MkDir, RmDir, and IterateDirRecursive to do this.
Don't print error messages under new normal uses of FileSize (on a non-existent file).
Implement portable way of piping output to /dev/null.
Fix test for Windows and comment fork-sigusr.test on why it won't be ported to Win.

Reviewers: zturner

Reviewed By: zturner

Subscribers: kcc, zturner, jdoerfert, #sanitizers, llvm-commits

Tags: #sanitizers, #llvm

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

llvm-svn: 355019

compiler-rt/lib/fuzzer/FuzzerFork.cpp
compiler-rt/lib/fuzzer/FuzzerIO.h
compiler-rt/lib/fuzzer/FuzzerIOPosix.cpp
compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp
compiler-rt/lib/fuzzer/FuzzerMerge.cpp
compiler-rt/test/fuzzer/fork-sigusr.test
compiler-rt/test/fuzzer/fork.test

index 1f7fe9b..52a233f 100644 (file)
@@ -304,13 +304,13 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
   }
   Stop = true;
 
-  // The workers have already finished doing useful work, or
-  // we were interrupted. Either way, cleanup up now.
-  RmDirRecursive(Env.TempDir);
-
   for (auto &T : Threads)
     T.join();
 
+  // The workers have terminated. Don't try to remove the directory before they
+  // terminate to avoid a race condition preventing cleanup on Windows.
+  RmDirRecursive(Env.TempDir);
+
   // Use the exit code from the last child process.
   Printf("INFO: exiting: %d time: %zds\n", ExitCode,
          Env.secondsSinceProcessStartUp());
@@ -318,4 +318,3 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
 }
 
 } // namespace fuzzer
-
index e51b8d1..9d4e765 100644 (file)
@@ -97,6 +97,8 @@ intptr_t GetHandleFromFd(int fd);
 void MkDir(const std::string &Path);
 void RmDir(const std::string &Path);
 
+const std::string &getDevNull();
+
 }  // namespace fuzzer
 
 #endif  // LLVM_FUZZER_IO_H
index 92d19d3..953848a 100644 (file)
@@ -170,6 +170,11 @@ void RmDir(const std::string &Path) {
   rmdir(Path.c_str());
 }
 
+const std::string &getDevNull() {
+  static const std::string devNull = "/dev/null";
+  return devNull;
+}
+
 }  // namespace fuzzer
 
 #endif // LIBFUZZER_POSIX
index e49e8a4..b8c200b 100644 (file)
@@ -71,6 +71,11 @@ bool IsFile(const std::string &Path) {
   return IsFile(Path, Att);
 }
 
+static bool IsDir(DWORD FileAttrs) {
+  if (FileAttrs == INVALID_FILE_ATTRIBUTES) return false;
+  return FileAttrs & FILE_ATTRIBUTE_DIRECTORY;
+}
+
 std::string Basename(const std::string &Path) {
   size_t Pos = Path.find_last_of("/\\");
   if (Pos == std::string::npos) return Path;
@@ -81,8 +86,10 @@ std::string Basename(const std::string &Path) {
 size_t FileSize(const std::string &Path) {
   WIN32_FILE_ATTRIBUTE_DATA attr;
   if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) {
-    Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n",
-           Path.c_str(), GetLastError());
+    DWORD LastError = GetLastError();
+    if (LastError != ERROR_FILE_NOT_FOUND)
+      Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n",
+             Path.c_str(), LastError);
     return 0;
   }
   ULARGE_INTEGER size;
@@ -145,8 +152,51 @@ void IterateDirRecursive(const std::string &Dir,
                          void (*DirPreCallback)(const std::string &Dir),
                          void (*DirPostCallback)(const std::string &Dir),
                          void (*FileCallback)(const std::string &Dir)) {
-  // Unimplemented.
-  // TODO: implement, and then implement ListFilesInDirRecursive via this one.
+  // TODO(metzman): Implement ListFilesInDirRecursive via this function.
+  DirPreCallback(Dir);
+
+  DWORD DirAttrs = GetFileAttributesA(Dir.c_str());
+  if (!IsDir(DirAttrs)) return;
+
+  std::string TargetDir(Dir);
+  assert(!TargetDir.empty());
+  if (TargetDir.back() != '\\') TargetDir.push_back('\\');
+  TargetDir.push_back('*');
+
+  WIN32_FIND_DATAA FindInfo;
+  // Find the directory's first file.
+  HANDLE FindHandle = FindFirstFileA(TargetDir.c_str(), &FindInfo);
+  if (FindHandle == INVALID_HANDLE_VALUE) {
+    DWORD LastError = GetLastError();
+    if (LastError != ERROR_FILE_NOT_FOUND) {
+      // If the directory isn't empty, then something abnormal is going on.
+      Printf("FindFirstFileA failed for %s (Error code: %lu).\n", Dir.c_str(),
+             LastError);
+    }
+    return;
+  }
+
+  do {
+    std::string Path = DirPlusFile(Dir, FindInfo.cFileName);
+    DWORD PathAttrs = FindInfo.dwFileAttributes;
+    if (IsDir(PathAttrs)) {
+      // Is Path the current directory (".") or the parent ("..")?
+      if (strcmp(FindInfo.cFileName, ".") == 0 ||
+          strcmp(FindInfo.cFileName, "..") == 0)
+        continue;
+      IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback);
+    } else if (PathAttrs != INVALID_FILE_ATTRIBUTES) {
+      FileCallback(Path);
+    }
+  } while (FindNextFileA(FindHandle, &FindInfo));
+
+  DWORD LastError = GetLastError();
+  if (LastError != ERROR_NO_MORE_FILES)
+    Printf("FindNextFileA failed for %s (Error code: %lu).\n", Dir.c_str(),
+           LastError);
+
+  FindClose(FindHandle);
+  DirPostCallback(Dir);
 }
 
 char GetSeparator() {
@@ -346,11 +396,20 @@ void RawPrint(const char *Str) {
 }
 
 void MkDir(const std::string &Path) {
-  Printf("MkDir: unimplemented\n");
+  if (CreateDirectoryA(Path.c_str(), nullptr)) return;
+  Printf("CreateDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(),
+         GetLastError());
 }
 
 void RmDir(const std::string &Path) {
-  Printf("RmDir: unimplemented\n");
+  if (RemoveDirectoryA(Path.c_str())) return;
+  Printf("RemoveDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(),
+         GetLastError());
+}
+
+const std::string &getDevNull() {
+  static const std::string devNull = "NUL";
+  return devNull;
 }
 
 }  // namespace fuzzer
index d70aa3e..dace45e 100644 (file)
@@ -331,7 +331,7 @@ void CrashResistantMerge(const Vector<std::string> &Args,
     Cmd.addFlag("merge_control_file", CFPath);
     Cmd.addFlag("merge_inner", "1");
     if (!V) {
-      Cmd.setOutputFile("/dev/null");  // TODO: need to handle this on Windows?
+      Cmd.setOutputFile(getDevNull());
       Cmd.combineOutAndErr();
     }
     auto ExitCode = ExecuteCommand(Cmd);
index f00a8b3..ea2e56c 100644 (file)
@@ -1,6 +1,5 @@
 # Check that libFuzzer honors SIGUSR1/SIGUSR2
-# FIXME: Disabled on Windows for now because of reliance on posix only features
-# (eg: export, "&", pkill).
+# Disabled on Windows which does not have SIGUSR1/SIGUSR2.
 UNSUPPORTED: darwin, windows
 RUN: rm -rf %t
 RUN: mkdir -p %t
index 881880e..393a9f6 100644 (file)
@@ -1,4 +1,4 @@
-# REQUIRES: linux
+# UNSUPPORTED: darwin, freebsd
 BINGO: BINGO
 RUN: %cpp_compiler %S/SimpleTest.cpp -o %t-SimpleTest
 RUN: not %run %t-SimpleTest -fork=1 2>&1 | FileCheck %s --check-prefix=BINGO
@@ -11,7 +11,8 @@ OOM: ERROR: libFuzzer: out-of-memory
 RUN: %cpp_compiler %S/OutOfMemoryTest.cpp -o %t-OutOfMemoryTest
 RUN: not %run %t-OutOfMemoryTest -fork=1 -ignore_ooms=0  -rss_limit_mb=128 2>&1 | FileCheck %s --check-prefix=OOM
 
-CRASH: SEGV on unknown address 0x000000000000
+# access-violation is the error thrown on Windows.
+CRASH: {{SEGV|access-violation}} on unknown address 0x000000000000
 RUN: %cpp_compiler %S/ShallowOOMDeepCrash.cpp -o %t-ShallowOOMDeepCrash
 RUN: not %run %t-ShallowOOMDeepCrash -fork=1 -rss_limit_mb=128 2>&1 | FileCheck %s --check-prefix=CRASH