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.


Philip Guenther


Index: sys/proc.h
===================================================================
RCS file: /data/src/openbsd/src/sys/sys/proc.h,v
retrieving revision 1.240
diff -u -p -r1.240 proc.h
--- sys/proc.h  29 Aug 2017 02:51:27 -0000      1.240
+++ sys/proc.h  1 Sep 2017 05:48:15 -0000
@@ -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 */
@@ -55,16 +56,25 @@
 #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];/* setlogin() name. */
+       pid_t   s_verauthppid;          /* TIOCSETVERAUTH info */
        uid_t   s_verauthuid;
        struct timeout s_verauthto;
 };
@@ -75,10 +85,10 @@ void zapverauth(/* struct session */ voi
  * 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];
@@ -407,6 +417,7 @@ struct uidinfo {
 };
 
 struct uidinfo *uid_find(uid_t);
+extern struct rwlock uidinfolk;                /* locked when uid_find() 
returns */
 
 /*
  * We use process IDs <= PID_MAX; PID_MAX + 1 must also fit in a pid_t,
@@ -473,6 +484,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. */
@@ -495,12 +507,12 @@ void      proc_printit(struct proc *p, const 
     int (*pr)(const char *, ...));
 
 int    chgproccnt(uid_t uid, int diff);
-int    enterpgrp(struct process *, pid_t, struct pgrp *, struct session *);
-void   fixjobc(struct process *, struct pgrp *, int);
+void   enternewpgrp(struct process *, struct pgrp *, struct session *);
+void   enterthispgrp(struct process *, struct pgrp *);
 int    inferior(struct process *, struct process *);
 void   leavepgrp(struct process *);
+void   killjobc(struct process *);
 void   preempt(void);
-void   pgdelete(struct pgrp *);
 void   procinit(void);
 void   resetpriority(struct proc *);
 void   setrunnable(struct proc *);
Index: sys/ucred.h
===================================================================
RCS file: /data/src/openbsd/src/sys/sys/ucred.h,v
retrieving revision 1.11
diff -u -p -r1.11 ucred.h
--- sys/ucred.h 2 Mar 2015 20:46:50 -0000       1.11
+++ sys/ucred.h 4 Sep 2017 03:19:13 -0000
@@ -67,8 +67,10 @@ struct xucred {
        gid_t   cr_groups[NGROUPS_MAX]; /* groups */
 };
 
-#ifdef _KERNEL
-#define        crhold(cr)      (cr)->cr_ref++
+#if defined(_KERNEL) && !defined(_STANDALONE)
+#include <sys/atomic.h>
+
+#define        crhold(cr)      atomic_inc_int(&(cr)->cr_ref)
 
 #define SUSER_NOACCT   0x1     /* don't mark accounting flags */
 
@@ -81,6 +83,6 @@ struct ucred  *crget(void);
 int            suser(struct proc *p, u_int flags);
 int            suser_ucred(struct ucred *cred);
 
-#endif /* _KERNEL */
+#endif /* _KERNEL && !_STANDALONE */
 
 #endif /* !_SYS_UCRED_H_ */
