<div dir="ltr">fuzzer-jobs.test periodically fails. <br><div>Do you see that? </div></div><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Aug 12, 2016 at 11:29 AM, Dan Liew via llvm-commits <span dir="ltr"><<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: delcypher<br>
Date: Fri Aug 12 13:29:36 2016<br>
New Revision: 278544<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=278544&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project?rev=278544&view=rev</a><br>
Log:<br>
[LibFuzzer] Fix `-jobs=<N>` where <N> > 1 and the number of workers is > 1 on macOS.<br>
<br>
The original `ExecuteCommand()` called `system()` from the C library.<br>
The C library implementation of this on macOS contains a mutex which<br>
serializes calls to `system()`. This prevented the `-jobs=` flag<br>
from running copies of the fuzzing binary in parallel which is<br>
the opposite of what is intended.<br>
<br>
To fix this on macOS an alternative implementation of `ExecuteCommand()`<br>
is provided that can be used concurrently. This is provided in<br>
`FuzzerUtilDarwin.cpp` which is guarded to only compile code on Apple<br>
platforms. The existing implementation has been moved to a new file<br>
`FuzzerUtilLinux.cpp` which is guarded to only compile code on Linux.<br>
<br>
This commit includes a simple test to check that LibFuzzer is being<br>
executed in parallel when requested.<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D22742" rel="noreferrer" target="_blank">https://reviews.llvm.org/<wbr>D22742</a><br>
<br>
Added:<br>
    llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtilDarwin.cpp<br>
    llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtilLinux.cpp<br>
    llvm/trunk/lib/Fuzzer/test/<wbr>fuzzer-jobs.test<br>
Modified:<br>
    llvm/trunk/lib/Fuzzer/<wbr>CMakeLists.txt<br>
    llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtil.cpp<br>
<br>
Modified: llvm/trunk/lib/Fuzzer/<wbr>CMakeLists.txt<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Fuzzer/CMakeLists.txt?rev=278544&r1=278543&r2=278544&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/llvm/trunk/lib/Fuzzer/<wbr>CMakeLists.txt?rev=278544&r1=<wbr>278543&r2=278544&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- llvm/trunk/lib/Fuzzer/<wbr>CMakeLists.txt (original)<br>
+++ llvm/trunk/lib/Fuzzer/<wbr>CMakeLists.txt Fri Aug 12 13:29:36 2016<br>
@@ -20,6 +20,8 @@ if( LLVM_USE_SANITIZE_COVERAGE )<br>
     FuzzerSHA1.cpp<br>
     FuzzerTracePC.cpp<br>
     FuzzerUtil.cpp<br>
