[llvm] r278544 - [LibFuzzer] Fix `-jobs=<N>` where <N> > 1 and the number of workers is > 1 on macOS.

Kostya Serebryany via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 16 17:44:54 PDT 2016


fuzzer-jobs.test periodically fails.
Do you see that?

On Fri, Aug 12, 2016 at 11:29 AM, Dan Liew via llvm-commits <
llvm-commits at lists.llvm.org> wrote:

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


More information about the llvm-commits mailing list