Browse Source

Kernel: Make sure threads which don't do any syscalls are terminated

Steps to reproduce:

$ cat loop.c
int main() { for (;;); }
$ gcc -o loop loop.c
$ ./loop

Terminating this process wasn't previously possible because we only
checked whether the thread should be terminated on syscall exit.
Gunnar Beutner 4 years ago
parent
commit
3c3a1726df
3 changed files with 20 additions and 0 deletions
  1. 7 0
      Kernel/Scheduler.cpp
  2. 9 0
      Kernel/Syscall.cpp
  3. 4 0
      Kernel/Thread.h

+ 7 - 0
Kernel/Scheduler.cpp

@@ -192,6 +192,13 @@ bool Scheduler::pick_next()
 
     ScopedSpinLock lock(g_scheduler_lock);
 
+    auto current_thread = Thread::current();
+    if (current_thread->should_die() && current_thread->may_die_immediately()) {
+        // Ordinarily the thread would die on syscall exit, however if the thread
+        // doesn't perform any syscalls we still need to mark it for termination here.
+        current_thread->set_state(Thread::Dying);
+    }
+
     if constexpr (SCHEDULER_RUNNABLE_DEBUG) {
         dump_thread_list();
     }

+ 9 - 0
Kernel/Syscall.cpp

@@ -4,6 +4,7 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <AK/ScopeGuard.h>
 #include <Kernel/API/Syscall.h>
 #include <Kernel/Arch/x86/CPU.h>
 #include <Kernel/Panic.h>
@@ -129,6 +130,14 @@ NEVER_INLINE void syscall_handler(TrapFrame* trap)
 {
     auto& regs = *trap->regs;
     auto current_thread = Thread::current();
+    {
+        ScopedSpinLock lock(g_scheduler_lock);
+        current_thread->set_may_die_immediately(false);
+    }
+    ScopeGuard reset_may_die_immediately = [&current_thread] {
+        ScopedSpinLock lock(g_scheduler_lock);
+        current_thread->set_may_die_immediately(true);
+    };
     VERIFY(current_thread->previous_mode() == Thread::PreviousMode::UserMode);
     auto& process = current_thread->process();
 

+ 4 - 0
Kernel/Thread.h

@@ -1134,6 +1134,9 @@ public:
     bool is_profiling_suppressed() const { return m_is_profiling_suppressed; }
     void set_profiling_suppressed() { m_is_profiling_suppressed = true; }
 
+    bool may_die_immediately() const { return m_may_die_immediately; }
+    void set_may_die_immediately(bool flag) { m_may_die_immediately = flag; }
+
 private:
     Thread(NonnullRefPtr<Process>, NonnullOwnPtr<Region>, NonnullRefPtr<Timer>);
 
@@ -1227,6 +1230,7 @@ private:
     Optional<Range> m_thread_specific_range;
     Array<SignalActionData, NSIG> m_signal_action_data;
     Blocker* m_blocker { nullptr };
+    bool m_may_die_immediately { true };
 
 #if LOCK_DEBUG
     struct HoldingLockInfo {