+    FuzzerUtilDarwin.cpp<br>
+    FuzzerUtilLinux.cpp<br>
     )<br>
   add_library(LLVMFuzzerNoMain STATIC<br>
     $<TARGET_OBJECTS:<wbr>LLVMFuzzerNoMainObjects><br>
<br>
Modified: llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtil.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Fuzzer/FuzzerUtil.cpp?rev=278544&r1=278543&r2=278544&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtil.cpp?rev=278544&r1=<wbr>278543&r2=278544&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtil.cpp (original)<br>
+++ llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtil.cpp Fri Aug 12 13:29:36 2016<br>
@@ -146,10 +146,6 @@ int NumberOfCpuCores() {<br>
   return N;<br>
 }<br>
<br>
-int ExecuteCommand(const std::string &Command) {<br>
-  return system(Command.c_str());<br>
-}<br>
-<br>
 bool ToASCII(uint8_t *Data, size_t Size) {<br>
   bool Changed = false;<br>
   for (size_t i = 0; i < Size; i++) {<br>
<br>
Added: llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtilDarwin.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Fuzzer/FuzzerUtilDarwin.cpp?rev=278544&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtilDarwin.cpp?rev=<wbr>278544&view=auto</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtilDarwin.cpp (added)<br>
+++ llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtilDarwin.cpp Fri Aug 12 13:29:36 2016<br>
@@ -0,0 +1,148 @@<br>
+//===- FuzzerUtilDarwin.cpp - Misc utils ------------------------------<wbr>----===//<br>
+//<br>
+//                     The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===------------------------<wbr>------------------------------<wbr>----------------===//<br>
+// Misc utils for Darwin.<br>
+//===------------------------<wbr>------------------------------<wbr>----------------===//<br>
+#include "FuzzerInternal.h"<br>
+#if LIBFUZZER_APPLE<br>
+#include <mutex><br>
+#include <signal.h><br>
+#include <spawn.h><br>
+#include <sys/wait.h><br>
+<br>
+// There is no header for this on macOS so declare here<br>
+extern "C" char **environ;<br>
+<br>
+namespace fuzzer {<br>
+<br>
+static std::mutex SignalMutex;<br>
+// Global variables used to keep track of how signal handling should be<br>
+// restored. They should **not** be accessed without holding `SignalMutex`.<br>
+static int ActiveThreadCount = 0;<br>
+static struct sigaction OldSigIntAction;<br>
+static struct sigaction OldSigQuitAction;<br>
+static sigset_t OldBlockedSignalsSet;<br>
+<br>
+// This is a reimplementation of Libc's `system()`. On Darwin the Libc<br>
+// implementation contains a mutex which prevents it from being used<br>
+// concurrently. This implementation **can** be used concurrently. It sets the<br>
+// signal handlers when the first thread enters and restores them when the last<br>
+// thread finishes execution of the function and ensures this is not racey by<br>
+// using a mutex.<br>
+int ExecuteCommand(const std::string &Command) {<br>
+  posix_spawnattr_t SpawnAttributes;<br>
+  if (posix_spawnattr_init(&<wbr>SpawnAttributes))<br>
+    return -1;<br>
+  // Block and ignore signals of the current process when the first thread<br>
+  // enters.<br>
+  {<br>
+    std::lock_guard<std::mutex> Lock(SignalMutex);<br>
+    if (ActiveThreadCount == 0) {<br>
+      static struct sigaction IgnoreSignalAction;<br>
+      sigset_t BlockedSignalsSet;<br>
+      memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction));<br>
+      IgnoreSignalAction.sa_handler = SIG_IGN;<br>
+<br>
+      if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) {<br>
+        Printf("Failed to ignore SIGINT\n");<br>
+        (void)posix_spawnattr_destroy(<wbr>&SpawnAttributes);<br>
+        return -1;<br>
+      }<br>
+      if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) {<br>
+        Printf("Failed to ignore SIGQUIT\n");<br>
+        // Try our best to restore the signal handlers.<br>
+        (void)sigaction(SIGINT, &OldSigIntAction, NULL);<br>
+        (void)posix_spawnattr_destroy(<wbr>&SpawnAttributes);<br>
+        return -1;<br>
+      }<br>
+<br>
+      (void)sigemptyset(&<wbr>BlockedSignalsSet);<br>
+      (void)sigaddset(&<wbr>BlockedSignalsSet, SIGCHLD);<br>
+      if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) ==<br>
+          -1) {<br>
+        Printf("Failed to block SIGCHLD\n");<br>
+        // Try our best to restore the signal handlers.<br>
+        (void)sigaction(SIGQUIT, &OldSigQuitAction, NULL);<br>
+        (void)sigaction(SIGINT, &OldSigIntAction, NULL);<br>
+        (void)posix_spawnattr_destroy(<wbr>&SpawnAttributes);<br>
+        return -1;<br>
+      }<br>
+    }<br>
+    ++ActiveThreadCount;<br>
+  }<br>
+<br>
+  // NOTE: Do not introduce any new `return` statements past this<br>
+  // point. It is important that `ActiveThreadCount` always be decremented<br>
+  // when leaving this function.<br>
+<br>
+  // Make sure the child process uses the default handlers for the<br>
+  // following signals rather than inheriting what the parent has.<br>
+  sigset_t DefaultSigSet;<br>
+  (void)sigemptyset(&<wbr>DefaultSigSet);<br>
+  (void)sigaddset(&<wbr>DefaultSigSet, SIGQUIT);<br>
+  (void)sigaddset(&<wbr>DefaultSigSet, SIGINT);<br>
+  (void)posix_spawnattr_<wbr>setsigdefault(&<wbr>SpawnAttributes, &DefaultSigSet);<br>
+  // Make sure the child process doesn't block SIGCHLD<br>
+  (void)posix_spawnattr_<wbr>setsigmask(&SpawnAttributes, &OldBlockedSignalsSet);<br>
+  short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;<br>
+  (void)posix_spawnattr_<wbr>setflags(&SpawnAttributes, SpawnFlags);<br>
+<br>
+  pid_t Pid;<br>
+  char **Environ = environ; // Read from global<br>
+  const char *CommandCStr = Command.c_str();<br>
+  const char *Argv[] = {"sh", "-c", CommandCStr, NULL};<br>
+  int ErrorCode = 0, ProcessStatus = 0;<br>
+  // FIXME: We probably shouldn't hardcode the shell path.<br>
+  ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes,<br>
+                          (char *const *)Argv, Environ);<br>
+  (void)posix_spawnattr_destroy(<wbr>&SpawnAttributes);<br>
+  if (!ErrorCode) {<br>
+    pid_t SavedPid = Pid;<br>
+    do {<br>
+      // Repeat until call completes uninterrupted.<br>
+      Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0);<br>
+    } while (Pid == -1 && errno == EINTR);<br>
+    if (Pid == -1) {<br>
+      // Fail for some other reason.<br>
+      ProcessStatus = -1;<br>
+    }<br>
+  } else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) {<br>
+    // Fork failure.<br>
+    ProcessStatus = -1;<br>
+  } else {<br>
+    // Shell execution failure.<br>
+    ProcessStatus = W_EXITCODE(127, 0);<br>
+  }<br>
+<br>
+  // Restore the signal handlers of the current process when the last thread<br>
+  // using this function finishes.<br>
+  {<br>
+    std::lock_guard<std::mutex> Lock(SignalMutex);<br>
+    --ActiveThreadCount;<br>
+    if (ActiveThreadCount == 0) {<br>
+      bool FailedRestore = false;<br>
+      if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) {<br>
+        Printf("Failed to restore SIGINT handling\n");<br>
+        FailedRestore = true;<br>
+      }<br>
+      if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) {<br>
+        Printf("Failed to restore SIGQUIT handling\n");<br>
+        FailedRestore = true;<br>
+      }<br>
+      if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) {<br>
+        Printf("Failed to unblock SIGCHLD\n");<br>
+        FailedRestore = true;<br>
+      }<br>
+      if (FailedRestore)<br>
+        ProcessStatus = -1;<br>
+    }<br>
+  }<br>
+  return ProcessStatus;<br>
+}<br>
+}<br>
+#endif // LIBFUZZER_APPLE<br>
<br>
Added: llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtilLinux.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Fuzzer/FuzzerUtilLinux.cpp?rev=278544&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtilLinux.cpp?rev=<wbr>278544&view=auto</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtilLinux.cpp (added)<br>
+++ llvm/trunk/lib/Fuzzer/<wbr>FuzzerUtilLinux.cpp Fri Aug 12 13:29:36 2016<br>
@@ -0,0 +1,19 @@<br>
+//===- FuzzerUtilLinux.cpp - Misc utils ------------------------------<wbr>-----===//<br>
+//<br>
+//                     The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===------------------------<wbr>------------------------------<wbr>----------------===//<br>
+// Misc utils for Linux.<br>
+//===------------------------<wbr>------------------------------<wbr>----------------===//<br>
+#include "FuzzerInternal.h"<br>
+#if LIBFUZZER_LINUX<br>
+#include <stdlib.h><br>
+namespace fuzzer {<br>
+int ExecuteCommand(const std::string &Command) {<br>
+  return system(Command.c_str());<br>
+}<br>
+}<br>
+#endif // LIBFUZZER_LINUX<br>
<br>
Added: llvm/trunk/lib/Fuzzer/test/<wbr>fuzzer-jobs.test<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Fuzzer/test/fuzzer-jobs.test?rev=278544&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/llvm/trunk/lib/Fuzzer/<wbr>test/fuzzer-jobs.test?rev=<wbr>278544&view=auto</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- llvm/trunk/lib/Fuzzer/test/<wbr>fuzzer-jobs.test (added)<br>
+++ llvm/trunk/lib/Fuzzer/test/<wbr>fuzzer-jobs.test Fri Aug 12 13:29:36 2016<br>
@@ -0,0 +1,29 @@<br>
+RUN: rm -rf %tmp<br>
+RUN: mkdir %tmp && cd %tmp<br>
+# Create a shared corpus directory<br>
+RUN: rm -rf FuzzerJobsTestCORPUS<br>
+RUN: mkdir FuzzerJobsTestCORPUS<br>
+RUN: rm -f fuzz-{0,1}.log<br>
+# Start fuzzer and in parallel check that the output files<br>
+# that should be created exist.<br>
+RUN: LLVMFuzzer-EmptyTest -max_total_time=4 -jobs=2 -workers=2 FuzzerJobsTestCORPUS > %t-fuzzer-jobs-test.log 2>&1 & export FUZZER_PID=$!<br>
+# Wait a short while to give time for the child processes<br>
+# to start fuzzing<br>
+RUN: sleep 1<br>
+# If the instances are running in parallel they should have created their log<br>
+# files by now.<br>
+RUN: ls fuzz-0.log<br>
+RUN: ls fuzz-1.log<br>
+# Wait for libfuzzer to finish.<br>
+# This probably isn't portable but we need a way to block until<br>
+# the fuzzer is done otherwise we might remove the files while<br>
+# they are being used.<br>
+RUN: while kill -0 ${FUZZER_PID}; do : ; done<br>
+RUN: rm -f fuzz-{0,1}.log<br>
+RUN: rm -rf FuzzerJobsTestCORPUS<br>
+RUN: FileCheck -input-file=%t-fuzzer-jobs-<wbr>test.log %s<br>
+RUN: rm %t-fuzzer-jobs-test.log<br>
+RUN: cd ../<br>
+<br>
+CHECK-DAG: Job 0 exited with exit code 0<br>
+CHECK-DAG: Job 1 exited with exit code 0<br>
<br>
<br>
______________________________<wbr>_________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@lists.llvm.org">llvm-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/<wbr>mailman/listinfo/llvm-commits</a><br>
</blockquote></div><br></div>