kill-pidtid-confusion.cpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /*
  2. * Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Assertions.h>
  7. #include <AK/Format.h>
  8. #include <LibPthread/pthread.h>
  9. #include <signal.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <unistd.h>
  13. /*
  14. * Bug:
  15. * If the main thread of a process is no longer alive, it cannot receive
  16. * signals anymore. This can manifest as, for example, an unkillable process.
  17. *
  18. * So what needs to happen:
  19. * - There is process P
  20. * - It has more than one thread
  21. * - The main thread calls thread_exit(), leaving the rest of the threads alive
  22. * - Now the process is unkillable!
  23. *
  24. * Here's how to demonstrate the bug:
  25. * - Time 0: PX forks into PZ (mnemonic: Zombie)
  26. * - Time 1: PZ's main thread T1 creates a new thread T2
  27. * - Time 2: Nothing (T2 could communicate to PX both process and thread ID)
  28. * (most LibC functions crash currently, which is a different bug I suppose.)
  29. * - Time 3: T1 calls thread_exit()
  30. * - Time 4:
  31. * * PX tries to kill PZ (should work, but doesn't)
  32. * * PX tries to kill PZ using T2's thread ID (shouldn't work, and doesn't)
  33. * * PX outputs all results.
  34. */
  35. static constexpr useconds_t STEP_SIZE = 1100000;
  36. static void fork_into(void(fn)())
  37. {
  38. const pid_t rc = fork();
  39. if (rc < 0) {
  40. perror("fork");
  41. exit(1);
  42. }
  43. if (rc > 0) {
  44. return;
  45. }
  46. fn();
  47. dbgln("child finished (?)");
  48. exit(1);
  49. }
  50. static void thread_into(void* (*fn)(void*))
  51. {
  52. pthread_t tid;
  53. const int rc = pthread_create(&tid, nullptr, fn, nullptr);
  54. if (rc < 0) {
  55. perror("pthread_create");
  56. exit(1);
  57. }
  58. }
  59. static void sleep_steps(useconds_t steps)
  60. {
  61. const int rc = usleep(steps * STEP_SIZE);
  62. if (rc < 0) {
  63. perror("usleep");
  64. VERIFY_NOT_REACHED();
  65. }
  66. }
  67. static bool try_kill(pid_t kill_id)
  68. {
  69. int rc = kill(kill_id, SIGTERM);
  70. perror("kill");
  71. printf("kill rc: %d\n", rc);
  72. return rc >= 0;
  73. }
  74. static void run_pz();
  75. static void* run_pz_t2_wrap(void* fd_ptr);
  76. static void run_pz_t2();
  77. int main(int, char**)
  78. {
  79. // This entire function is the entirety of process PX.
  80. // Time 0: PX forks into PZ (mnemonic: Zombie)
  81. dbgln("PX forks into PZ");
  82. fork_into(run_pz);
  83. sleep_steps(4);
  84. // Time 4:
  85. dbgln("Let's hope everything went fine!");
  86. pid_t guessed_pid = getpid() + 1;
  87. pid_t guessed_tid = guessed_pid + 1;
  88. printf("About to kill PID %d, TID %d.\n", guessed_pid, guessed_tid);
  89. if (try_kill(guessed_tid)) {
  90. printf("FAIL, could kill a thread\n");
  91. exit(1);
  92. }
  93. if (!try_kill(guessed_pid)) {
  94. printf("FAIL, could not kill the process\n");
  95. exit(1);
  96. }
  97. printf("PASS\n");
  98. return 0;
  99. }
  100. static void run_pz()
  101. {
  102. // Time 0: PX forks into PZ (mnemonic: Zombie)
  103. sleep_steps(1);
  104. // Time 1: PZ's main thread T1 creates a new thread T2
  105. dbgln("PZ calls pthread_create");
  106. thread_into(run_pz_t2_wrap);
  107. sleep_steps(2);
  108. // Time 3: T1 calls thread_exit()
  109. dbgln("PZ(T1) calls thread_exit");
  110. pthread_exit(nullptr);
  111. VERIFY_NOT_REACHED();
  112. }
  113. static void* run_pz_t2_wrap(void*)
  114. {
  115. run_pz_t2();
  116. exit(1);
  117. }
  118. static void run_pz_t2()
  119. {
  120. // Time 1: PZ's main thread T1 creates a new thread T2
  121. sleep_steps(1);
  122. // Time 2: Nothing
  123. // FIXME: For some reason, both printf() and dbg() crash.
  124. // This also prevents us from using a pipe to communicate to PX both process and thread ID
  125. // dbgln("T2: I'm alive and well.");
  126. sleep_steps(18);
  127. // Time 20: Cleanup
  128. printf("PZ(T2) dies from boredom.\n");
  129. exit(0);
  130. }