text: // Copyright 2017 The Crashpad Authors. All rights reserved.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  #include "util/posix/signals.h"
  
  #include <fcntl.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/stat.h>
  #include <sys/time.h>
  #include <unistd.h>
  
  #include <limits>
  
  #include "base/compiler_specific.h"
  #include "base/cxx17_backports.h"
  #include "base/files/scoped_file.h"
  #include "base/logging.h"
  #include "base/strings/stringprintf.h"
  #include "build/build_config.h"
  #include "gtest/gtest.h"
  #include "test/errors.h"
  #include "test/multiprocess.h"
  #include "test/scoped_temp_dir.h"
  #include "util/posix/scoped_mmap.h"
  
  namespace crashpad {
  namespace test {
  namespace {
  
  constexpr int kUnexpectedExitStatus = 3;
  
  // Keep synchronized with CauseSignal().
  bool CanCauseSignal(int sig) {
    return sig == SIGABRT ||
           sig == SIGALRM ||
           sig == SIGBUS ||
  #if !defined(ARCH_CPU_ARM64)
           sig == SIGFPE ||
  #endif  // !defined(ARCH_CPU_ARM64)
  #if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
           sig == SIGILL ||
  #endif  // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
           sig == SIGPIPE ||
           sig == SIGSEGV ||
  #if defined(OS_APPLE)
           sig == SIGSYS ||
  #endif  // OS_APPLE
  #if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
           sig == SIGTRAP ||
  #endif  // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
           false;
  }
  
  // Keep synchronized with CanCauseSignal().
  void CauseSignal(int sig) {
    switch (sig) {
      case SIGABRT: {
        abort();
      }
  
      case SIGALRM: {
        struct itimerval itimer = {};
        itimer.it_value.tv_usec = 1E3;  // 1 millisecond
        if (setitimer(ITIMER_REAL, &itimer, nullptr) != 0) {
          PLOG(ERROR) << "setitimer";
          _exit(kUnexpectedExitStatus);
        }
  
        while (true) {
          sleep(std::numeric_limits<unsigned int>::max());
        }
      }
  
      case SIGBUS: {
        ScopedMmap mapped_file;
        {
          base::ScopedFD fd;
          {
            ScopedTempDir temp_dir;
            fd.reset(open(temp_dir.path().Append("empty").value().c_str(),
                          O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_CLOEXEC,
                          0644));
            if (fd.get() < 0) {
              PLOG(ERROR) << "open";
            }
          }
          if (fd.get() < 0) {
            _exit(kUnexpectedExitStatus);
          }
  
          if (!mapped_file.ResetMmap(nullptr,
                                     getpagesize(),
                                     PROT_READ | PROT_WRITE,
                                     MAP_PRIVATE,
                                     fd.get(),
                                     0)) {
            _exit(kUnexpectedExitStatus);
          }
        }
  
        *mapped_file.addr_as<char*>() = 0;
  
        _exit(kUnexpectedExitStatus);
      }
  
  #if !defined(ARCH_CPU_ARM64)
      // ARM64 has hardware integer division instructions that don’t generate a
      // trap for divide-by-zero, so this doesn’t produce SIGFPE.
      case SIGFPE: {
        // Optimization makes this tricky, so get zero from a system call likely
        // to succeed, and try to do something with the result.
        struct stat stat_buf;
        int zero = stat("/", &stat_buf);
        if (zero == -1) {
          // It’s important to check |== -1| and not |!= 0|. An optimizer is free
          // to discard an |== 0| branch entirely, because division by zero is
          // undefined behavior.
          PLOG(ERROR) << "stat";
          _exit(kUnexpectedExitStatus);
        }
  
        int quotient = 2 / zero;
        fstat(quotient, &stat_buf);
        break;
      }
  #endif  // ARCH_CPU_ARM64
  
  #if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
      case SIGILL: {
        // __builtin_trap() causes SIGTRAP on arm64 on Android.
        __builtin_trap();
      }
  #endif  // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)
  
      case SIGPIPE: {
        int pipe_fds[2];
        if (pipe(pipe_fds) != 0) {
          PLOG(ERROR) << "pipe";
          _exit(kUnexpectedExitStatus);
        }
  
        if (close(pipe_fds[0]) != 0) {
          PLOG(ERROR) << "close";
          _exit(kUnexpectedExitStatus);
        }
  
        char c = 0;
        ssize_t rv = write(pipe_fds[1], &c, sizeof(c));
        if (rv < 0) {
          PLOG(ERROR) << "write";
          _exit(kUnexpectedExitStatus);
        } else if (rv != sizeof(c)) {
          LOG(ERROR) << "write";
          _exit(kUnexpectedExitStatus);
        }
        break;
      }
  
      case SIGSEGV: {
        volatile int* i = nullptr;
        *i = 0;
        break;
      }
  
  #if defined(OS_APPLE)
      case SIGSYS: {
  #pragma clang diagnostic push
  #pragma clang diagnostic ignored "-Wdeprecated-declarations"
        int rv = syscall(4095);
  #pragma clang diagnostic pop
        if (rv != 0) {
          PLOG(ERROR) << "syscall";
          _exit(kUnexpectedExitStatus);
        }
        break;
      }
  #endif  // OS_APPLE
  
  #if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
      case SIGTRAP: {
  #if defined(ARCH_CPU_X86_FAMILY)
        asm("int3");
  #elif defined(ARCH_CPU_ARM64)
        // bkpt #0 should work for 32-bit ARCH_CPU_ARMEL, but according to
        // https://crrev.com/f53167270c44, it only causes SIGTRAP on Linux under a
        // 64-bit kernel. For a pure 32-bit armv7 system, it generates SIGBUS.
        asm("brk #0");
  #endif
        break;
      }
  #endif  // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
  
      default: {
        LOG(ERROR) << "unexpected signal " << sig;
        _exit(kUnexpectedExitStatus);
      }
    }
  }
  
  class SignalsTest : public Multiprocess {
   public:
    enum class SignalSource {
      kCause,
      kRaise,
    };
    enum class TestType {
      kDefaultHandler,
      kHandlerExits,
      kHandlerReraisesToDefault,
      kHandlerReraisesToPrevious,
    };
    static constexpr int kExitingHandlerExitStatus = 2;
  
    SignalsTest(TestType test_type, SignalSource signal_source, int sig)
        : Multiprocess(),
          sig_(sig),
          test_type_(test_type),
          signal_source_(signal_source) {}
  
    SignalsTest(const SignalsTest&) = delete;
    SignalsTest& operator=(const SignalsTest&) = delete;
  
    ~SignalsTest() {}
  
   private:
    static void SignalHandler_Exit(int sig, siginfo_t* siginfo, void* context) {
      _exit(kExitingHandlerExitStatus);
    }
  
    static void SignalHandler_ReraiseToDefault(int sig,
                                               siginfo_t* siginfo,
                                               void* context) {
      Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
    }
  
    static void SignalHandler_ReraiseToPrevious(int sig,
                                                siginfo_t* siginfo,
                                                void* context) {
      Signals::RestoreHandlerAndReraiseSignalOnReturn(
          siginfo, old_actions_.ActionForSignal(sig));
    }
  
    // Multiprocess:
    void MultiprocessParent() override {}
  
    void MultiprocessChild() override {
      bool (*install_handlers)(Signals::Handler, int, Signals::OldActions*);
      if (Signals::IsCrashSignal(sig_)) {
        install_handlers = [](Signals::Handler handler,
                              int flags,
                              Signals::OldActions* old_actions) {
          return Signals::InstallCrashHandlers(
              handler, flags, old_actions, nullptr);
        };
      } else if (Signals::IsTerminateSignal(sig_)) {
        install_handlers = Signals::InstallTerminateHandlers;
      } else {
        _exit(kUnexpectedExitStatus);
      }
  
      switch (test_type_) {
        case TestType::kDefaultHandler: {
          // Don’t rely on the default handler being active. Something may have
          // changed it (particularly on Android).
          struct sigaction action;
          sigemptyset(&action.sa_mask);
          action.sa_flags = 0;
          action.sa_handler = SIG_DFL;
          ASSERT_EQ(sigaction(sig_, &action, nullptr), 0)
              << ErrnoMessage("sigaction");
          break;
        }
  
        case TestType::kHandlerExits: {
          ASSERT_TRUE(install_handlers(SignalHandler_Exit, 0, nullptr));
          break;
        }
  
        case TestType::kHandlerReraisesToDefault: {
          ASSERT_TRUE(
              install_handlers(SignalHandler_ReraiseToDefault, 0, nullptr));
          break;
        }
  
        case TestType::kHandlerReraisesToPrevious: {
          ASSERT_TRUE(install_handlers(SignalHandler_Exit, 0, nullptr));
          ASSERT_TRUE(install_handlers(
              SignalHandler_ReraiseToPrevious, 0, &old_actions_));
          break;
        }
      }
  
      switch (signal_source_) {
        case SignalSource::kCause:
          CauseSignal(sig_);
          break;
        case SignalSource::kRaise:
          raise(sig_);
          break;
      }
  
      _exit(kUnexpectedExitStatus);
    }
  
    int sig_;
    TestType test_type_;
    SignalSource signal_source_;
    static Signals::OldActions old_actions_;
  };
  
  Signals::OldActions SignalsTest::old_actions_;
  
  bool ShouldTestSignal(int sig) {
    return Signals::IsCrashSignal(sig) || Signals::IsTerminateSignal(sig);
  }
  
  TEST(Signals, WillSignalReraiseAutonomously) {
    const struct {
      int sig;
      int code;
      bool result;
    } kTestData[] = {
        {SIGBUS, BUS_ADRALN, true},
        {SIGFPE, FPE_FLTDIV, true},
        {SIGILL, ILL_ILLOPC, true},
        {SIGSEGV, SEGV_MAPERR, true},
        {SIGBUS, 0, false},
        {SIGFPE, -1, false},
        {SIGILL, SI_USER, false},
        {SIGSEGV, SI_QUEUE, false},
        {SIGTRAP, TRAP_BRKPT, false},
        {SIGHUP, SEGV_MAPERR, false},
        {SIGINT, SI_USER, false},
    };
    for (size_t index = 0; index < base::size(kTestData); ++index) {
      const auto test_data = kTestData[index];
      SCOPED_TRACE(base::StringPrintf(
          "index %zu, sig %d, code %d", index, test_data.sig, test_data.code));
      siginfo_t siginfo = {};
      siginfo.si_signo = test_data.sig;
      siginfo.si_code = test_data.code;
      EXPECT_EQ(Signals::WillSignalReraiseAutonomously(&siginfo),
                test_data.result);
    }
  }
  
  TEST(Signals, Cause_DefaultHandler) {
    for (int sig = 1; sig < NSIG; ++sig) {
      SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
  
      if (!CanCauseSignal(sig)) {
        continue;
      }
  
      SignalsTest test(SignalsTest::TestType::kDefaultHandler,
                       SignalsTest::SignalSource::kCause,
                       sig);
      test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, sig);
      test.Run();
    }
  }
  
  TEST(Signals, Cause_HandlerExits) {
    for (int sig = 1; sig < NSIG; ++sig) {
      SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
  
      if (!CanCauseSignal(sig)) {
        continue;
      }
  
      SignalsTest test(SignalsTest::TestType::kHandlerExits,
                       SignalsTest::SignalSource::kCause,
                       sig);
      test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,
                                       SignalsTest::kExitingHandlerExitStatus);
      test.Run();
    }
  }
  
  TEST(Signals, Cause_HandlerReraisesToDefault) {
    for (int sig = 1; sig < NSIG; ++sig) {
      SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
  
      if (!CanCauseSignal(sig)) {
        continue;
      }
  
      SignalsTest test(SignalsTest::TestType::kHandlerReraisesToDefault,
                       SignalsTest::SignalSource::kCause,
                       sig);
      test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, sig);
      test.Run();
    }
  }
  
  TEST(Signals, Cause_HandlerReraisesToPrevious) {
    for (int sig = 1; sig < NSIG; ++sig) {
      SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
  
      if (!CanCauseSignal(sig)) {
        continue;
      }
  
      SignalsTest test(SignalsTest::TestType::kHandlerReraisesToPrevious,
                       SignalsTest::SignalSource::kCause,
                       sig);
      test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,
                                       SignalsTest::kExitingHandlerExitStatus);
      test.Run();
    }
  }
  
  TEST(Signals, Raise_DefaultHandler) {
    for (int sig = 1; sig < NSIG; ++sig) {
      SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
  
      if (!ShouldTestSignal(sig)) {
        continue;
      }
  
      SignalsTest test(SignalsTest::TestType::kDefaultHandler,
                       SignalsTest::SignalSource::kRaise,
                       sig);
      test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, sig);
      test.Run();
    }
  }
  
  TEST(Signals, Raise_HandlerExits) {
    for (int sig = 1; sig < NSIG; ++sig) {
      SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
  
      if (!ShouldTestSignal(sig)) {
        continue;
      }
  
      SignalsTest test(SignalsTest::TestType::kHandlerExits,
                       SignalsTest::SignalSource::kRaise,
                       sig);
      test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,
                                       SignalsTest::kExitingHandlerExitStatus);
      test.Run();
    }
  }
  
  TEST(Signals, Raise_HandlerReraisesToDefault) {
    for (int sig = 1; sig < NSIG; ++sig) {
      SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
  
      if (!ShouldTestSignal(sig)) {
        continue;
      }
  
  #if defined(OS_APPLE)
      if (sig == SIGBUS
  #if defined(ARCH_CPU_ARM64)
          || sig == SIGILL || sig == SIGSEGV
  #endif  // defined(ARCH_CPU_ARM64)
         ) {
        // Signal handlers can’t distinguish between these signals arising out of
        // hardware faults and raised asynchronously.
        // Signals::RestoreHandlerAndReraiseSignalOnReturn() assumes that they
        // come from hardware faults, but this test uses raise(), so the re-raise
        // test must be skipped.
        continue;
      }
  #endif  // defined(OS_APPLE)
  
      SignalsTest test(SignalsTest::TestType::kHandlerReraisesToDefault,
                       SignalsTest::SignalSource::kRaise,
                       sig);
      test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, sig);
      test.Run();
    }
  }
  
  TEST(Signals, Raise_HandlerReraisesToPrevious) {
    for (int sig = 1; sig < NSIG; ++sig) {
      SCOPED_TRACE(base::StringPrintf("sig %d (%s)", sig, strsignal(sig)));
  
      if (!ShouldTestSignal(sig)) {
        continue;
      }
  
  #if defined(OS_APPLE)
      if (sig == SIGBUS
  #if defined(ARCH_CPU_ARM64)
          || sig == SIGILL || sig == SIGSEGV
  #endif  // defined(ARCH_CPU_ARM64)
         ) {
        // Signal handlers can’t distinguish between these signals arising out of
        // hardware faults and raised asynchronously.
        // Signals::RestoreHandlerAndReraiseSignalOnReturn() assumes that they
        // come from hardware faults, but this test uses raise(), so the re-raise
        // test must be skipped.
        continue;
      }
  #endif  // defined(OS_APPLE)
  
      SignalsTest test(SignalsTest::TestType::kHandlerReraisesToPrevious,
                       SignalsTest::SignalSource::kRaise,
                       sig);
      test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,
                                       SignalsTest::kExitingHandlerExitStatus);
      test.Run();
    }
  }
  
  TEST(Signals, IsCrashSignal) {
    // Always crash signals.
    EXPECT_TRUE(Signals::IsCrashSignal(SIGABRT));
    EXPECT_TRUE(Signals::IsCrashSignal(SIGBUS));
    EXPECT_TRUE(Signals::IsCrashSignal(SIGFPE));
    EXPECT_TRUE(Signals::IsCrashSignal(SIGILL));
    EXPECT_TRUE(Signals::IsCrashSignal(SIGQUIT));
    EXPECT_TRUE(Signals::IsCrashSignal(SIGSEGV));
    EXPECT_TRUE(Signals::IsCrashSignal(SIGSYS));
    EXPECT_TRUE(Signals::IsCrashSignal(SIGTRAP));
  
    // Always terminate signals.
    EXPECT_FALSE(Signals::IsCrashSignal(SIGALRM));
    EXPECT_FALSE(Signals::IsCrashSignal(SIGHUP));
    EXPECT_FALSE(Signals::IsCrashSignal(SIGINT));
    EXPECT_FALSE(Signals::IsCrashSignal(SIGPIPE));
    EXPECT_FALSE(Signals::IsCrashSignal(SIGPROF));
    EXPECT_FALSE(Signals::IsCrashSignal(SIGTERM));
    EXPECT_FALSE(Signals::IsCrashSignal(SIGUSR1));
    EXPECT_FALSE(Signals::IsCrashSignal(SIGUSR2));
    EXPECT_FALSE(Signals::IsCrashSignal(SIGVTALRM));
  
    // Never crash or terminate signals.
    EXPECT_FALSE(Signals::IsCrashSignal(SIGCHLD));
    EXPECT_FALSE(Signals::IsCrashSignal(SIGCONT));
    EXPECT_FALSE(Signals::IsCrashSignal(SIGTSTP));
    EXPECT_FALSE(Signals::IsCrashSignal(SIGTTIN));
    EXPECT_FALSE(Signals::IsCrashSignal(SIGTTOU));
    EXPECT_FALSE(Signals::IsCrashSignal(SIGURG));
    EXPECT_FALSE(Signals::IsCrashSignal(SIGWINCH));
  }
  
  TEST(Signals, IsTerminateSignal) {
    // Always terminate signals.
    EXPECT_TRUE(Signals::IsTerminateSignal(SIGALRM));
    EXPECT_TRUE(Signals::IsTerminateSignal(SIGHUP));
    EXPECT_TRUE(Signals::IsTerminateSignal(SIGINT));
    EXPECT_TRUE(Signals::IsTerminateSignal(SIGPIPE));
    EXPECT_TRUE(Signals::IsTerminateSignal(SIGPROF));
    EXPECT_TRUE(Signals::IsTerminateSignal(SIGTERM));
    EXPECT_TRUE(Signals::IsTerminateSignal(SIGUSR1));
    EXPECT_TRUE(Signals::IsTerminateSignal(SIGUSR2));
    EXPECT_TRUE(Signals::IsTerminateSignal(SIGVTALRM));
  
    // Always crash signals.
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGABRT));
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGBUS));
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGFPE));
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGILL));
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGQUIT));
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGSEGV));
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGSYS));
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGTRAP));
  
    // Never crash or terminate signals.
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGCHLD));
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGCONT));
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGTSTP));
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGTTIN));
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGTTOU));
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGURG));
    EXPECT_FALSE(Signals::IsTerminateSignal(SIGWINCH));
  }
  
  }  // namespace
  }  // namespace test
  }  // namespace crashpad
  