On 26/02/18(Mon) 17:52, Martin Pieuchot wrote:
> On 20/10/17(Fri) 11:50, Martin Pieuchot wrote:
> > On 14/10/17(Sat) 22:07, Philip Guenther wrote:
> > > 
> > > The diff below adds proctreelk, an rwlock protecting the links of the 
> > > process tree and related bits, as well as uidinfolk, an rwlock protecting 
> > > the uidinfo hash table.
> > > 
> > > Parts of this are based on FreeBSD's proctree_lock, particularly the 
> > > reorganization of enterpgrp() into enternewpgrp() and enterthispgrp() and 
> > > the splitting out of killjobc() from exit1().
> > > 
> > > This diff should address the previously reported crashes seen when using 
> > > ktrace(2) while creating/exiting processes.
> > > 
> > > This has been stable for quite a while under my usage; please test and 
> > > report any issues.
> > 
> > First of all, I'm very happy to see this diff.  Thanks Philip.
> > 
> > I have been running this diff on my amd64 NFS client/server build
> > machine since you posted it.  So far no issue, so it is stable for
> > this usage as well.
> > 
> > I'd however appreciate if you could commit the killjobc() and
> > enter*grp() refactoring first.  Because in case of revert this
> > would be less pain.
> 
> I've done that.
> 
> > That's also for this reason that I introduced a macro for the
> > NET_LOCK().  So I could enable/disable it without having to revert
> > N files.  No idea if this could be useful there two.
> > 
> > I like the way you annotate the protected elements in the structure.
> > I'll try to do the same for bpf.
> > 
> > I'm also suggesting you commit the `uidinfolk' bits first.  This seems
> > quite safe.  In this exact part, what about introducing a uid_release(),
> > a wrapper around rw_exit_write(&uidinfolk), to be called after uid_find()?
> > This way you can keep the lock local.
> 
> And that too, here's an updated/rebased diff.

Here's an updated diff where I fixed multiple issues reported by visa@.
This is mostly for discussion / backup.  Changes include:

 - Release the `proctreelk' in sys_getsid()'s error path.

 - Document that process_zap() releases the `proctreelk'.

 - Do not hold the `proctreelk' around ttywait() in killjobc()

 - All access to fields marked with [t] in sys/proc.h should now be
   protected or documented with XXXPT.

 - As a result setlogin(2) and getlogin_r(2) are now marked NOLOCK.

The bigger problem is currently csignal() and pgsignal().  Both can be
called from interrupt context.  That means that `pg_members' might have
to be protected differently.

Thoughts?

diff --git sys/kern/exec_elf.c sys/kern/exec_elf.c
index 0a2fc0a9c8a..e371acf1b33 100644
--- sys/kern/exec_elf.c
+++ sys/kern/exec_elf.c
@@ -1168,12 +1168,14 @@ coredump_notes_elf(struct proc *p, void *iocookie, 
size_t *sizep)
                cpi.cpi_sigcatch = pr->ps_sigacts->ps_sigcatch;
 
                cpi.cpi_pid = pr->ps_pid;
+               rw_enter_read(&proctreelk);
                cpi.cpi_ppid = pr->ps_pptr->ps_pid;
                cpi.cpi_pgrp = pr->ps_pgid;
                if (pr->ps_session->s_leader)
                        cpi.cpi_sid = pr->ps_session->s_leader->ps_pid;
                else
                        cpi.cpi_sid = 0;
+               rw_exit_read(&proctreelk);
 
                cpi.cpi_ruid = p->p_ucred->cr_ruid;
                cpi.cpi_euid = p->p_ucred->cr_uid;
diff --git sys/kern/kern_acct.c sys/kern/kern_acct.c
index 9951a2a2789..78c6fa6a8ac 100644
--- sys/kern/kern_acct.c
+++ sys/kern/kern_acct.c
@@ -206,11 +206,13 @@ acct_process(struct proc *p)
        acct.ac_gid = pr->ps_ucred->cr_rgid;
 
        /* (7) The terminal from which the process was started */
+       rw_enter_read(&proctreelk);
        if ((pr->ps_flags & PS_CONTROLT) &&
            pr->ps_pgrp->pg_session->s_ttyp)
                acct.ac_tty = pr->ps_pgrp->pg_session->s_ttyp->t_dev;
        else
                acct.ac_tty = NODEV;
+       rw_exit_read(&proctreelk);
 
        /* (8) The boolean flags that tell how the process terminated, etc. */
        acct.ac_flag = pr->ps_acflag;
diff --git sys/kern/kern_exec.c sys/kern/kern_exec.c
index 7aa601f43a3..acb91590e36 100644
--- sys/kern/kern_exec.c
+++ sys/kern/kern_exec.c
@@ -509,9 +509,11 @@ sys_execve(struct proc *p, void *v, register_t *retval)
 
        atomic_setbits_int(&pr->ps_flags, PS_EXEC);
        if (pr->ps_flags & PS_PPWAIT) {
+               rw_enter_read(&proctreelk);
                atomic_clearbits_int(&pr->ps_flags, PS_PPWAIT);
                atomic_clearbits_int(&pr->ps_pptr->ps_flags, PS_ISPWAIT);
                wakeup(pr->ps_pptr);
+               rw_exit_read(&proctreelk);
        }
 
        /*
diff --git sys/kern/kern_exit.c sys/kern/kern_exit.c
index dfd4e38078f..6e90b86c641 100644
--- sys/kern/kern_exit.c
+++ sys/kern/kern_exit.c
@@ -146,6 +146,7 @@ exit1(struct proc *p, int rv, int flags)
                 * is set; we wake up the parent early to avoid deadlock.
                 */
                if (pr->ps_flags & PS_PPWAIT) {
+                       /* XXXPT `proctreelk` ? */
                        atomic_clearbits_int(&pr->ps_flags, PS_PPWAIT);
                        atomic_clearbits_int(&pr->ps_pptr->ps_flags,
                            PS_ISPWAIT);
@@ -198,6 +199,7 @@ exit1(struct proc *p, int rv, int flags)
                 * If parent has the SAS_NOCLDWAIT flag set, we're not
                 * going to become a zombie.
                 */
+               /* XXXPT `proctreelk` ? */
                if (pr->ps_pptr->ps_sigacts->ps_flags & SAS_NOCLDWAIT)
                        atomic_setbits_int(&pr->ps_flags, PS_NOZOMBIE);
        }
@@ -212,11 +214,7 @@ exit1(struct proc *p, int rv, int flags)
         * thread of a process that isn't PS_NOZOMBIE, we'll put
         * the process on the zombprocess list below.
         */
-       /*
-        * NOTE: WE ARE NO LONGER ALLOWED TO SLEEP!
-        */
-       p->p_stat = SDEAD;
-
+       rw_enter_write(&proctreelk);
        LIST_REMOVE(p, p_hash);
        LIST_REMOVE(p, p_list);
 
@@ -264,6 +262,7 @@ exit1(struct proc *p, int rv, int flags)
                        }
                }
        }
+       rw_exit_write(&proctreelk);
 
        /* add thread's accumulated rusage into the process's total */
        ruadd(rup, &p->p_ru);
