Module Name: src Committed By: ad Date: Thu Mar 26 21:31:55 UTC 2020
Modified Files: src/sys/kern: kern_exit.c kern_lwp.c Log Message: Fix crash observed with procfs on current-users by David Hopper. LWP refcnt and p_zomblwp both must reach the needed state, and LSZOMB be set, under a single hold of p_lock. To generate a diff of this commit: cvs rdiff -u -r1.285 -r1.286 src/sys/kern/kern_exit.c cvs rdiff -u -r1.230 -r1.231 src/sys/kern/kern_lwp.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/kern/kern_exit.c diff -u src/sys/kern/kern_exit.c:1.285 src/sys/kern/kern_exit.c:1.286 --- src/sys/kern/kern_exit.c:1.285 Sun Mar 8 15:05:18 2020 +++ src/sys/kern/kern_exit.c Thu Mar 26 21:31:55 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_exit.c,v 1.285 2020/03/08 15:05:18 ad Exp $ */ +/* $NetBSD: kern_exit.c,v 1.286 2020/03/26 21:31:55 ad Exp $ */ /*- * Copyright (c) 1998, 1999, 2006, 2007, 2008, 2020 The NetBSD Foundation, Inc. @@ -67,7 +67,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_exit.c,v 1.285 2020/03/08 15:05:18 ad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_exit.c,v 1.286 2020/03/26 21:31:55 ad Exp $"); #include "opt_ktrace.h" #include "opt_dtrace.h" @@ -559,7 +559,9 @@ exit1(struct lwp *l, int exitcode, int s /* Free the linux lwp id */ if ((l->l_pflag & LP_PIDLID) != 0 && l->l_lid != p->p_pid) proc_free_pid(l->l_lid); - lwp_drainrefs(l); + if (l->l_refcnt > 0) { + lwp_drainrefs(l); + } lwp_lock(l); l->l_prflag &= ~LPR_DETACHED; l->l_stat = LSZOMB; Index: src/sys/kern/kern_lwp.c diff -u src/sys/kern/kern_lwp.c:1.230 src/sys/kern/kern_lwp.c:1.231 --- src/sys/kern/kern_lwp.c:1.230 Thu Mar 26 20:19:06 2020 +++ src/sys/kern/kern_lwp.c Thu Mar 26 21:31:55 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_lwp.c,v 1.230 2020/03/26 20:19:06 ad Exp $ */ +/* $NetBSD: kern_lwp.c,v 1.231 2020/03/26 21:31:55 ad Exp $ */ /*- * Copyright (c) 2001, 2006, 2007, 2008, 2009, 2019, 2020 @@ -211,7 +211,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v 1.230 2020/03/26 20:19:06 ad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v 1.231 2020/03/26 21:31:55 ad Exp $"); #include "opt_ddb.h" #include "opt_lockdebug.h" @@ -274,7 +274,7 @@ struct lwp lwp0 __aligned(MIN_LWP_ALIGNM .l_stat = LSONPROC, .l_ts = &turnstile0, .l_syncobj = &sched_syncobj, - .l_refcnt = 1, + .l_refcnt = 0, .l_priority = PRI_USER + NPRI_USER - 1, .l_inheritedprio = -1, .l_class = SCHED_OTHER, @@ -821,7 +821,7 @@ lwp_create(lwp_t *l1, proc_t *p2, vaddr_ l2->l_stat = LSIDL; l2->l_proc = p2; - l2->l_refcnt = 1; + l2->l_refcnt = 0; l2->l_class = sclass; /* @@ -1180,19 +1180,27 @@ lwp_exit(struct lwp *l) * mark it waiting for collection in the proc structure. Note that * before we can do that, we need to free any other dead, deatched * LWP waiting to meet its maker. + * + * All conditions need to be observed upon under the same hold of + * p_lock, because if the lock is dropped any of them can change. */ mutex_enter(p->p_lock); - lwp_drainrefs(l); - - if ((l->l_prflag & LPR_DETACHED) != 0) { - while ((l2 = p->p_zomblwp) != NULL) { - p->p_zomblwp = NULL; - lwp_free(l2, false, false);/* releases proc mutex */ - mutex_enter(p->p_lock); - l->l_refcnt++; + for (;;) { + if (l->l_refcnt > 0) { lwp_drainrefs(l); + continue; + } + if ((l->l_prflag & LPR_DETACHED) != 0) { + if ((l2 = p->p_zomblwp) != NULL) { + p->p_zomblwp = NULL; + lwp_free(l2, false, false); + /* proc now unlocked */ + mutex_enter(p->p_lock); + continue; + } + p->p_zomblwp = l; } - p->p_zomblwp = l; + break; } /* @@ -1692,7 +1700,6 @@ lwp_addref(struct lwp *l) KASSERT(mutex_owned(l->l_proc->p_lock)); KASSERT(l->l_stat != LSZOMB); - KASSERT(l->l_refcnt != 0); l->l_refcnt++; } @@ -1724,6 +1731,7 @@ lwp_delref2(struct lwp *l) KASSERT(mutex_owned(p->p_lock)); KASSERT(l->l_stat != LSZOMB); KASSERT(l->l_refcnt > 0); + if (--l->l_refcnt == 0) cv_broadcast(&p->p_lwpcv); } @@ -1737,10 +1745,8 @@ lwp_drainrefs(struct lwp *l) struct proc *p = l->l_proc; KASSERT(mutex_owned(p->p_lock)); - KASSERT(l->l_refcnt != 0); - l->l_refcnt--; - while (l->l_refcnt != 0) + while (l->l_refcnt > 0) cv_wait(&p->p_lwpcv, p->p_lock); }