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