@@ -292,9 +291,13 @@ exit1(struct proc *p, int rv, int flags)
                 * wait4() to return ECHILD.
                 */
                if (pr->ps_flags & PS_NOZOMBIE) {
-                       struct process *ppr = pr->ps_pptr;
+                       struct process *ppr;
+
+                       rw_enter_write(&proctreelk);
+                       ppr = pr->ps_pptr;
                        proc_reparent(pr, initprocess);
                        wakeup(ppr);
+                       rw_exit_write(&proctreelk);
                }
 
                /*
@@ -311,6 +314,11 @@ exit1(struct proc *p, int rv, int flags)
                KASSERT(pr->ps_refcnt > 0);
        }
 
+       /*
+        * NOTE: WE ARE NO LONGER ALLOWED TO SLEEP!
+        */
+       p->p_stat = SDEAD;
+
        /*
         * Other substructures are freed from reaper and wait().
         */
@@ -413,6 +421,7 @@ reaper(void)
                        /* Release the rest of the process's vmspace */
                        uvm_exit(pr);
 
+                       rw_enter_write(&proctreelk);
                        if ((pr->ps_flags & PS_NOZOMBIE) == 0) {
                                /* Process is now a true zombie. */
                                atomic_setbits_int(&pr->ps_flags, PS_ZOMBIE);
@@ -420,8 +429,14 @@ reaper(void)
 
                                /* Wake up the parent so it can get exit 
status. */
                                wakeup(pr->ps_pptr);
+                               rw_exit_write(&proctreelk);
                        } else {
-                               /* No one will wait for us. Just zap the 
process now */
+                               /*
+                                * No one will wait for us. Just zap the
+                                * process now.
+                                *
+                                * Release `proctreelk' for us.
+                                */
                                process_zap(pr);
                        }
                }
@@ -467,12 +482,12 @@ dowait4(struct proc *q, pid_t pid, int *statusp, int 
options,
        struct proc *p;
        int error;
 
-       if (pid == 0)
-               pid = -q->p_p->ps_pgid;
        if (options &~ (WUNTRACED|WNOHANG|WCONTINUED))
                return (EINVAL);
-
 loop:
+       rw_enter_write(&proctreelk);
+       if (pid == 0)
+               pid = -q->p_p->ps_pgid;
        nfound = 0;
        LIST_FOREACH(pr, &q->p_p->ps_children, ps_sibling) {
                if ((pr->ps_flags & PS_NOZOMBIE) ||
@@ -491,6 +506,7 @@ loop:
                                *statusp = p->p_xstat;  /* convert to int */
                        if (rusage != NULL)
                                memcpy(rusage, pr->ps_ru, sizeof(*rusage));
+                       /* Release `proctreelk' for us */
                        proc_finish_wait(q, p);
                        return (0);
                }
@@ -498,6 +514,7 @@ loop:
                    (pr->ps_flags & PS_WAITED) == 0 && pr->ps_single &&
                    pr->ps_single->p_stat == SSTOP &&
                    (pr->ps_single->p_flag & P_SUSPSINGLE) == 0) {
+                       rw_exit_write(&proctreelk);
                        single_thread_wait(pr);
 
                        atomic_setbits_int(&pr->ps_flags, PS_WAITED);
@@ -521,6 +538,7 @@ loop:
                                *statusp = W_STOPCODE(p->p_xstat);
                        if (rusage != NULL)
                                memset(rusage, 0, sizeof(*rusage));
+                       rw_exit_write(&proctreelk);
                        return (0);
                }
                if ((options & WCONTINUED) && (p->p_flag & P_CONTINUED)) {
@@ -531,16 +549,21 @@ loop:
                                *statusp = _WCONTINUED;
                        if (rusage != NULL)
                                memset(rusage, 0, sizeof(*rusage));
+                       rw_exit_write(&proctreelk);
                        return (0);
                }
        }
-       if (nfound == 0)
+       if (nfound == 0) {
+               rw_exit_write(&proctreelk);
                return (ECHILD);
+       }
        if (options & WNOHANG) {
+               rw_exit_write(&proctreelk);
                retval[0] = 0;
                return (0);
        }
-       if ((error = tsleep(q->p_p, PWAIT | PCATCH, "wait", 0)) != 0)
+       if ((error = rwsleep(q->p_p, &proctreelk, PWAIT | PCATCH | PNORELOCK,
+           "wait", 0)) != 0)
                return (error);
        goto loop;
 }
@@ -551,6 +574,8 @@ proc_finish_wait(struct proc *waiter, struct proc *p)
        struct process *pr, *tr;
        struct rusage *rup;
 
+       rw_assert_wrlock(&proctreelk);
+
        /*
         * If we got the child via a ptrace 'attach',
         * we need to give it back to the old parent.
@@ -562,6 +587,7 @@ proc_finish_wait(struct proc *waiter, struct proc *p)
                proc_reparent(pr, tr);
                prsignal(tr, SIGCHLD);
                wakeup(tr);
+               rw_exit_write(&proctreelk);
        } else {
                scheduler_wait_hook(waiter, p);
                p->p_xstat = 0;
@@ -569,6 +595,7 @@ proc_finish_wait(struct proc *waiter, struct proc *p)
                ruadd(rup, pr->ps_ru);
                LIST_REMOVE(pr, ps_list);       /* off zombprocess */
                freepid(pr->ps_pid);
+               /* Release `proctreelk' for us */
                process_zap(pr);
        }
 }
