<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>