Browse Source

Kernel: Handle removal of Process from list before unref

This makes the following scenario impossible with an SMP setup:

1) CPU A enters unref() and decrements the link count to 0.
2) CPU B sees the process in the process list and ref()s it.
3) CPU A removes the process from the list and continues destructing.
4) CPU B is now holding a destructed Process object.

By holding the process list lock before doing anything with it, we
ensure that other CPUs can't find this process in the middle of it being
destructed.
sin-ack 3 years ago
parent
commit
18f260b78b
2 changed files with 19 additions and 5 deletions
  1. 16 4
      Kernel/Process.cpp
  2. 3 1
      Kernel/Process.h

+ 16 - 4
Kernel/Process.cpp

@@ -295,11 +295,23 @@ Process::~Process()
     VERIFY(!m_alarm_timer);
 
     PerformanceManager::add_process_exit_event(*this);
+}
 
-    if (m_list_node.is_in_list())
-        processes().with_exclusive([&](auto& list) {
-            list.remove(*this);
-        });
+bool Process::unref() const
+{
+    // NOTE: We need to obtain the process list lock before doing anything,
+    //       because otherwise someone might get in between us lowering the
+    //       refcount and acquiring the lock.
+    return processes().with_exclusive([&](auto& list) {
+        auto new_ref_count = deref_base();
+        if (new_ref_count > 0)
+            return false;
+
+        if (m_list_node.is_in_list())
+            list.remove(*const_cast<Process*>(this));
+        delete this;
+        return true;
+    });
 }
 
 // Make sure the compiler doesn't "optimize away" this function:

+ 3 - 1
Kernel/Process.h

@@ -85,7 +85,7 @@ typedef HashMap<FlatPtr, RefPtr<FutexQueue>> FutexQueues;
 struct LoadResult;
 
 class Process
-    : public RefCounted<Process>
+    : public AK::RefCountedBase
     , public Weakable<Process> {
 
     class ProtectedValues {
@@ -175,6 +175,8 @@ public:
     static RefPtr<Process> create_kernel_process(RefPtr<Thread>& first_thread, String&& name, void (*entry)(void*), void* entry_data = nullptr, u32 affinity = THREAD_AFFINITY_DEFAULT, RegisterProcess do_register = RegisterProcess::Yes);
     static RefPtr<Process> create_user_process(RefPtr<Thread>& first_thread, const String& path, uid_t, gid_t, ProcessID ppid, int& error, Vector<String>&& arguments = Vector<String>(), Vector<String>&& environment = Vector<String>(), TTY* = nullptr);
     static void register_new(Process&);
+
+    bool unref() const;
     ~Process();
 
     static NonnullRefPtrVector<Process> all_processes();