@@ -580,6 +607,7 @@ void
 proc_reparent(struct process *child, struct process *parent)
 {
 
+       rw_assert_wrlock(&proctreelk);
        if (child->ps_pptr == parent)
                return;
 
@@ -594,12 +622,15 @@ process_zap(struct process *pr)
        struct vnode *otvp;
        struct proc *p = pr->ps_mainproc;
 
+       rw_assert_wrlock(&proctreelk);
+
        /*
         * Finally finished with old proc entry.
         * Unlink it from its process group and free it.
         */
        leavepgrp(pr);
        LIST_REMOVE(pr, ps_sibling);
+       rw_exit_write(&proctreelk);
 
        /*
         * Decrement the count of procs running with this uid.
diff --git sys/kern/kern_fork.c sys/kern/kern_fork.c
index 8d90d91d11b..7bb716f6fa8 100644
--- sys/kern/kern_fork.c
+++ sys/kern/kern_fork.c
@@ -224,7 +224,6 @@ process_new(struct proc *p, struct process *parent, int 
flags)
            (caddr_t)&pr->ps_endcopy - (caddr_t)&pr->ps_startcopy);
 
        process_initialize(pr, p);
-       pr->ps_pid = allocpid();
 
        /* post-copy fixups */
        pr->ps_pptr = parent;
@@ -237,8 +236,6 @@ process_new(struct proc *p, struct process *parent, int 
flags)
 
        pr->ps_flags = parent->ps_flags &
            (PS_SUGID | PS_SUGIDEXEC | PS_PLEDGE | PS_EXECPLEDGE | PS_WXNEEDED);
-       if (parent->ps_session->s_ttyvp != NULL)
-               pr->ps_flags |= parent->ps_flags & PS_CONTROLT;
 
        /*
         * Duplicate sub-structures as needed.
@@ -269,10 +266,11 @@ process_new(struct proc *p, struct process *parent, int 
flags)
        /* mark as embryo to protect against others */
        pr->ps_flags |= PS_EMBRYO;
 
-       /* Force visibility of all of the above changes */
-       membar_producer();
-
        /* it's sufficiently inited to be globally visible */
+       rw_enter_write(&proctreelk);
+       if (parent->ps_session->s_ttyvp != NULL)
+               pr->ps_flags |= parent->ps_flags & PS_CONTROLT;
+       pr->ps_pid = allocpid();
        LIST_INSERT_HEAD(&allprocess, pr, ps_list);
 
        return pr;
@@ -373,6 +371,7 @@ fork1(struct proc *curp, int flags, void (*func)(void *), 
void *arg,
 
        /*
         * From now on, we're committed to the fork and cannot fail.
+        * process_new() returns with proctreelk held!
         */
        p = thread_new(curp, uaddr);
        pr = process_new(p, curpr, flags);
@@ -447,6 +446,7 @@ fork1(struct proc *curp, int flags, void (*func)(void *), 
void *arg,
                        pr->ps_ptstat->pe_other_pid = curpr->ps_pid;
                }
        }
+       rw_exit_write(&proctreelk);
 
        /*
         * For new processes, set accounting bits and mark as complete.
diff --git sys/kern/kern_ktrace.c sys/kern/kern_ktrace.c
index 5bd55d71d5f..59024ac5bf0 100644
--- sys/kern/kern_ktrace.c
+++ sys/kern/kern_ktrace.c
@@ -448,6 +448,7 @@ doktrace(struct vnode *vp, int ops, int facs, pid_t pid, 
struct proc *p)
        /*
         * do it
         */
+       rw_enter_read(&proctreelk);
        if (pid < 0) {
                /*
                 * by process group
@@ -455,7 +456,7 @@ doktrace(struct vnode *vp, int ops, int facs, pid_t pid, 
struct proc *p)
                pg = pgfind(-pid);
                if (pg == NULL) {
                        error = ESRCH;
-                       goto done;
+                       goto done2;
                }
                LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
                        if (descend)
@@ -471,7 +472,7 @@ doktrace(struct vnode *vp, int ops, int facs, pid_t pid, 
struct proc *p)
                pr = prfind(pid);
                if (pr == NULL) {
                        error = ESRCH;
-                       goto done;
+                       goto done2;
                }
                if (descend)
                        ret |= ktrsetchildren(p, pr, ops, facs, vp, cred);
@@ -480,6 +481,8 @@ doktrace(struct vnode *vp, int ops, int facs, pid_t pid, 
struct proc *p)
        }
        if (!ret)
                error = EPERM;
+done2:
+       rw_exit_read(&proctreelk);
 done:
        return (error);
 }
@@ -549,6 +552,8 @@ ktrsetchildren(struct proc *curp, struct process *top, int 
ops, int facs,
        struct process *pr;
        int ret = 0;
 
+       rw_assert_rdlock(&proctreelk);
+
        pr = top;
        for (;;) {
                ret |= ktrops(curp, pr, ops, facs, vp, cred);
diff --git sys/kern/kern_proc.c sys/kern/kern_proc.c
index 45e84d8462d..112a572a0c7 100644
--- sys/kern/kern_proc.c
+++ sys/kern/kern_proc.c
@@ -67,6 +67,7 @@ u_long pgrphash;
 struct processlist allprocess;
 struct processlist zombprocess;
 struct proclist allproc;
+struct rwlock proctreelk;
 
 struct pool proc_pool;
 struct pool process_pool;
@@ -79,9 +80,6 @@ void  pgdelete(struct pgrp *);
 void   fixjobc(struct process *, struct pgrp *, int);
 
 static void orphanpg(struct pgrp *);
-#ifdef DEBUG
-void pgrpdump(void);
-#endif
 
 /*
  * Initialize global process hashing structures.
@@ -93,6 +91,7 @@ procinit(void)
        LIST_INIT(&zombprocess);
        LIST_INIT(&allproc);
 
+       rw_init(&proctreelk, "proctree");
        rw_init(&uidinfolk, "uidinfo");
 
        tidhashtbl = hashinit(maxthread / 4, M_PROC, M_NOWAIT, &tidhash);
@@ -180,6 +179,7 @@ int
 inferior(struct process *pr, struct process *parent)
 {
 
+       rw_assert_wrlock(&proctreelk);
        for (; pr != parent; pr = pr->ps_pptr)
                if (pr->ps_pid == 0 || pr->ps_pid == 1)
                        return (0);
@@ -222,6 +222,7 @@ pgfind(pid_t pgid)
 {
        struct pgrp *pgrp;
 
+       rw_assert_anylock(&proctreelk);
        LIST_FOREACH(pgrp, PGRPHASH(pgid), pg_hash)
                if (pgrp->pg_id == pgid)
                        return (pgrp);
@@ -250,6 +251,8 @@ zombiefind(pid_t pid)
 void
 enternewpgrp(struct process *pr, struct pgrp *pgrp, struct session *newsess)
 {
+       rw_assert_wrlock(&proctreelk);
+
 #ifdef DIAGNOSTIC
        if (SESS_LEADER(pr))
                panic("%s: session leader attempted setpgrp", __func__);
@@ -292,6 +295,8 @@ enterthispgrp(struct process *pr, struct pgrp *pgrp)
 {
        struct pgrp *savepgrp = pr->ps_pgrp;
 
+       rw_assert_wrlock(&proctreelk);
+
        /*
         * Adjust eligibility of affected pgrps to participate in job control.
         * Increment eligibility counts before decrementing, otherwise we
@@ -302,6 +307,7 @@ enterthispgrp(struct process *pr, struct pgrp *pgrp)
 
        LIST_REMOVE(pr, ps_pglist);
        pr->ps_pgrp = pgrp;
+
        LIST_INSERT_HEAD(&pgrp->pg_members, pr, ps_pglist);
        if (LIST_EMPTY(&savepgrp->pg_members))
                pgdelete(savepgrp);
@@ -314,6 +320,7 @@ void
 leavepgrp(struct process *pr)
 {
 
+       rw_assert_wrlock(&proctreelk);
        if (pr->ps_session->s_verauthppid == pr->ps_pid)
                zapverauth(pr->ps_session);
        LIST_REMOVE(pr, ps_pglist);
@@ -329,6 +336,7 @@ void
 pgdelete(struct pgrp *pgrp)
 {
 
+       rw_assert_wrlock(&proctreelk);
        if (pgrp->pg_session->s_ttyp != NULL && 
            pgrp->pg_session->s_ttyp->t_pgrp == pgrp)
                pgrp->pg_session->s_ttyp->t_pgrp = NULL;
@@ -341,6 +349,9 @@ void
 zapverauth(void *v)
 {
        struct session *sess = v;
+
+       rw_assert_wrlock(&proctreelk);
+
        sess->s_verauthuid = 0;
        sess->s_verauthppid = 0;
 }
@@ -362,6 +373,8 @@ fixjobc(struct process *pr, struct pgrp *pgrp, int entering)
        struct pgrp *hispgrp;
        struct session *mysession = pgrp->pg_session;
 
+       rw_assert_wrlock(&proctreelk);
+
        /*
         * Check pr's parent to see whether pr qualifies its own process
         * group; if so, adjust count for pr's process group.
@@ -393,6 +406,7 @@ fixjobc(struct process *pr, struct pgrp *pgrp, int entering)
 void
 killjobc(struct process *pr)
 {
+       rw_enter_write(&proctreelk);
        if (SESS_LEADER(pr)) {
                struct session *sp = pr->ps_session;
 
@@ -408,7 +422,9 @@ killjobc(struct process *pr)
                        if (sp->s_ttyp->t_session == sp) {
                                if (sp->s_ttyp->t_pgrp)
                                        pgsignal(sp->s_ttyp->t_pgrp, SIGHUP, 1);
+                               rw_exit_write(&proctreelk);
                                ttywait(sp->s_ttyp);
+                               rw_enter_write(&proctreelk);
                                /*
                                 * The tty could have been revoked
                                 * if we blocked.
@@ -430,6 +446,7 @@ killjobc(struct process *pr)
                sp->s_leader = NULL;
        }
        fixjobc(pr, pr->ps_pgrp, 0);
+       rw_exit_write(&proctreelk);
 }
 
 /* 
@@ -442,6 +459,7 @@ orphanpg(struct pgrp *pg)
 {
        struct process *pr;
 
+       rw_assert_wrlock(&proctreelk);
        LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
                if (pr->ps_mainproc->p_stat == SSTOP) {
                        LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
@@ -631,29 +649,3 @@ db_show_all_procs(db_expr_t addr, int haddr, db_expr_t 
count, char *modif)
        }
 }
 #endif
-
-#ifdef DEBUG
-void
-pgrpdump(void)
-{
-       struct pgrp *pgrp;
-       struct process *pr;
-       int i;
-
-       for (i = 0; i <= pgrphash; i++) {
-               if (!LIST_EMPTY(&pgrphashtbl[i])) {
-                       printf("\tindx %d\n", i);
-                       LIST_FOREACH(pgrp, &pgrphashtbl[i], pg_hash) {
-                               printf("\tpgrp %p, pgid %d, sess %p, sesscnt 
%d, mem %p\n",
-                                   pgrp, pgrp->pg_id, pgrp->pg_session,
-                                   pgrp->pg_session->s_count,
-                                   LIST_FIRST(&pgrp->pg_members));
-                               LIST_FOREACH(pr, &pgrp->pg_members, ps_pglist) {
-                                       printf("\t\tpid %d addr %p pgrp %p\n", 
-                                           pr->ps_pid, pr, pr->ps_pgrp);
-                               }
-                       }
-               }
-       }
-}
-#endif /* DEBUG */
diff --git sys/kern/kern_prot.c sys/kern/kern_prot.c
index ec7ac4d4f32..af03e094cb9 100644
--- sys/kern/kern_prot.c
+++ sys/kern/kern_prot.c
@@ -83,7 +83,9 @@ int
 sys_getppid(struct proc *p, void *v, register_t *retval)
 {
 
+       rw_enter_read(&proctreelk);
        *retval = p->p_p->ps_pptr->ps_pid;
+       rw_exit_read(&proctreelk);
        return (0);
 }
 
@@ -92,7 +94,9 @@ int
 sys_getpgrp(struct proc *p, void *v, register_t *retval)
 {
 
+       rw_enter_read(&proctreelk);
        *retval = p->p_p->ps_pgrp->pg_id;
+       rw_exit_read(&proctreelk);
        return (0);
 }
 
@@ -106,16 +110,23 @@ sys_getpgid(struct proc *curp, void *v, register_t 
*retval)
                syscallarg(pid_t) pid;
        } */ *uap = v;
        struct process *targpr = curp->p_p;
+       int error = 0;
 
-       if (SCARG(uap, pid) == 0 || SCARG(uap, pid) == targpr->ps_pid)
-               goto found;
+       if (SCARG(uap, pid) == 0 || SCARG(uap, pid) == targpr->ps_pid) {
+               rw_enter_read(&proctreelk);
+               *retval = targpr->ps_pgid;
+               rw_exit_read(&proctreelk);
+               return 0;
+       }
+       rw_enter_read(&proctreelk);
        if ((targpr = prfind(SCARG(uap, pid))) == NULL)
-               return (ESRCH);
-       if (targpr->ps_session != curp->p_p->ps_session)
-               return (EPERM);
-found:
-       *retval = targpr->ps_pgid;
-       return (0);
+               error = ESRCH;
+       else if (targpr->ps_session != curp->p_p->ps_session)
+               error = EPERM;
+       else
+               *retval = targpr->ps_pgid;
+       rw_exit_read(&proctreelk);
+       return error;
 }
 
 int
