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 */

Reply via email to