Index: kern/exec_elf.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/exec_elf.c,v
retrieving revision 1.141
diff -u -p -r1.141 exec_elf.c
--- kern/exec_elf.c     7 Sep 2017 11:32:14 -0000       1.141
+++ kern/exec_elf.c     9 Sep 2017 18:20:47 -0000
@@ -1168,12 +1168,14 @@ coredump_notes_elf(struct proc *p, void 
                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;
Index: kern/kern_acct.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/kern_acct.c,v
retrieving revision 1.33
diff -u -p -r1.33 kern_acct.c
--- kern/kern_acct.c    21 Jan 2017 05:42:03 -0000      1.33
+++ kern/kern_acct.c    29 Jul 2017 21:11:23 -0000
@@ -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;
Index: kern/kern_exec.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/kern_exec.c,v
retrieving revision 1.189
diff -u -p -r1.189 kern_exec.c
--- kern/kern_exec.c    29 Aug 2017 02:51:27 -0000      1.189
+++ kern/kern_exec.c    29 Aug 2017 16:02:01 -0000
@@ -503,9 +503,11 @@ sys_execve(struct proc *p, void *v, regi
 
        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);
        }
 
        /*
Index: kern/kern_exit.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/kern_exit.c,v
retrieving revision 1.161
diff -u -p -r1.161 kern_exit.c
--- kern/kern_exit.c    29 Aug 2017 02:51:27 -0000      1.161
+++ kern/kern_exit.c    29 Aug 2017 16:02:01 -0000
@@ -118,7 +118,6 @@ exit1(struct proc *p, int rv, int flags)
 {
        struct process *pr, *qr, *nqr;
        struct rusage *rup;
-       struct vnode *ovp;
        
        atomic_setbits_int(&p->p_flag, P_WEXIT);
 
@@ -185,44 +184,7 @@ exit1(struct proc *p, int rv, int flags)
 #ifdef SYSVSEM
                semexit(pr);
 #endif
-               if (SESS_LEADER(pr)) {
-                       struct session *sp = pr->ps_session;
-
-                       if (sp->s_ttyvp) {
-                               /*
-                                * Controlling process.
-                                * Signal foreground pgrp,
-                                * drain controlling terminal
-                                * and revoke access to controlling terminal.
-                                */
-                               if (sp->s_ttyp->t_session == sp) {
-                                       if (sp->s_ttyp->t_pgrp)
-                                               pgsignal(sp->s_ttyp->t_pgrp,
-                                                   SIGHUP, 1);
-                                       ttywait(sp->s_ttyp);
-                                       /*
-                                        * The tty could have been revoked
-                                        * if we blocked.
-                                        */
-                                       if (sp->s_ttyvp)
-                                               VOP_REVOKE(sp->s_ttyvp,
-                                                   REVOKEALL);
-                               }
-                               ovp = sp->s_ttyvp;
-                               sp->s_ttyvp = NULL;
-                               if (ovp)
-                                       vrele(ovp);
-                               /*
-                                * s_ttyp is not zero'd; we use this to
-                                * indicate that the session once had a
-                                * controlling terminal.  (for logging and
-                                * informational purposes)
-                                */
-                       }
-                       sp->s_leader = NULL;
-               }
-               fixjobc(pr, pr->ps_pgrp, 0);
-
+               killjobc(pr);
 #ifdef ACCOUNTING
                acct_process(p);
 #endif
@@ -251,11 +213,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);
 
@@ -303,6 +261,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);
@@ -331,9 +290,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);
                }
 
                /*
@@ -351,6 +314,11 @@ exit1(struct proc *p, int rv, int flags)
        }
 
        /*
+        * NOTE: WE ARE NO LONGER ALLOWED TO SLEEP!
+        */
+       p->p_stat = SDEAD;
+
+       /*
         * Other substructures are freed from reaper and wait().
         */
 
@@ -451,6 +419,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);
@@ -458,6 +427,7 @@ 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 */
                                process_zap(pr);
@@ -511,6 +481,7 @@ dowait4(struct proc *q, pid_t pid, int *
                return (EINVAL);
 
 loop:
+       rw_enter_write(&proctreelk);
        nfound = 0;
        LIST_FOREACH(pr, &q->p_p->ps_children, ps_sibling) {
                if ((pr->ps_flags & PS_NOZOMBIE) ||
@@ -536,6 +507,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);
@@ -559,6 +531,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)) {
@@ -569,16 +542,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;
 }