@@ -125,19 +136,28 @@ sys_getsid(struct proc *curp, void *v, register_t *retval)
                syscallarg(pid_t) pid;
        } */ *uap = v;
        struct process *targpr = curp->p_p;
+       int error = 0;
+
+       rw_enter_read(&proctreelk);
+       if (SCARG(uap, pid) != 0 && SCARG(uap, pid) != targpr->ps_pid) {
+               if ((targpr = prfind(SCARG(uap, pid))) == NULL) {
+                       error = ESRCH;
+                       goto out;
+               }
+               if (targpr->ps_session != curp->p_p->ps_session) {
+                       error = EPERM;
+                       goto out;
+               }
+       }
 
-       if (SCARG(uap, pid) == 0 || SCARG(uap, pid) == targpr->ps_pid)
-               goto found;
-       if ((targpr = prfind(SCARG(uap, pid))) == NULL)
-               return (ESRCH);
-       if (targpr->ps_session != curp->p_p->ps_session)
-               return (EPERM);
-found:
        /* Skip exiting processes */
        if (targpr->ps_pgrp->pg_session->s_leader == NULL)
-               return (ESRCH);
-       *retval = targpr->ps_pgrp->pg_session->s_leader->ps_pid;
-       return (0);
+               error = ESRCH;
+       else
+               *retval = targpr->ps_pgrp->pg_session->s_leader->ps_pid;
+out:
+       rw_exit_read(&proctreelk);
+       return error;
 }
 
 int
@@ -224,12 +244,15 @@ sys_setsid(struct proc *p, void *v, register_t *retval)
        newsess = pool_get(&session_pool, PR_WAITOK);
        newpgrp = pool_get(&pgrp_pool, PR_WAITOK);
 
+       rw_enter_write(&proctreelk);
        if (pr->ps_pgid == pid || pgfind(pid) != NULL) {
+               rw_exit_write(&proctreelk);
                pool_put(&pgrp_pool, newpgrp);
                pool_put(&session_pool, newsess);
                return (EPERM);
        } else {
                enternewpgrp(pr, newpgrp, newsess);
+               rw_exit_write(&proctreelk);
                *retval = pid;
                return (0);
        }
@@ -269,6 +292,7 @@ sys_setpgid(struct proc *curp, void *v, register_t *retval)
 
        newpgrp = pool_get(&pgrp_pool, PR_WAITOK);
 
+       rw_enter_write(&proctreelk);
        if (pid != 0 && pid != curpr->ps_pid) {
                if ((targpr = prfind(pid)) == 0 || !inferior(targpr, curpr)) {
                        error = ESRCH;
@@ -307,7 +331,8 @@ sys_setpgid(struct proc *curp, void *v, register_t *retval)
                else
                        enterthispgrp(targpr, pgrp);
        }
- out:
+out:
+       rw_exit_write(&proctreelk);
        if (newpgrp != NULL)
                pool_put(&pgrp_pool, newpgrp);
        return (error);
@@ -947,6 +972,12 @@ crget(void)
        return (cr);
 }
 
