Module Name: src Committed By: rmind Date: Sun Jul 22 22:40:19 UTC 2012
Modified Files: src/sys/kern: kern_exec.c kern_exit.c kern_fork.c kern_lwp.c src/sys/sys: lwp.h proc.h Log Message: fork1: fix use-after-free problems. Addresses PR/46128 from Andrew Doran. Note: PL_PPWAIT should be fully replaced and modificaiton of l_pflag by other LWP is undesirable, but this is enough for netbsd-6. To generate a diff of this commit: cvs rdiff -u -r1.352 -r1.353 src/sys/kern/kern_exec.c cvs rdiff -u -r1.238 -r1.239 src/sys/kern/kern_exit.c cvs rdiff -u -r1.189 -r1.190 src/sys/kern/kern_fork.c cvs rdiff -u -r1.170 -r1.171 src/sys/kern/kern_lwp.c cvs rdiff -u -r1.162 -r1.163 src/sys/sys/lwp.h cvs rdiff -u -r1.316 -r1.317 src/sys/sys/proc.h 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_exec.c diff -u src/sys/kern/kern_exec.c:1.352 src/sys/kern/kern_exec.c:1.353 --- src/sys/kern/kern_exec.c:1.352 Wed May 2 23:33:11 2012 +++ src/sys/kern/kern_exec.c Sun Jul 22 22:40:19 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_exec.c,v 1.352 2012/05/02 23:33:11 rmind Exp $ */ +/* $NetBSD: kern_exec.c,v 1.353 2012/07/22 22:40:19 rmind Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -59,7 +59,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.352 2012/05/02 23:33:11 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.353 2012/07/22 22:40:19 rmind Exp $"); #include "opt_exec.h" #include "opt_ktrace.h" @@ -1180,10 +1180,17 @@ execve_runproc(struct lwp *l, struct exe * exited and exec()/exit() are the only places it will be cleared. */ if ((p->p_lflag & PL_PPWAIT) != 0) { + lwp_t *lp; + mutex_enter(proc_lock); + lp = p->p_vforklwp; + p->p_vforklwp = NULL; + l->l_lwpctl = NULL; /* was on loan from blocked parent */ p->p_lflag &= ~PL_PPWAIT; - cv_broadcast(&p->p_pptr->p_waitcv); + + lp->l_pflag &= ~LP_VFORKWAIT; /* XXX */ + cv_broadcast(&lp->l_waitcv); mutex_exit(proc_lock); } Index: src/sys/kern/kern_exit.c diff -u src/sys/kern/kern_exit.c:1.238 src/sys/kern/kern_exit.c:1.239 --- src/sys/kern/kern_exit.c:1.238 Sun Apr 8 11:27:45 2012 +++ src/sys/kern/kern_exit.c Sun Jul 22 22:40:19 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_exit.c,v 1.238 2012/04/08 11:27:45 martin Exp $ */ +/* $NetBSD: kern_exit.c,v 1.239 2012/07/22 22:40:19 rmind Exp $ */ /*- * Copyright (c) 1998, 1999, 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -67,7 +67,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_exit.c,v 1.238 2012/04/08 11:27:45 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_exit.c,v 1.239 2012/07/22 22:40:19 rmind Exp $"); #include "opt_ktrace.h" #include "opt_perfctrs.h" @@ -330,9 +330,15 @@ exit1(struct lwp *l, int rv) */ mutex_enter(proc_lock); if (p->p_lflag & PL_PPWAIT) { + lwp_t *lp; + l->l_lwpctl = NULL; /* was on loan from blocked parent */ p->p_lflag &= ~PL_PPWAIT; - cv_broadcast(&p->p_pptr->p_waitcv); + + lp = p->p_vforklwp; + p->p_vforklwp = NULL; + lp->l_pflag &= ~LP_VFORKWAIT; /* XXX */ + cv_broadcast(&lp->l_waitcv); } if (SESS_LEADER(p)) { Index: src/sys/kern/kern_fork.c diff -u src/sys/kern/kern_fork.c:1.189 src/sys/kern/kern_fork.c:1.190 --- src/sys/kern/kern_fork.c:1.189 Tue Mar 13 18:40:52 2012 +++ src/sys/kern/kern_fork.c Sun Jul 22 22:40:19 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_fork.c,v 1.189 2012/03/13 18:40:52 elad Exp $ */ +/* $NetBSD: kern_fork.c,v 1.190 2012/07/22 22:40:19 rmind Exp $ */ /*- * Copyright (c) 1999, 2001, 2004, 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -67,7 +67,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_fork.c,v 1.189 2012/03/13 18:40:52 elad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_fork.c,v 1.190 2012/07/22 22:40:19 rmind Exp $"); #include "opt_ktrace.h" @@ -373,7 +373,14 @@ fork1(struct lwp *l1, int flags, int exi p2->p_limit = lim_copy(p1_lim); } - p2->p_lflag = ((flags & FORK_PPWAIT) ? PL_PPWAIT : 0); + if (flags & FORK_PPWAIT) { + /* Mark ourselves as waiting for a child. */ + l1->l_pflag |= LP_VFORKWAIT; + p2->p_lflag = PL_PPWAIT; + p2->p_vforklwp = l1; + } else { + p2->p_lflag = 0; + } p2->p_sflag = 0; p2->p_slflag = 0; parent = (flags & FORK_NOWAIT) ? initproc : p1; @@ -565,15 +572,24 @@ fork1(struct lwp *l1, int flags, int exi sched_enqueue(l2, false); lwp_unlock(l2); } + + /* + * Return child pid to parent process, + * marking us as parent via retval[1]. + */ + if (retval != NULL) { + retval[0] = p2->p_pid; + retval[1] = 0; + } mutex_exit(p2->p_lock); /* * Preserve synchronization semantics of vfork. If waiting for - * child to exec or exit, set PL_PPWAIT on child, and sleep on our - * proc (in case of exit). + * child to exec or exit, sleep until it clears LP_VFORKWAIT. */ - while (p2->p_lflag & PL_PPWAIT) - cv_wait(&p1->p_waitcv, proc_lock); + while (l1->l_pflag & LP_VFORKWAIT) { + cv_wait(&l1->l_waitcv, proc_lock); + } /* * Let the parent know that we are tracing its child. @@ -586,17 +602,7 @@ fork1(struct lwp *l1, int flags, int exi ksi.ksi_lid = l1->l_lid; kpsignal(p1, &ksi, NULL); } - mutex_exit(proc_lock); - /* - * Return child pid to parent process, - * marking us as parent via retval[1]. - */ - if (retval != NULL) { - retval[0] = p2->p_pid; - retval[1] = 0; - } - return 0; } Index: src/sys/kern/kern_lwp.c diff -u src/sys/kern/kern_lwp.c:1.170 src/sys/kern/kern_lwp.c:1.171 --- src/sys/kern/kern_lwp.c:1.170 Sat Jun 9 02:55:32 2012 +++ src/sys/kern/kern_lwp.c Sun Jul 22 22:40:19 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_lwp.c,v 1.170 2012/06/09 02:55:32 christos Exp $ */ +/* $NetBSD: kern_lwp.c,v 1.171 2012/07/22 22:40:19 rmind Exp $ */ /*- * Copyright (c) 2001, 2006, 2007, 2008, 2009 The NetBSD Foundation, Inc. @@ -211,7 +211,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v 1.170 2012/06/09 02:55:32 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v 1.171 2012/07/22 22:40:19 rmind Exp $"); #include "opt_ddb.h" #include "opt_lockdebug.h" @@ -356,6 +356,7 @@ lwp0_init(void) callout_init(&l->l_timeout_ch, CALLOUT_MPSAFE); callout_setfunc(&l->l_timeout_ch, sleepq_timeout, l); cv_init(&l->l_sigcv, "sigwait"); + cv_init(&l->l_waitcv, "vfork"); kauth_cred_hold(proc0.p_cred); l->l_cred = proc0.p_cred; @@ -824,6 +825,7 @@ lwp_create(lwp_t *l1, proc_t *p2, vaddr_ callout_init(&l2->l_timeout_ch, CALLOUT_MPSAFE); callout_setfunc(&l2->l_timeout_ch, sleepq_timeout, l2); cv_init(&l2->l_sigcv, "sigwait"); + cv_init(&l2->l_waitcv, "vfork"); l2->l_syncobj = &sched_syncobj; if (rnewlwpp != NULL) @@ -1162,6 +1164,7 @@ lwp_free(struct lwp *l, bool recycle, bo sigclear(&l->l_sigpend, NULL, &kq); ksiginfo_queue_drain(&kq); cv_destroy(&l->l_sigcv); + cv_destroy(&l->l_waitcv); /* * Free lwpctl structure and affinity. Index: src/sys/sys/lwp.h diff -u src/sys/sys/lwp.h:1.162 src/sys/sys/lwp.h:1.163 --- src/sys/sys/lwp.h:1.162 Sat Jun 9 02:31:15 2012 +++ src/sys/sys/lwp.h Sun Jul 22 22:40:18 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: lwp.h,v 1.162 2012/06/09 02:31:15 christos Exp $ */ +/* $NetBSD: lwp.h,v 1.163 2012/07/22 22:40:18 rmind Exp $ */ /*- * Copyright (c) 2001, 2006, 2007, 2008, 2009, 2010 @@ -53,9 +53,9 @@ #include <machine/proc.h> /* Machine-dependent proc substruct. */ /* - * Lightweight process. Field markings and the corresponding locks: + * Lightweight process. Field markings and the corresponding locks: * - * a: proclist_lock + * a: proc_lock * c: condition variable interlock, passed to cv_wait() * l: *l_mutex * p: l_proc->p_lock @@ -124,6 +124,7 @@ struct lwp { u_int l_slptime; /* l: time since last blocked */ callout_t l_timeout_ch; /* !: callout for tsleep */ u_int l_emap_gen; /* !: emap generation number */ + kcondvar_t l_waitcv; /* a: vfork() wait */ #if PCU_UNIT_COUNT > 0 struct cpu_info * volatile l_pcu_cpu[PCU_UNIT_COUNT]; @@ -245,6 +246,7 @@ extern int maxlwp __read_mostly; /* max #define LP_INTR 0x00000040 /* Soft interrupt handler */ #define LP_SYSCTLWRITE 0x00000080 /* sysctl write lock held */ #define LP_MUSTJOIN 0x00000100 /* Must join kthread on exit */ +#define LP_VFORKWAIT 0x00000200 /* Waiting at vfork() for a child */ #define LP_TIMEINTR 0x00010000 /* Time this soft interrupt */ #define LP_RUNNING 0x20000000 /* Active on a CPU */ #define LP_BOUND 0x80000000 /* Bound to a CPU */ Index: src/sys/sys/proc.h diff -u src/sys/sys/proc.h:1.316 src/sys/sys/proc.h:1.317 --- src/sys/sys/proc.h:1.316 Sun Feb 19 21:06:58 2012 +++ src/sys/sys/proc.h Sun Jul 22 22:40:18 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: proc.h,v 1.316 2012/02/19 21:06:58 rmind Exp $ */ +/* $NetBSD: proc.h,v 1.317 2012/07/22 22:40:18 rmind Exp $ */ /*- * Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -212,7 +212,7 @@ struct proc { krwlock_t p_reflock; /* p: lock for debugger, procfs */ kcondvar_t p_waitcv; /* p: wait, stop CV on children */ kcondvar_t p_lwpcv; /* p: wait, stop CV on LWPs */ - + /* Substructures: */ struct kauth_cred *p_cred; /* p: Master copy of credentials */ struct filedesc *p_fd; /* :: Ptr to open files structure */ @@ -256,6 +256,7 @@ struct proc { u_int p_nstopchild; /* l: Count of stopped/dead children */ u_int p_waited; /* l: parent has waited on child */ struct lwp *p_zomblwp; /* p: detached LWP to be reaped */ + struct lwp *p_vforklwp; /* p: parent LWP waiting at vfork() */ /* scheduling */ void *p_sched_info; /* p: Scheduler-specific structure */