@@ -589,6 +567,8 @@ proc_finish_wait(struct proc *waiter, st
        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.
@@ -600,6 +580,7 @@ proc_finish_wait(struct proc *waiter, st
                proc_reparent(pr, tr);
                prsignal(tr, SIGCHLD);
                wakeup(tr);
+               rw_exit_write(&proctreelk);
        } else {
                scheduler_wait_hook(waiter, p);
                p->p_xstat = 0;
@@ -618,6 +599,7 @@ void
 proc_reparent(struct process *child, struct process *parent)
 {
 
+       rw_assert_wrlock(&proctreelk);
        if (child->ps_pptr == parent)
                return;
 
@@ -632,12 +614,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.
Index: kern/kern_fork.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/kern_fork.c,v
retrieving revision 1.200
diff -u -p -r1.200 kern_fork.c
--- kern/kern_fork.c    27 Sep 2017 06:45:00 -0000      1.200
+++ kern/kern_fork.c    15 Oct 2017 04:28:16 -0000
@@ -225,7 +225,6 @@ process_new(struct proc *p, struct proce
            (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;
@@ -270,10 +269,9 @@ process_new(struct proc *p, struct proce
        /* 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);
+       pr->ps_pid = allocpid();
        LIST_INSERT_HEAD(&allprocess, pr, ps_list);
 
        return pr;
@@ -374,6 +372,7 @@ fork1(struct proc *curp, int flags, void
 
        /*
         * 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);
@@ -448,6 +447,7 @@ fork1(struct proc *curp, int flags, void
                        pr->ps_ptstat->pe_other_pid = curpr->ps_pid;
                }
        }
+       rw_exit_write(&proctreelk);
 
        /*
         * For new processes, set accounting bits and mark as complete.
Index: kern/kern_ktrace.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/kern_ktrace.c,v
retrieving revision 1.92
diff -u -p -r1.92 kern_ktrace.c
--- kern/kern_ktrace.c  12 Aug 2017 00:03:10 -0000      1.92
+++ kern/kern_ktrace.c  12 Aug 2017 00:09:06 -0000
@@ -448,6 +448,7 @@ doktrace(struct vnode *vp, int ops, int 
        /*
         * do it
         */
+       rw_enter_read(&proctreelk);
        if (pid < 0) {
                /*
                 * by process group
@@ -455,7 +456,7 @@ doktrace(struct vnode *vp, int ops, int 
                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 
                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 
        }
        if (!ret)
                error = EPERM;
+done2:
+       rw_exit_read(&proctreelk);
 done:
        return (error);
 }
@@ -578,6 +581,8 @@ ktrsetchildren(struct proc *curp, struct
 {
        struct process *pr;
        int ret = 0;
+
+       rw_assert_rdlock(&proctreelk);
 
        pr = top;
        for (;;) {
Index: kern/kern_proc.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/kern_proc.c,v
retrieving revision 1.77
diff -u -p -r1.77 kern_proc.c
--- kern/kern_proc.c    29 Sep 2017 09:36:04 -0000      1.77
+++ kern/kern_proc.c    15 Oct 2017 04:28:16 -0000
@@ -40,6 +40,7 @@
 #include <sys/acct.h>
 #include <sys/wait.h>
 #include <sys/file.h>
+#include <sys/rwlock.h>
 #include <ufs/ufs/quota.h>
 #include <sys/uio.h>
 #include <sys/malloc.h>
@@ -48,6 +49,7 @@
 #include <sys/tty.h>
 #include <sys/signalvar.h>
 #include <sys/pool.h>
+#include <sys/vnode.h>
 
 #define        UIHASH(uid)     (&uihashtbl[(uid) & uihash])
 LIST_HEAD(uihashhead, uidinfo) *uihashtbl;
@@ -65,6 +67,8 @@ u_long pgrphash;
 struct processlist allprocess;
 struct processlist zombprocess;
 struct proclist allproc;
+struct rwlock proctreelk;
+struct rwlock uidinfolk;
 
 struct pool proc_pool;
 struct pool process_pool;
@@ -73,6 +77,9 @@ struct pool ucred_pool;
 struct pool pgrp_pool;
 struct pool session_pool;
 
+void   pgdelete(struct pgrp *);
+void   fixjobc(struct process *, struct pgrp *, int);
+
 static void orphanpg(struct pgrp *);
 #ifdef DEBUG
 void pgrpdump(void);
@@ -87,7 +94,8 @@ procinit(void)
        LIST_INIT(&allprocess);
        LIST_INIT(&zombprocess);
        LIST_INIT(&allproc);
-
+       rw_init(&proctreelk, "proctree");
+       rw_init(&uidinfolk, "uidinfo");
 
        tidhashtbl = hashinit(maxthread / 4, M_PROC, M_NOWAIT, &tidhash);
        pidhashtbl = hashinit(maxprocess / 4, M_PROC, M_NOWAIT, &pidhash);
@@ -110,6 +118,10 @@ procinit(void)
            PR_WAITOK, "sessionpl", NULL);
 }
 
+/*
+ * This returns with uidinfolk held: caller must unlock it after
+ * making whatever change they needed.
+ */
 struct uidinfo *
 uid_find(uid_t uid)
 {
@@ -117,16 +129,20 @@ uid_find(uid_t uid)
        struct uihashhead *uipp;
 
        uipp = UIHASH(uid);
+       rw_enter_write(&uidinfolk);
        LIST_FOREACH(uip, uipp, ui_hash)
                if (uip->ui_uid == uid)
                        break;
        if (uip)
                return (uip);
+       rw_exit_write(&uidinfolk);
        nuip = malloc(sizeof(*nuip), M_PROC, M_WAITOK|M_ZERO);
+       rw_enter_write(&uidinfolk);
        LIST_FOREACH(uip, uipp, ui_hash)
                if (uip->ui_uid == uid)
                        break;
        if (uip) {
+               /* XXX unlock uidinfolk across the free()? */
                free(nuip, M_PROC, sizeof(*nuip));
                return (uip);
        }
@@ -144,12 +160,14 @@ int
 chgproccnt(uid_t uid, int diff)
 {
        struct uidinfo *uip;
+       long count;
 
        uip = uid_find(uid);
-       uip->ui_proccnt += diff;
-       if (uip->ui_proccnt < 0)
+       count = (uip->ui_proccnt += diff);
+       rw_exit_write(&uidinfolk);
+       if (count < 0)
                panic("chgproccnt: procs < 0");
-       return (uip->ui_proccnt);
+       return count;
 }
 
 /*
@@ -159,6 +177,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);
@@ -201,6 +220,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);
@@ -222,67 +242,58 @@ zombiefind(pid_t pid)
 }
 
 /*
- * Move p to a new or existing process group (and session)
- * Caller provides a pre-allocated pgrp and session that should
- * be freed if they are not used.
- * XXX need proctree lock
+ * Move process to a new process group.  If a session is provided
+ * then it's a new session to contain this process group; otherwise
+ * the process is staying within its existing session.
  */
-int
-enterpgrp(struct process *pr, pid_t pgid, struct pgrp *newpgrp,
-    struct session *newsess)
+void
+enternewpgrp(struct process *pr, struct pgrp *pgrp, struct session *newsess)
 {
-       struct pgrp *pgrp = pgfind(pgid);
+       rw_assert_wrlock(&proctreelk);
 
 #ifdef DIAGNOSTIC
-       if (pgrp != NULL && newsess)    /* firewalls */
-               panic("enterpgrp: setsid into non-empty pgrp");
        if (SESS_LEADER(pr))
-               panic("enterpgrp: session leader attempted setpgrp");
+               panic("%s: session leader attempted setpgrp", __func__);
 #endif
-       if (pgrp == NULL) {
+
+       if (newsess != NULL) {
                /*
-                * new process group
+                * New session.  Initialize it completely
                 */
+               timeout_set(&newsess->s_verauthto, zapverauth, newsess);
+               newsess->s_leader = pr;
+               newsess->s_count = 1;
+               newsess->s_ttyvp = NULL;
+               newsess->s_ttyp = NULL;
+               memcpy(newsess->s_login, pr->ps_session->s_login,
+                   sizeof(newsess->s_login));
+               atomic_clearbits_int(&pr->ps_flags, PS_CONTROLT);
+               pgrp->pg_session = newsess;
 #ifdef DIAGNOSTIC
-               if (pr->ps_pid != pgid)
-                       panic("enterpgrp: new pgrp and pid != pgid");
-#endif
-
-               pgrp = newpgrp;
-               if (newsess) {
-                       /*
-                        * new session
-                        */
-                       newsess->s_leader = pr;
-                       newsess->s_count = 1;
-                       newsess->s_ttyvp = NULL;
-                       newsess->s_ttyp = NULL;
-                       memcpy(newsess->s_login, pr->ps_session->s_login,
-                           sizeof(newsess->s_login));
-                       atomic_clearbits_int(&pr->ps_flags, PS_CONTROLT);
-                       pgrp->pg_session = newsess;
-#ifdef DIAGNOSTIC
-                       if (pr != curproc->p_p)
-                               panic("enterpgrp: mksession but not curproc");
+               if (pr != curproc->p_p)
+                       panic("%s: mksession but not curproc", __func__);
 #endif
-               } else {
-                       pgrp->pg_session = pr->ps_session;
-                       pgrp->pg_session->s_count++;
-               }
-               pgrp->pg_id = pgid;
-               LIST_INIT(&pgrp->pg_members);
-               LIST_INSERT_HEAD(PGRPHASH(pgid), pgrp, pg_hash);
-               pgrp->pg_jobc = 0;
-       } else if (pgrp == pr->ps_pgrp) {
-               if (newsess)
-                       pool_put(&session_pool, newsess);
-               pool_put(&pgrp_pool, newpgrp);
-               return (0);
        } else {
-               if (newsess)
-                       pool_put(&session_pool, newsess);
-               pool_put(&pgrp_pool, newpgrp);
+               pgrp->pg_session = pr->ps_session;
+               pgrp->pg_session->s_count++;
        }
+       pgrp->pg_id = pr->ps_pid;
+       LIST_INIT(&pgrp->pg_members);
+       LIST_INSERT_HEAD(PGRPHASH(pr->ps_pid), pgrp, pg_hash);
+       pgrp->pg_jobc = 0;
+
+       enterthispgrp(pr, pgrp);
+}
+
+/*
+ * move process to an existing process group
+ */
+void
+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.
@@ -290,14 +301,14 @@ enterpgrp(struct process *pr, pid_t pgid
         * could reach 0 spuriously during the first call.
         */
        fixjobc(pr, pgrp, 1);
-       fixjobc(pr, pr->ps_pgrp, 0);
+       fixjobc(pr, savepgrp, 0);
 
        LIST_REMOVE(pr, ps_pglist);
-       if (LIST_EMPTY(&pr->ps_pgrp->pg_members))
-               pgdelete(pr->ps_pgrp);
        pr->ps_pgrp = pgrp;
+
        LIST_INSERT_HEAD(&pgrp->pg_members, pr, ps_pglist);
-       return (0);
+       if (LIST_EMPTY(&savepgrp->pg_members))
+               pgdelete(savepgrp);
 }
 
 /*
@@ -307,6 +318,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);
@@ -322,6 +334,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;
@@ -355,6 +368,8 @@ fixjobc(struct process *pr, struct pgrp 
        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.
@@ -383,6 +398,52 @@ fixjobc(struct process *pr, struct pgrp 
                }
 }
 
+void
+killjobc(struct process *pr)
+{
+       rw_enter_write(&proctreelk);
+       if (SESS_LEADER(pr)) {
+               struct session *sp = pr->ps_session;
+
+               if (sp->s_ttyvp != NULL) {
+                       struct vnode *ovp;
+
+                       /*
+                        * Controlling process.
+                        * Signal foreground pgrp,
+                        * drain controlling terminal
+                        * and revoke access to controlling terminal.
+                        */
+                       if (sp->s_ttyp->t_session == sp) {
+                               if (sp->s_ttyp->t_pgrp)
+                                       pgsignal(sp->s_ttyp->t_pgrp,
+                                           SIGHUP, 1);
+                               ttywait(sp->s_ttyp);
+                               /*
+                                * The tty could have been revoked
+                                * if we blocked.
+                                */
+                               if (sp->s_ttyvp)
+                                       VOP_REVOKE(sp->s_ttyvp,
+                                           REVOKEALL);
+                       }
+                       ovp = sp->s_ttyvp;
+                       sp->s_ttyvp = NULL;
+                       if (ovp != NULL)
+                               vrele(ovp);
+                       /*
+                        * s_ttyp is not zero'd; we use this to
+                        * indicate that the session once had a
+                        * controlling terminal.  (for logging and
+                        * informational purposes)
+                        */
+               }
+               sp->s_leader = NULL;
+       }
+       fixjobc(pr, pr->ps_pgrp, 0);
+       rw_exit_write(&proctreelk);
+}
+
 /* 
  * A process group has become orphaned;
  * if there are any stopped processes in the group,
@@ -393,6 +454,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) {
Index: kern/kern_prot.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/kern_prot.c,v
retrieving revision 1.71
diff -u -p -r1.71 kern_prot.c
--- kern/kern_prot.c    27 Sep 2017 06:45:00 -0000      1.71
+++ kern/kern_prot.c    15 Oct 2017 04:28:16 -0000
@@ -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, 
                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,7 +136,9 @@ sys_getsid(struct proc *curp, void *v, r
                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)
                goto found;
        if ((targpr = prfind(SCARG(uap, pid))) == NULL)
@@ -135,9 +148,12 @@ sys_getsid(struct proc *curp, void *v, r
 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;
+       rw_exit_read(&proctreelk);
+
+       return error;
 }
 
 int
@@ -222,15 +238,17 @@ sys_setsid(struct proc *p, void *v, regi
        pid_t pid = pr->ps_pid;
 
        newsess = pool_get(&session_pool, PR_WAITOK);
-       timeout_set(&newsess->s_verauthto, zapverauth, newsess);
        newpgrp = pool_get(&pgrp_pool, PR_WAITOK);
 
-       if (pr->ps_pgid == pid || pgfind(pid)) {
+       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 {
-               (void) enterpgrp(pr, pid, newpgrp, newsess);
+               enternewpgrp(pr, newpgrp, newsess);
+               rw_exit_write(&proctreelk);
                *retval = pid;
                return (0);
        }
@@ -270,6 +288,7 @@ sys_setpgid(struct proc *curp, void *v, 
 
        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;
@@ -291,15 +310,27 @@ sys_setpgid(struct proc *curp, void *v, 
        }
        if (pgid == 0)
                pgid = targpr->ps_pid;
-       else if (pgid != targpr->ps_pid)
-               if ((pgrp = pgfind(pgid)) == 0 ||
-                   pgrp->pg_session != curpr->ps_session) {
+
+       error = 0;
+       if ((pgrp = pgfind(pgid)) == NULL) {
+               /* can only create a new process group with pgid == pid */
+               if (pgid != targpr->ps_pid)
                        error = EPERM;
-                       goto out;
+               else {
+                       enternewpgrp(targpr, newpgrp, NULL);
+                       newpgrp = NULL;
                }
-       return (enterpgrp(targpr, pgid, newpgrp, NULL));
+       } else if (pgrp != targpr->ps_pgrp) {           /* anything to do? */
+               if (pgid != targpr->ps_pid &&
+                   pgrp->pg_session != curpr->ps_session)
+                       error = EPERM;
+               else
+                       enterthispgrp(targpr, pgrp);
+       }
 out:
-       pool_put(&pgrp_pool, newpgrp);
+       rw_exit_write(&proctreelk);
+       if (newpgrp != NULL)
+               pool_put(&pgrp_pool, newpgrp);
        return (error);
 }
 
Index: kern/kern_resource.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/kern_resource.c,v
retrieving revision 1.57
diff -u -p -r1.57 kern_resource.c
--- kern/kern_resource.c        15 Sep 2016 02:00:16 -0000      1.57
+++ kern/kern_resource.c        29 Jul 2017 22:26:00 -0000
@@ -90,13 +90,17 @@ sys_getpriority(struct proc *curp, void 
        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 
        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;
        }
 
Index: kern/kern_sig.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/kern_sig.c,v
retrieving revision 1.212
diff -u -p -r1.212 kern_sig.c
--- kern/kern_sig.c     8 Jun 2017 17:14:02 -0000       1.212
+++ kern/kern_sig.c     31 Jul 2017 21:39:34 -0000
@@ -661,6 +661,7 @@ killpg1(struct proc *cp, int signum, int
                                prsignal(pr, signum);
                }
        } else {
+               rw_enter_read(&proctreelk);
                if (pgid == 0)
                        /*
                         * zero pgid means send to my process group.
@@ -668,8 +669,10 @@ killpg1(struct proc *cp, int signum, int
                        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 ||
@@ -679,6 +682,7 @@ killpg1(struct proc *cp, int signum, int
                        if (signum)
                                prsignal(pr, signum);
                }
+               rw_exit_read(&proctreelk);
        }
        return (nfound ? 0 : ESRCH);
 }
@@ -1314,6 +1318,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;
@@ -1323,6 +1328,7 @@ proc_stop_sweep(void *v)
                        prsignal(pr->ps_pptr, SIGCHLD);
                wakeup(pr->ps_pptr);
        }
+       rw_exit_read(&proctreelk);
 }
 
 /*
Index: kern/kern_sysctl.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/kern_sysctl.c,v
retrieving revision 1.330
diff -u -p -r1.330 kern_sysctl.c
--- kern/kern_sysctl.c  11 Aug 2017 21:24:19 -0000      1.330
+++ kern/kern_sysctl.c  12 Aug 2017 00:08:14 -0000
@@ -1506,6 +1506,7 @@ again:
                        break;
 
                case KERN_PROC_TTY:
+                       /* XXX proctreelk? */
                        if ((pr->ps_flags & PS_CONTROLT) == 0 ||
                            pr->ps_session->s_ttyp == NULL ||
                            pr->ps_session->s_ttyp->t_dev != (dev_t)arg)
@@ -1612,6 +1613,7 @@ fill_kproc(struct process *pr, struct ki
        if (s->s_leader)
                ki->p_sid = s->s_leader->ps_pid;
 
+       /* XXX proctreelk */
        if ((pr->ps_flags & PS_CONTROLT) && (tp = s->s_ttyp)) {
                ki->p_tdev = tp->t_dev;
                ki->p_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : -1;
Index: kern/subr_prf.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/subr_prf.c,v
retrieving revision 1.91
diff -u -p -r1.91 subr_prf.c
--- kern/subr_prf.c     30 Apr 2017 16:45:46 -0000      1.91
+++ kern/subr_prf.c     29 Jul 2017 21:15:31 -0000
@@ -362,6 +362,7 @@ uprintf(const char *fmt, ...)
        struct process *pr = curproc->p_p;
        va_list ap;
 
+       /* XXX 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);
Index: kern/sys_process.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/sys_process.c,v
retrieving revision 1.78
diff -u -p -r1.78 sys_process.c
--- kern/sys_process.c  14 Oct 2017 10:17:08 -0000      1.78
+++ kern/sys_process.c  15 Oct 2017 04:30:18 -0000
@@ -276,8 +276,13 @@ ptrace_ctrl(struct proc *p, int req, pid
        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. */
@@ -288,6 +293,7 @@ ptrace_ctrl(struct proc *p, int req, pid
                        tr->ps_ptstat = malloc(sizeof(*tr->ps_ptstat),
                            M_SUBPROC, M_WAITOK);
                memset(tr->ps_ptstat, 0, sizeof(*tr->ps_ptstat));
+               rw_exit_write(&proctreelk);
                return 0;
 
        /* calls that only operate on the PID */
@@ -295,8 +301,10 @@ ptrace_ctrl(struct proc *p, int req, pid
        case PT_ATTACH:
        case PT_DETACH:
                /* Find the process we're supposed to be operating on. */
-               if ((tr = prfind(pid)) == NULL)
-                       return (ESRCH);
+               if ((tr = prfind(pid)) == NULL) {
+                       error = ESRCH;
+                       goto fail;
+               }
                t = TAILQ_FIRST(&tr->ps_threads);
                break;
 
@@ -305,20 +313,24 @@ ptrace_ctrl(struct proc *p, int req, pid
 #ifdef PT_STEP
        case PT_STEP:
 #endif
-               if ((tr = process_tprfind(pid, &t)) == NULL)
-                       return ESRCH;
+               if ((tr = process_tprfind(pid, &t)) == NULL) {
+                       error = ESRCH;
+                       goto fail;
+               }
                break;
        }
 
        /* Check permissions/state */
        if (req != PT_ATTACH) {
                /* Check that the data is a valid signal number or zero. */
-               if (req != PT_KILL && (data < 0 || data >= NSIG))
-                       return EINVAL;
+               if (req != PT_KILL && (data < 0 || data >= NSIG)) {
+                       error = EINVAL;
+                       goto fail;
+               }
 
                /* Most operations require the target to already be traced */
                if ((error = process_checktracestate(p->p_p, tr, t)))
-                       return error;
+                       goto fail;
 
                /* Do single-step fixup if needed. */
                FIX_SSTEP(t);
@@ -327,26 +339,34 @@ ptrace_ctrl(struct proc *p, int req, pid
                 * PT_ATTACH is the opposite; you can't attach to a process if:
                 *      (1) it's the process that's doing the attaching,
                 */
-               if (tr == p->p_p)
-                       return (EINVAL);
+               if (tr == p->p_p) {
+                       error = EINVAL;
+                       goto fail;
+               }
 
                /*
                 *      (2) it's a system process
                 */
-               if (ISSET(tr->ps_flags, PS_SYSTEM))
-                       return (EPERM);
+               if (ISSET(tr->ps_flags, PS_SYSTEM)) {
+                       error = EPERM;
+                       goto fail;
+               }
 
                /*
                 *      (3) it's already being traced, or
                 */
-               if (ISSET(tr->ps_flags, PS_TRACED))
-                       return (EBUSY);
+               if (ISSET(tr->ps_flags, PS_TRACED)) {
+                       error = EBUSY;
+                       goto fail;
+               }
 
                /*
                 *      (4) it's in the middle of execve(2)
                 */
-               if (ISSET(tr->ps_flags, PS_INEXEC))
-                       return (EAGAIN);
+               if (ISSET(tr->ps_flags, PS_INEXEC)) {
+                       error = EAGAIN;
+                       goto fail;
+               }
 
                /*
                 *      (5) it's not owned by you, or the last exec
@@ -362,14 +382,14 @@ ptrace_ctrl(struct proc *p, int req, pid
                if ((tr->ps_ucred->cr_ruid != p->p_ucred->cr_ruid ||
                    ISSET(tr->ps_flags, PS_SUGIDEXEC | PS_SUGID)) &&
                    (error = suser(p, 0)) != 0)
-                       return (error);
+                       goto fail;
 
                /*
                 *      (5.5) it's not a child of the tracing process.
                 */
                if (global_ptrace == 0 && !inferior(tr, p->p_p) &&
                    (error = suser(p, 0)) != 0)
-                       return (error);
+                       goto fail;
 
                /*
                 *      (6) ...it's init, which controls the security level
@@ -377,16 +397,20 @@ ptrace_ctrl(struct proc *p, int req, pid
                 *          compiled with permanently insecure mode turned
                 *          on.
                 */
-               if ((tr->ps_pid == 1) && (securelevel > -1))
-                       return (EPERM);
+               if ((tr->ps_pid == 1) && (securelevel > -1)) {
+                       error = EPERM;
+                       goto fail;
+               }
 
                /*
                 *      (7) it's an ancestor of the current process and
                 *          not init (because that would create a loop in
                 *          the process graph).
                 */
-               if (tr->ps_pid != 1 && inferior(p->p_p, tr))
-                       return (EINVAL);
+               if (tr->ps_pid != 1 && inferior(p->p_p, tr)) {
+                       error = EINVAL;
+                       goto fail;
+               }
        }
 
        switch (req) {
@@ -419,7 +443,7 @@ ptrace_ctrl(struct proc *p, int req, pid
                /* If the address parameter is not (int *)1, set the pc. */
                if ((int *)addr != (int *)1)
                        if ((error = process_set_pc(t, addr)) != 0)
-                               return error;
+                               goto fail;
 
 #ifdef PT_STEP
                /*
@@ -427,7 +451,7 @@ ptrace_ctrl(struct proc *p, int req, pid
                 */
                error = process_sstep(t, req == PT_STEP);
                if (error)
-                       return error;
+                       goto fail;
 #endif
                goto sendsig;
 
@@ -453,7 +477,7 @@ ptrace_ctrl(struct proc *p, int req, pid
                 */
                error = process_sstep(t, 0);
                if (error)
-                       return error;
+                       goto fail;
 #endif
 
                /* give process back to original parent or init */
@@ -469,6 +493,10 @@ ptrace_ctrl(struct proc *p, int req, pid
                atomic_clearbits_int(&tr->ps_flags, PS_TRACED|PS_WAITED);
 
        sendsig:
+               KASSERT(proctree_locked);
+               proctree_locked = 0;
+               rw_exit_write(&proctreelk);
+
                memset(tr->ps_ptstat, 0, sizeof(*tr->ps_ptstat));
 
                /* Finally, deliver the requested signal (or none). */
@@ -515,6 +543,9 @@ ptrace_ctrl(struct proc *p, int req, pid
                break;
        }
 
+fail:
+       if (proctree_locked)
+               rw_exit_write(&proctreelk);
        return error;
 }
 
Index: kern/syscalls.master
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/syscalls.master,v
retrieving revision 1.178
diff -u -p -r1.178 syscalls.master
--- kern/syscalls.master        25 Sep 2017 23:00:33 -0000      1.178
+++ kern/syscalls.master        15 Oct 2017 04:28:16 -0000
@@ -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, \
@@ -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); }
Index: kern/tty.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/tty.c,v
retrieving revision 1.134
diff -u -p -r1.134 tty.c
--- kern/tty.c  29 Jun 2017 04:10:07 -0000      1.134
+++ kern/tty.c  10 Aug 2017 14:23:06 -0000
@@ -973,10 +973,13 @@ ttioctl(struct tty *tp, u_long cmd, cadd
                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,10 +987,14 @@ ttioctl(struct tty *tp, u_long cmd, cadd
                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);
+               rw_exit_write(&proctreelk);
                if (!isctty(pr, tp))
                        return (ENOTTY);
                else if (pgrp == NULL)
@@ -2125,6 +2132,7 @@ process_sum(struct process *pr, fixpt_t 
 
 /*
  * Report on state of foreground process group.
+ * XXX needs proctreelk?
  */
 void
 ttyinfo(struct tty *tp)
Index: kern/vfs_lockf.c
===================================================================
RCS file: /data/src/openbsd/src/sys/kern/vfs_lockf.c,v
retrieving revision 1.24
diff -u -p -r1.24 vfs_lockf.c
--- kern/vfs_lockf.c    7 Nov 2016 00:26:33 -0000       1.24
+++ kern/vfs_lockf.c    29 Jul 2017 21:08:40 -0000
@@ -106,9 +106,12 @@ lf_alloc(uid_t uid, int allowfail)
 
        uip = uid_find(uid);
        if (uid && allowfail && uip->ui_lockcnt >
-           (allowfail == 1 ? maxlocksperuid : (maxlocksperuid * 2)))
+           (allowfail == 1 ? maxlocksperuid : (maxlocksperuid * 2))) {
+               rw_exit_write(&uidinfolk);
                return (NULL);
+       }
        uip->ui_lockcnt++;
+       rw_exit_write(&uidinfolk);
        lock = pool_get(&lockfpool, PR_WAITOK);
        lock->lf_uid = uid;
        return (lock);
@@ -121,6 +124,7 @@ lf_free(struct lockf *lock)
 
        uip = uid_find(lock->lf_uid);
        uip->ui_lockcnt--;
+       rw_exit_write(&uidinfolk);
        pool_put(&lockfpool, lock);
 }
 

Reply via email to