+void
+crhold(struct ucred *cr)
+{
+       atomic_inc_int(&cr->cr_ref);
+}
+
 /*
  * Free a cred structure.
  * Throws away space when ref count gets to 0.
@@ -954,8 +985,7 @@ crget(void)
 void
 crfree(struct ucred *cr)
 {
-
-       if (--cr->cr_ref == 0)
+       if (atomic_dec_int_nv(&cr->cr_ref) == 0)
                pool_put(&ucred_pool, cr);
 }
 
@@ -1018,12 +1048,17 @@ sys_getlogin_r(struct proc *p, void *v, register_t 
*retval)
                syscallarg(size_t) namelen;
        } */ *uap = v;
        size_t namelen = SCARG(uap, namelen);
-       struct session *s = p->p_p->ps_pgrp->pg_session;
+       struct session *s;
+       char buf[sizeof(s->s_login)];
        int error;
 
+       rw_enter_read(&proctreelk);
+       s = p->p_p->ps_pgrp->pg_session;
        if (namelen > sizeof(s->s_login))
                namelen = sizeof(s->s_login);
-       error = copyoutstr(s->s_login, SCARG(uap, namebuf), namelen, NULL);
+       namelen = strlcpy(buf, s->s_login, namelen) + 1;
+       rw_exit_read(&proctreelk);
+       error = copyoutstr(buf, SCARG(uap, namebuf), namelen, NULL);
        if (error == ENAMETOOLONG)
                error = ERANGE;
        *retval = error;
@@ -1039,15 +1074,19 @@ sys_setlogin(struct proc *p, void *v, register_t 
*retval)
        struct sys_setlogin_args /* {
                syscallarg(const char *) namebuf;
        } */ *uap = v;
-       struct session *s = p->p_p->ps_pgrp->pg_session;
+       struct session *s;
        char buf[sizeof(s->s_login)];
        int error;
 
        if ((error = suser(p)) != 0)
                return (error);
        error = copyinstr(SCARG(uap, namebuf), buf, sizeof(buf), NULL);
-       if (error == 0)
+       if (error == 0) {
+               rw_enter_write(&proctreelk);
+               s = p->p_p->ps_pgrp->pg_session;
                strlcpy(s->s_login, buf, sizeof(s->s_login));
+               rw_exit_write(&proctreelk);
+       }
        else if (error == ENAMETOOLONG)
                error = EINVAL;
        return (error);
diff --git sys/kern/kern_resource.c sys/kern/kern_resource.c
index 184c538d2ef..04c010ef3c9 100644
--- sys/kern/kern_resource.c
+++ sys/kern/kern_resource.c
@@ -90,13 +90,17 @@ sys_getpriority(struct proc *curp, void *v, register_t 
*retval)
        case PRIO_PGRP: {
                struct pgrp *pg;
 
+               rw_enter_read(&proctreelk);
                if (SCARG(uap, who) == 0)
                        pg = curp->p_p->ps_pgrp;
-               else if ((pg = pgfind(SCARG(uap, who))) == NULL)
+               else if ((pg = pgfind(SCARG(uap, who))) == NULL) {
+                       rw_exit_read(&proctreelk);
                        break;
+               }
                LIST_FOREACH(pr, &pg->pg_members, ps_pglist)
                        if (pr->ps_nice < low)
                                low = pr->ps_nice;
+               rw_exit_read(&proctreelk);
                break;
        }
 
@@ -145,14 +149,18 @@ sys_setpriority(struct proc *curp, void *v, register_t 
*retval)
        case PRIO_PGRP: {
                struct pgrp *pg;
                 
+               rw_enter_read(&proctreelk);
                if (SCARG(uap, who) == 0)
                        pg = curp->p_p->ps_pgrp;
-               else if ((pg = pgfind(SCARG(uap, who))) == NULL)
+               else if ((pg = pgfind(SCARG(uap, who))) == NULL) {
+                       rw_exit_read(&proctreelk);
                        break;
+               }
                LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
                        error = donice(curp, pr, SCARG(uap, prio));
                        found++;
                }
+               rw_exit_read(&proctreelk);
                break;
        }
 
diff --git sys/kern/kern_sig.c sys/kern/kern_sig.c
index 5e8153a90a6..91f0e6220f2 100644
--- sys/kern/kern_sig.c
+++ sys/kern/kern_sig.c
@@ -107,6 +107,7 @@ cansignal(struct proc *p, struct process *qr, int signum)
        if (uc == quc)
                return (1);
 
+       rw_assert_rdlock(&proctreelk);
        if (signum == SIGCONT && qr->ps_session == pr->ps_session)
                return (1);             /* SIGCONT in session */
 
@@ -568,7 +569,7 @@ sys_kill(struct proc *cp, void *v, register_t *retval)
        struct process *pr;
        int pid = SCARG(uap, pid);
        int signum = SCARG(uap, signum);
-       int error;
+       int error, cansig;
        int zombie = 0;
 
        if ((error = pledge_kill(cp, pid)) != 0)
@@ -582,7 +583,10 @@ sys_kill(struct proc *cp, void *v, register_t *retval)
                        else
                                zombie = 1;
                }
-               if (!cansignal(cp, pr, signum))
+               rw_enter_read(&proctreelk);
+               cansig = cansignal(cp, pr, signum);
+               rw_exit_read(&proctreelk);
+               if (!cansig)
                        return (EPERM);
 
                /* kill single process */
@@ -652,6 +656,7 @@ killpg1(struct proc *cp, int signum, int pgid, int all)
                /* 
                 * broadcast
                 */
