소스 검색

Kernel: Ignore subsequent calls to Process::die

It's possible that another thread might try to exit the process just
about the same time another thread does the same, or a crash happens.
Also, we may not be able to kill all other threads instantly as they
may be blocked in the kernel (though in this case they would get killed
before ever returning back to user mode. So keep track of whether
Process::die was already called and ignore it on subsequent calls.

Fixes #8485
Tom 4 년 전
부모
커밋
d7e5521a04
3개의 변경된 파일25개의 추가작업 그리고 3개의 파일을 삭제
  1. 10 1
      Kernel/Process.cpp
  2. 9 2
      Kernel/Process.h
  3. 6 0
      Kernel/Syscall.cpp

+ 10 - 1
Kernel/Process.cpp

@@ -581,7 +581,7 @@ void Process::finalize()
     // allow us to take references anymore.
     ProcFSComponentRegistry::the().unregister_process(*this);
 
-    m_dead = true;
+    m_state.store(State::Dead, AK::MemoryOrder::memory_order_release);
 
     {
         // FIXME: PID/TID BUG
@@ -626,6 +626,15 @@ void Process::unblock_waiters(Thread::WaitBlocker::UnblockFlags flags, u8 signal
 
 void Process::die()
 {
+    auto expected = State::Running;
+    if (!m_state.compare_exchange_strong(expected, State::Dying, AK::memory_order_acquire)) {
+        // It's possible that another thread calls this at almost the same time
+        // as we can't always instantly kill other threads (they may be blocked)
+        // So if we already were called then other threads should stop running
+        // momentarily and we only really need to service the first thread
+        return;
+    }
+
     // Let go of the TTY, otherwise a slave PTY may keep the master PTY from
     // getting an EOF when the last process using the slave PTY dies.
     // If the master PTY owner relies on an EOF to know when to wait() on a

+ 9 - 2
Kernel/Process.h

@@ -148,6 +148,12 @@ class Process
         Process& m_process;
     };
 
+    enum class State : u8 {
+        Running = 0,
+        Dying,
+        Dead
+    };
+
 public:
     inline static Process* current()
     {
@@ -201,7 +207,8 @@ public:
     bool should_core_dump() const { return m_should_dump_core; }
     void set_dump_core(bool dump_core) { m_should_dump_core = dump_core; }
 
-    bool is_dead() const { return m_dead; }
+    bool is_dying() const { return m_state.load(AK::MemoryOrder::memory_order_acquire) != State::Running; }
+    bool is_dead() const { return m_state.load(AK::MemoryOrder::memory_order_acquire) == State::Dead; }
 
     bool is_stopped() const { return m_is_stopped; }
     bool set_stopped(bool stopped) { return m_is_stopped.exchange(stopped); }
@@ -670,7 +677,7 @@ private:
     mutable RecursiveSpinLock m_thread_list_lock;
 
     const bool m_is_kernel_process;
-    bool m_dead { false };
+    Atomic<State> m_state { State::Running };
     bool m_profiling { false };
     Atomic<bool, AK::MemoryOrder::memory_order_relaxed> m_is_stopped { false };
     bool m_should_dump_core { false };

+ 6 - 0
Kernel/Syscall.cpp

@@ -155,6 +155,12 @@ NEVER_INLINE void syscall_handler(TrapFrame* trap)
     auto current_thread = Thread::current();
     VERIFY(current_thread->previous_mode() == Thread::PreviousMode::UserMode);
     auto& process = current_thread->process();
+    if (process.is_dying()) {
+        // It's possible this thread is just about to make a syscall while another is
+        // is killing our process.
+        current_thread->die_if_needed();
+        return;
+    }
 
     if (auto tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) {
         tracer->set_trace_syscalls(false);