CrashTest.cpp 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2019-2020, Shannon Booth <shannon.ml.booth@gmail.com>
  4. * Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/Assertions.h>
  9. #include <AK/Platform.h>
  10. #include <LibTest/CrashTest.h>
  11. #include <sys/wait.h>
  12. #include <unistd.h>
  13. #if !defined(AK_OS_MACOS) && !defined(AK_OS_EMSCRIPTEN)
  14. # include <sys/prctl.h>
  15. #endif
  16. namespace Test {
  17. Crash::Crash(String test_type, Function<Crash::Failure()> crash_function, int crash_signal)
  18. : m_type(move(test_type))
  19. , m_crash_function(move(crash_function))
  20. , m_crash_signal(crash_signal)
  21. {
  22. }
  23. bool Crash::run(RunType run_type)
  24. {
  25. outln("\x1B[33mTesting\x1B[0m: \"{}\"", m_type);
  26. if (run_type == RunType::UsingCurrentProcess) {
  27. return do_report(m_crash_function());
  28. } else {
  29. // Run the test in a child process so that we do not crash the crash program :^)
  30. pid_t pid = fork();
  31. if (pid < 0) {
  32. perror("fork");
  33. VERIFY_NOT_REACHED();
  34. } else if (pid == 0) {
  35. #if !defined(AK_OS_MACOS) && !defined(AK_OS_EMSCRIPTEN)
  36. if (prctl(PR_SET_DUMPABLE, 0, 0) < 0)
  37. perror("prctl(PR_SET_DUMPABLE)");
  38. #endif
  39. exit((int)m_crash_function());
  40. }
  41. int status;
  42. waitpid(pid, &status, 0);
  43. if (WIFEXITED(status)) {
  44. return do_report(Failure(WEXITSTATUS(status)));
  45. }
  46. if (WIFSIGNALED(status)) {
  47. int signal = WTERMSIG(status);
  48. VERIFY(signal > 0);
  49. return do_report(signal);
  50. }
  51. VERIFY_NOT_REACHED();
  52. }
  53. }
  54. bool Crash::do_report(Report report)
  55. {
  56. bool pass = false;
  57. if (m_crash_signal == ANY_SIGNAL) {
  58. pass = report.has<int>();
  59. } else if (m_crash_signal == 0) {
  60. pass = report.has<Failure>() && report.get<Failure>() == Failure::DidNotCrash;
  61. } else if (m_crash_signal > 0) {
  62. pass = report.has<int>() && report.get<int>() == m_crash_signal;
  63. } else {
  64. VERIFY_NOT_REACHED();
  65. }
  66. if (pass)
  67. out("\x1B[32mPASS\x1B[0m: ");
  68. else
  69. out("\x1B[31mFAIL\x1B[0m: ");
  70. report.visit(
  71. [&](Failure const& failure) {
  72. switch (failure) {
  73. case Failure::DidNotCrash:
  74. out("Did not crash");
  75. break;
  76. case Failure::UnexpectedError:
  77. out("Unexpected error");
  78. break;
  79. default:
  80. VERIFY_NOT_REACHED();
  81. }
  82. },
  83. [&](int const& signal) {
  84. out("Terminated with signal {}", signal);
  85. });
  86. if (!pass) {
  87. if (m_crash_signal == ANY_SIGNAL) {
  88. out(" while expecting any signal");
  89. } else if (m_crash_signal > 0) {
  90. out(" while expecting signal {}", m_crash_signal);
  91. }
  92. }
  93. outln();
  94. return pass;
  95. }
  96. }