+               rw_enter_read(&proctreelk);
                LIST_FOREACH(pr, &allprocess, ps_list) {
                        if (pr->ps_pid <= 1 ||
                            pr->ps_flags & (PS_SYSTEM | PS_NOBROADCASTKILL) ||
@@ -661,7 +666,9 @@ killpg1(struct proc *cp, int signum, int pgid, int all)
                        if (signum)
                                prsignal(pr, signum);
                }
+               rw_exit_read(&proctreelk);
        } else {
+               rw_enter_read(&proctreelk);
                if (pgid == 0)
                        /*
                         * zero pgid means send to my process group.
@@ -669,8 +676,10 @@ killpg1(struct proc *cp, int signum, int pgid, int all)
                        pgrp = cp->p_p->ps_pgrp;
                else {
                        pgrp = pgfind(pgid);
-                       if (pgrp == NULL)
+                       if (pgrp == NULL) {
+                               rw_exit_read(&proctreelk);
                                return (ESRCH);
+                       }
                }
                LIST_FOREACH(pr, &pgrp->pg_members, ps_pglist) {
                        if (pr->ps_pid <= 1 || pr->ps_flags & PS_SYSTEM ||
@@ -680,6 +689,7 @@ killpg1(struct proc *cp, int signum, int pgid, int all)
                        if (signum)
                                prsignal(pr, signum);
                }
+               rw_exit_read(&proctreelk);
        }
        return (nfound ? 0 : ESRCH);
 }
@@ -703,6 +713,12 @@ csignal(pid_t pgid, int signum, uid_t uid, uid_t euid)
        struct pgrp *pgrp;
        struct process *pr;
 
+       /*
+        * XXXPT `pg_members' needs to be protected but this can be called
+        * from interrupt context.
+        */
+       //rw_assert_wrlock(&proctreelk)
+
        if (pgid == 0)
                return;
        if (pgid < 0) {
@@ -741,6 +757,12 @@ pgsignal(struct pgrp *pgrp, int signum, int checkctty)
 {
        struct process *pr;
 
+       /*
+        * XXXPT `pg_members' needs to be protected but this can be called
+        * from interrupt context.
+        */
+       //rw_assert_wrlock(&proctreelk)
+
        if (pgrp)
                LIST_FOREACH(pr, &pgrp->pg_members, ps_pglist)
                        if (checkctty == 0 || pr->ps_flags & PS_CONTROLT)
@@ -1329,6 +1351,7 @@ proc_stop_sweep(void *v)
 {
        struct process *pr;
 
+       rw_enter_read(&proctreelk);
        LIST_FOREACH(pr, &allprocess, ps_list) {
                if ((pr->ps_flags & PS_STOPPED) == 0)
                        continue;
@@ -1338,6 +1361,7 @@ proc_stop_sweep(void *v)
                        prsignal(pr->ps_pptr, SIGCHLD);
                wakeup(pr->ps_pptr);
        }
+       rw_exit_read(&proctreelk);
 }
 
 /*
diff --git sys/kern/kern_sysctl.c sys/kern/kern_sysctl.c
index c98f9d53d72..144260428ef 100644
--- sys/kern/kern_sysctl.c
+++ sys/kern/kern_sysctl.c
@@ -1469,6 +1469,7 @@ sysctl_doproc(int *name, u_int namelen, char *where, 
size_t *sizep)
        if (where != NULL)
                kproc = malloc(sizeof(*kproc), M_TEMP, M_WAITOK);
 
+       rw_enter_read(&proctreelk);
        pr = LIST_FIRST(&allprocess);
        doingzomb = 0;
 again:
@@ -1507,6 +1508,7 @@ again:
                        break;
 
                case KERN_PROC_TTY:
+                       /* XXXPT proctreelk? */
                        if ((pr->ps_flags & PS_CONTROLT) == 0 ||
                            pr->ps_session->s_ttyp == NULL ||
                            pr->ps_session->s_ttyp->t_dev != (dev_t)arg)
@@ -1570,6 +1572,7 @@ again:
                doingzomb++;
                goto again;
        }
+       rw_exit_read(&proctreelk);
        if (where != NULL) {
                *sizep = dp - where;
                if (needed > *sizep) {
@@ -1599,6 +1602,8 @@ fill_kproc(struct process *pr, struct kinfo_proc *ki, 
struct proc *p,
        struct timespec ut, st;
        int isthread;
 
+       rw_assert_rdlock(&proctreelk);
+
        isthread = p != NULL;
        if (!isthread)
                p = pr->ps_mainproc;            /* XXX */
diff --git sys/kern/subr_prf.c sys/kern/subr_prf.c
index 3572e809d09..6cb6d62481e 100644
--- sys/kern/subr_prf.c
+++ sys/kern/subr_prf.c
@@ -362,6 +362,7 @@ uprintf(const char *fmt, ...)
        struct process *pr = curproc->p_p;
        va_list ap;
 
+       /* XXXPT proctreelk? */
        if (pr->ps_flags & PS_CONTROLT && pr->ps_session->s_ttyvp) {
                va_start(ap, fmt);
                kprintf(fmt, TOTTY, pr->ps_session->s_ttyp, NULL, ap);
diff --git sys/kern/sys_process.c sys/kern/sys_process.c
index 08bfb73bf5f..14bedae5d5b 100644
--- sys/kern/sys_process.c
+++ sys/kern/sys_process.c
@@ -276,13 +276,19 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t 
addr, int data)
        struct proc *t;                         /* target thread */
        struct process *tr;                     /* target process */
        int error = 0;
+       int proctree_locked;
        int s;
 
+       /* Lock proctree before looking up the process. */
+       rw_enter_write(&proctreelk);
+       proctree_locked = 1;
+
        switch (req) {
        case PT_TRACE_ME:
                /* Just set the trace flag. */
                tr = p->p_p;
                atomic_setbits_int(&tr->ps_flags, PS_TRACED);
+               rw_exit_write(&proctreelk);
                tr->ps_oppid = tr->ps_pptr->ps_pid;
                if (tr->ps_ptstat == NULL)
                        tr->ps_ptstat = malloc(sizeof(*tr->ps_ptstat),
@@ -447,6 +453,8 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t 
addr, int data)
                if (error)
                        goto fail;
 #endif
+               proctree_locked = 0;
+               rw_exit_write(&proctreelk);
                goto sendsig;
 
        case PT_DETACH:
@@ -486,7 +494,11 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t 
addr, int data)
                tr->ps_oppid = 0;
                atomic_clearbits_int(&tr->ps_flags, PS_TRACED|PS_WAITED);
 
+               proctree_locked = 0;
+               rw_exit_write(&proctreelk);
        sendsig:
+               KASSERT(proctree_locked == 0);
+
                memset(tr->ps_ptstat, 0, sizeof(*tr->ps_ptstat));
 
                /* Finally, deliver the requested signal (or none). */
@@ -507,6 +519,8 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t 
addr, int data)
 
                /* just send the process a KILL signal. */
                data = SIGKILL;
+               proctree_locked = 0;
+               rw_exit_write(&proctreelk);
                goto sendsig;   /* in PT_CONTINUE, above. */
 
        case PT_ATTACH:
@@ -523,6 +537,8 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t 
addr, int data)
                tr->ps_oppid = tr->ps_pptr->ps_pid;
                if (tr->ps_pptr != p->p_p)
                        proc_reparent(tr, p->p_p);
+               proctree_locked = 0;
+               rw_exit_write(&proctreelk);
                if (tr->ps_ptstat == NULL)
                        tr->ps_ptstat = malloc(sizeof(*tr->ps_ptstat),
                            M_SUBPROC, M_WAITOK);
@@ -534,6 +550,8 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t 
addr, int data)
        }
 
 fail:
+       if (proctree_locked)
+               rw_exit_write(&proctreelk);
        return error;
 }
 
diff --git sys/kern/syscalls.master sys/kern/syscalls.master
index c9411db3ba1..46f8988c84e 100644
--- sys/kern/syscalls.master
+++ sys/kern/syscalls.master
@@ -107,7 +107,7 @@
 36     STD             { void sys_sync(void); }
 37     OBSOL           o58_kill
 38     STD             { int sys_stat(const char *path, struct stat *ub); }
-39     STD             { pid_t sys_getppid(void); }
+39     STD NOLOCK      { pid_t sys_getppid(void); }
 40     STD             { int sys_lstat(const char *path, struct stat *ub); }
 41     STD             { int sys_dup(int fd); }
 42     STD             { int sys_fstatat(int fd, const char *path, \
@@ -127,7 +127,7 @@
 47     STD NOLOCK      { gid_t sys_getgid(void); }
 48     STD NOLOCK      { int sys_sigprocmask(int how, sigset_t mask); }
 49     OBSOL           ogetlogin
-50     STD             { int sys_setlogin(const char *namebuf); }
+50     STD NOLOCK      { int sys_setlogin(const char *namebuf); }
 #ifdef ACCOUNTING
 51     STD             { int sys_acct(const char *path); }
 #else
@@ -185,7 +185,7 @@
                            gid_t *gidset); }
 80     STD             { int sys_setgroups(int gidsetsize, \
                            const gid_t *gidset); }
-81     STD             { int sys_getpgrp(void); }
+81     STD NOLOCK      { int sys_getpgrp(void); }
 82     STD             { int sys_setpgid(pid_t pid, pid_t pgid); }
 83     STD NOLOCK      { int sys_futex(uint32_t *f, int op, int val, \
                            const struct timespec *timeout, uint32_t *g); }
