From dabbc348bc4edab522901d8473acaabe276cdc45 Mon Sep 17 00:00:00 2001
From: Frans Kaashoek <kaashoek@mit.edu>
Date: Sat, 6 Jul 2019 16:38:41 -0400
Subject: Maybe fix two races identified by rtm (thx!):

- during exit(), hold p's parent lock and p's lock across all changes
to p and its parent (e.g., reparenting and wakeup1).  the lock
ordering between concurrent exits of children, parent, and great
parent might work out because processes form a tree.

- in wakeup1() test and set p->state atomically by asking caller to
have p locked.

a correctness proof would be desirable.
---
 kernel/proc.c | 32 ++++++++++++++++++++------------
 1 file changed, 20 insertions(+), 12 deletions(-)

(limited to 'kernel')

diff --git a/kernel/proc.c b/kernel/proc.c
index 9c64c4a..2266ca6 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -288,22 +288,28 @@ fork(void)
   return pid;
 }
 
-// Pass p's abandoned children to init.
-void reparent(struct proc *p) {
+// Pass p's abandoned children to init. p and p's parent
+// are locked.
+void reparent(struct proc *p, struct proc *parent) {
   struct proc *pp;
 
   for(pp = ptable.proc; pp < &ptable.proc[NPROC]; pp++){
-    acquire(&pp->lock);
-    if(pp->parent == p){
-      pp->parent = initproc;
-      if(pp->state == ZOMBIE) {
+    if (pp != p && pp != parent) {
+      acquire(&pp->lock);
+      if(pp->parent == p){
+        pp->parent = initproc;
+        if(pp->state == ZOMBIE) {
+          acquire(&initproc->lock);
           wakeup1(initproc);
+          release(&initproc->lock);
+        }
       }
+      release(&pp->lock);
     }
-    release(&pp->lock);
   }
 }
 
+
 // Exit the current process.  Does not return.
 // An exited process remains in the zombie state
 // until its parent calls wait().
@@ -331,19 +337,20 @@ exit(void)
   iput(cwd);
   end_op();
 
-  reparent(p);
-  
   acquire(&p->parent->lock);
     
   acquire(&p->lock);
+
+  reparent(p, p->parent);
+
   p->cwd = 0;
   p->state = ZOMBIE;
 
-  release(&p->parent->lock);
-
   // Parent might be sleeping in wait().
   wakeup1(p->parent);
 
+  release(&p->parent->lock);
+
   // Jump into the scheduler, never to return.
   sched();
   panic("zombie exit");
@@ -529,7 +536,8 @@ sleep(void *chan, struct spinlock *lk)
 }
 
 //PAGEBREAK!
-// Wake up locked parent, used by exit()
+// Wake up p, used by exit()
+// Caller should lock p.
 static void
 wakeup1(struct proc *p)
 {
-- 
cgit v1.2.3