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