@@ -273,7 +273,7 @@
 139    OBSOL           4.2 sigreturn
 140    STD             { int sys_adjtime(const struct timeval *delta, \
                            struct timeval *olddelta); }
-141    STD             { int sys_getlogin_r(char *namebuf, u_int namelen); }
+141    STD NOLOCK      { int sys_getlogin_r(char *namebuf, u_int namelen); }
 142    OBSOL           ogethostid
 143    OBSOL           osethostid
 144    OBSOL           ogetrlimit
diff --git sys/kern/tty.c sys/kern/tty.c
index b9fec5b6bb9..95f8f8fb1f0 100644
--- sys/kern/tty.c
+++ sys/kern/tty.c
@@ -836,7 +836,9 @@ ttioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, 
struct proc *p)
        case TIOCGSID:                  /* get sid of tty */
                if (!isctty(pr, tp))
                        return (ENOTTY);
+               rw_enter_read(&proctreelk);
                *(int *)data = tp->t_session->s_leader->ps_pid;
+               rw_exit_read(&proctreelk);
                break;
 #ifdef TIOCHPCL
        case TIOCHPCL:                  /* hang up on last close */
@@ -892,10 +894,12 @@ ttioctl(struct tty *tp, u_long cmd, caddr_t data, int 
flag, struct proc *p)
                                tp->t_cflag = t->c_cflag;
                                tp->t_ispeed = t->c_ispeed;
                                tp->t_ospeed = t->c_ospeed;
+                               rw_enter_read(&proctreelk);
                                if (t->c_ospeed == 0 && tp->t_session &&
                                    tp->t_session->s_leader)
                                        prsignal(tp->t_session->s_leader,
                                            SIGHUP);
+                               rw_exit_read(&proctreelk);
                        }
                        ttsetwater(tp);
                }
@@ -973,10 +977,13 @@ ttioctl(struct tty *tp, u_long cmd, caddr_t data, int 
flag, struct proc *p)
                break;
        case TIOCSCTTY:                 /* become controlling tty */
                /* Session ctty vnode pointer set in vnode layer. */
+               rw_enter_write(&proctreelk);
                if (!SESS_LEADER(pr) ||
                    ((pr->ps_session->s_ttyvp || tp->t_session) &&
-                    (tp->t_session != pr->ps_session)))
+                    (tp->t_session != pr->ps_session))) {
+                       rw_exit_write(&proctreelk);
                        return (EPERM);
+               }
                if (tp->t_session)
                        SESSRELE(tp->t_session);
                SESSHOLD(pr->ps_session);
@@ -984,17 +991,25 @@ ttioctl(struct tty *tp, u_long cmd, caddr_t data, int 
flag, struct proc *p)
                tp->t_pgrp = pr->ps_pgrp;
                pr->ps_session->s_ttyp = tp;
                atomic_setbits_int(&pr->ps_flags, PS_CONTROLT);
+               rw_exit_write(&proctreelk);
                break;
        case TIOCSPGRP: {               /* set pgrp of tty */
-               struct pgrp *pgrp = pgfind(*(int *)data);
+               struct pgrp *pgrp;
 
+               rw_enter_write(&proctreelk);
+               pgrp = pgfind(*(int *)data);
                if (!isctty(pr, tp))
-                       return (ENOTTY);
+                       error = ENOTTY;
                else if (pgrp == NULL)
-                       return (EINVAL);
+                       error = EINVAL;
                else if (pgrp->pg_session != pr->ps_session)
-                       return (EPERM);
-               tp->t_pgrp = pgrp;
+                       error = EPERM;
+               else {
+                       tp->t_pgrp = pgrp;
+                       error = 0;
+               }
+               rw_exit_write(&proctreelk);
+               return (error);
                break;
        }
        case TIOCSTAT:                  /* get load avg stats */
@@ -1385,6 +1400,11 @@ ttymodem(struct tty *tp, int flag)
                CLR(tp->t_state, TS_CARR_ON);
                if (ISSET(tp->t_state, TS_ISOPEN) &&
                    !ISSET(tp->t_cflag, CLOCAL)) {
+                       /*
+                        * XXXPT `s_leader' needs to be protected but this
+                        * can be called from interrupt context.
+                        */
+                       //rw_assert_wrlock(&proctreelk)
                        if (tp->t_session && tp->t_session->s_leader)
                                prsignal(tp->t_session->s_leader, SIGHUP);
                        ttyflush(tp, FREAD | FWRITE);
@@ -1414,6 +1434,11 @@ nullmodem(struct tty *tp, int flag)
                CLR(tp->t_state, TS_CARR_ON);
                if (ISSET(tp->t_state, TS_ISOPEN) &&
                    !ISSET(tp->t_cflag, CLOCAL)) {
+                       /*
+                        * XXXPT `s_leader' needs to be protected but this
+                        * can be called from interrupt context.
+                        */
+                       //rw_assert_wrlock(&proctreelk)
                        if (tp->t_session && tp->t_session->s_leader)
                                prsignal(tp->t_session->s_leader, SIGHUP);
                        ttyflush(tp, FREAD | FWRITE);
@@ -2138,6 +2163,12 @@ ttyinfo(struct tty *tp)
        if (ttycheckoutq(tp,0) == 0)
                return;
 
+       /*
+        * XXXPT `pg_members' needs to be protected but this can be called
+        * from interrupt context.
+        */
+       //rw_assert_wrlock(&proctreelk)
+
        /* Print load average. */
        tmp = (averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT;
        ttyprintf(tp, "load: %d.%02d ", tmp / 100, tmp % 100);
diff --git sys/kern/tty_tty.c sys/kern/tty_tty.c
index 0f88b8d9b27..5a94d996497 100644
--- sys/kern/tty_tty.c
+++ sys/kern/tty_tty.c
@@ -45,6 +45,9 @@
 #include <sys/fcntl.h>
 
 
+/*
+ * XXXPT Must hold `proctreelk' to dereference `ps_session'.
+ */
 #define cttyvp(p) \
        ((p)->p_p->ps_flags & PS_CONTROLT ? \
            (p)->p_p->ps_session->s_ttyvp : NULL)
@@ -105,11 +108,14 @@ cttyioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, 
struct proc *p)
        if (cmd == TIOCSCTTY)           /* XXX */
                return (EINVAL);
        if (cmd == TIOCNOTTY) {
+               rw_enter_read(&proctreelk);
                if (!SESS_LEADER(p->p_p)) {
                        atomic_clearbits_int(&p->p_p->ps_flags, PS_CONTROLT);
-                       return (0);
+                       error = 0;
                } else
-                       return (EINVAL);
+                       error = EINVAL;
+               rw_exit_read(&proctreelk);
+               return error;
        }
        switch (cmd) {
        case TIOCSETVERAUTH:
@@ -118,15 +124,19 @@ cttyioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, 
struct proc *p)
                secs = *(int *)addr;
                if (secs < 1 || secs > 3600)
                        return EINVAL;
+               rw_enter_write(&proctreelk);
                sess = p->p_p->ps_pgrp->pg_session;
                sess->s_verauthuid = p->p_ucred->cr_ruid;
                sess->s_verauthppid = p->p_p->ps_pptr->ps_pid;
                timeout_add_sec(&sess->s_verauthto, secs);
+               rw_exit_write(&proctreelk);
                return 0;
        case TIOCCLRVERAUTH:
+               rw_enter_write(&proctreelk);
                sess = p->p_p->ps_pgrp->pg_session;
                timeout_del(&sess->s_verauthto);
                zapverauth(sess);
+               rw_exit_write(&proctreelk);
                return 0;
        case TIOCCHKVERAUTH:
                /*
@@ -136,11 +146,15 @@ cttyioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, 
struct proc *p)
                 * Nevertheless, the checks reflect the original intention;
                 * namely, that it be the same user using the same shell.
                 */
+               rw_enter_read(&proctreelk);
                sess = p->p_p->ps_pgrp->pg_session;
                if (sess->s_verauthuid == p->p_ucred->cr_ruid &&
                    sess->s_verauthppid == p->p_p->ps_pptr->ps_pid)
-                       return 0;
-               return EPERM;
+                       error = 0;
+               else
+                       error = EPERM;
+               rw_exit_read(&proctreelk);
+               return error;
        }
        return (VOP_IOCTL(ttyvp, cmd, addr, flag, NOCRED, p));
 }
diff --git sys/sys/proc.h sys/sys/proc.h
index edcaf7cb960..2205f111cea 100644
--- sys/sys/proc.h
+++ sys/sys/proc.h
@@ -44,6 +44,7 @@
 #include <sys/selinfo.h>               /* For struct selinfo */
 #include <sys/syslimits.h>             /* For LOGIN_NAME_MAX */
 #include <sys/queue.h>
+#include <sys/rwlock.h>                        /* For struct rwlock */
 #include <sys/timeout.h>               /* For struct timeout */
 #include <sys/event.h>                 /* For struct klist */
 #include <sys/mutex.h>                 /* For struct mutex */
@@ -54,17 +55,26 @@
 #define __need_process
 #endif
 
+/*
+ * Locks used to protect struct members in this file:
+ *     I       immutable after creation
+ *     t       proctreelk
+ *
+ * If multiple locks are listed then all are required for writes,
+ * but any one of them is sufficient for reads.
+ */
+
 /*
  * One structure allocated per session.
  */
 struct process;
 struct session {
        int     s_count;                /* Ref cnt; pgrps in session. */
-       struct  process *s_leader;      /* Session leader. */
+       struct  process *s_leader;      /* [t] Session leader. */
        struct  vnode *s_ttyvp;         /* Vnode of controlling terminal. */
-       struct  tty *s_ttyp;            /* Controlling terminal. */
-       char    s_login[LOGIN_NAME_MAX];        /* Setlogin() name. */
-       pid_t   s_verauthppid;
+       struct  tty *s_ttyp;            /* [t] Controlling terminal. */
+       char    s_login[LOGIN_NAME_MAX];/* [t] setlogin() name. */
+       pid_t   s_verauthppid;          /* TIOCSETVERAUTH info */
        uid_t   s_verauthuid;
        struct timeout s_verauthto;
 };
@@ -75,10 +85,10 @@ void zapverauth(/* struct session */ void *);
  * One structure allocated per process group.
  */
 struct pgrp {
-       LIST_ENTRY(pgrp) pg_hash;       /* Hash chain. */
-       LIST_HEAD(, process) pg_members;/* Pointer to pgrp members. */
-       struct  session *pg_session;    /* Pointer to session. */
-       pid_t   pg_id;                  /* Pgrp id. */
+       LIST_ENTRY(pgrp) pg_hash;       /* [t] Hash chain. */
+       LIST_HEAD(, process) pg_members;/* [t] Pointer to pgrp members. */
+       struct  session *pg_session;    /* [I] Pointer to session. */
+       pid_t   pg_id;                  /* [I] Pgrp id. */
        int     pg_jobc;        /* # procs qualifying pgrp for job control */
 };
 
@@ -156,17 +166,17 @@ struct process {
        LIST_ENTRY(process) ps_list;    /* List of all processes. */
        TAILQ_HEAD(,proc) ps_threads;   /* Threads in this process. */
 
-       LIST_ENTRY(process) ps_pglist;  /* List of processes in pgrp. */
-       struct  process *ps_pptr;       /* Pointer to parent process. */
-       LIST_ENTRY(process) ps_sibling; /* List of sibling processes. */
-       LIST_HEAD(, process) ps_children;/* Pointer to list of children. */
+       LIST_ENTRY(process) ps_pglist;  /* [t] List of processes in pgrp. */
+       struct  process *ps_pptr;       /* [t] Pointer to parent process. */
+       LIST_ENTRY(process) ps_sibling; /* [t] List of sibling processes. */
+       LIST_HEAD(, process) ps_children;/* [t] Pointer to list of children. */
        LIST_ENTRY(process) ps_hash;    /* Hash chain. */
 
        struct  sigacts *ps_sigacts;    /* Signal actions, state */
        struct  vnode *ps_textvp;       /* Vnode of executable. */
        struct  filedesc *ps_fd;        /* Ptr to open files structure */
        struct  vmspace *ps_vmspace;    /* Address space */
-       pid_t   ps_pid;                 /* Process identifier. */
+       pid_t   ps_pid;                 /* [I] Process identifier. */
 
 /* The following fields are all zeroed upon creation in process_new. */
 #define        ps_startzero    ps_klist
@@ -180,7 +190,7 @@ struct process {
        struct  vnode *ps_tracevp;      /* Trace to vnode. */
        struct  ucred *ps_tracecred;    /* Creds for writing trace */
 
-       pid_t   ps_oppid;               /* Save parent pid during ptrace. */
+       pid_t   ps_oppid;               /* [t] Save parent pid during ptrace. */
        int     ps_ptmask;              /* Ptrace event mask */
        struct  ptrace_state *ps_ptstat;/* Ptrace state */
 
@@ -197,7 +207,7 @@ struct process {
 /* The following fields are all copied upon creation in process_new. */
 #define        ps_startcopy    ps_limit
        struct  plimit *ps_limit;       /* Process limits. */
-       struct  pgrp *ps_pgrp;          /* Pointer to process group. */
+       struct  pgrp *ps_pgrp;          /* [t] Pointer to process group. */
        struct  emul *ps_emul;          /* Emulation information */
 
        char    ps_comm[MAXCOMLEN+1];
@@ -475,6 +485,7 @@ LIST_HEAD(processlist, process);
 extern struct processlist allprocess;  /* List of all processes. */
 extern struct processlist zombprocess; /* List of zombie processes. */
 extern struct proclist allproc;                /* List of all threads. */
+extern struct rwlock proctreelk;       /* parent/child, pgrp, session */
 
 extern struct process *initprocess;    /* Process slot for init. */
 extern struct proc *reaperproc;                /* Thread slot for reaper. */
diff --git sys/sys/ucred.h sys/sys/ucred.h
index 03e023dc04a..9a6cb0095f4 100644
--- sys/sys/ucred.h
+++ sys/sys/ucred.h
@@ -68,12 +68,11 @@ struct xucred {
 };
 
 #ifdef _KERNEL
-#define        crhold(cr)      (cr)->cr_ref++
-
 int            crfromxucred(struct ucred *, const struct xucred *);
 void           crset(struct ucred *, const struct ucred *);
 struct ucred   *crcopy(struct ucred *cr);
 struct ucred   *crdup(struct ucred *cr);
+void           crhold(struct ucred *cr);
 void           crfree(struct ucred *cr);
 struct ucred   *crget(void);
 int            suser(struct proc *p);

Reply via email to