Index: kern/kern_fork.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_fork.c,v
retrieving revision 1.57
diff -u -r1.57 kern_fork.c
--- kern/kern_fork.c	19 Sep 2006 11:47:35 -0000	1.57
+++ kern/kern_fork.c	6 Oct 2006 17:21:12 -0000
@@ -93,7 +93,7 @@
 	struct proc *p2;
 	int error;
 
-	error = fork1(lp, RFFDG | RFPROC, &p2);
+	error = fork1(lp, RFFDG | RFPROC | RFPGLOCK, &p2);
 	if (error == 0) {
 		start_forked_proc(lp, p2);
 		uap->sysmsg_fds[0] = p2->p_pid;
@@ -110,7 +110,7 @@
 	struct proc *p2;
 	int error;
 
-	error = fork1(lp, RFFDG | RFPROC | RFPPWAIT | RFMEM, &p2);
+	error = fork1(lp, RFFDG | RFPROC | RFPPWAIT | RFMEM | RFPGLOCK, &p2);
 	if (error == 0) {
 		start_forked_proc(lp, p2);
 		uap->sysmsg_fds[0] = p2->p_pid;
@@ -140,7 +140,7 @@
 	if ((uap->flags & RFKERNELONLY) != 0)
 		return (EINVAL);
 
-	error = fork1(lp, uap->flags, &p2);
+	error = fork1(lp, uap->flags | RFPGLOCK, &p2);
 	if (error == 0) {
 		if (p2)
 			start_forked_proc(lp, p2);
@@ -158,9 +158,10 @@
 {
 	struct proc *p1 = lp1->lwp_proc;
 	struct proc *p2, *pptr;
+	struct pgrp *pgrp;
 	struct lwp *lp2;
 	uid_t uid;
-	int ok;
+	int ok, error;
 	static int curfail = 0;
 	static struct timeval lastfail;
 	struct forklist *ep;
@@ -203,6 +204,26 @@
 	}
 
 	/*
+	 * Interlock against process group signal delivery.  If signals
+	 * are pending after the interlock is obtained we have to restart
+	 * the system call to process the signals.  If we don't the child
+	 * can miss a pgsignal (such as ^C) sent during the fork.
+	 *
+	 * We can't use CURSIG() here because it will process any STOPs
+	 * and cause the process group lock to be held indefinitely.  If
+	 * a STOP occurs, the fork will be restarted after the CONT.
+	 */
+	error = 0;
+	pgrp = NULL;
+	if ((flags & RFPGLOCK) && (pgrp = p1->p_pgrp) != NULL) {
+		lockmgr(&pgrp->pg_lock, LK_SHARED);
+		if (CURSIGNB(p1)) {
+			error = ERESTART;
+			goto done;
+		}
+	}
+
+	/*
 	 * Although process entries are dynamically created, we still keep
 	 * a global limit on the maximum number we will create.  Don't allow
 	 * a nonprivileged user to use the last ten processes; don't let root
@@ -215,7 +236,8 @@
 			printf("maxproc limit exceeded by uid %d, please "
 			       "see tuning(7) and login.conf(5).\n", uid);
 		tsleep(&forksleep, 0, "fork", hz / 2);
-		return (EAGAIN);
+		error = EAGAIN;
+		goto done;
 	}
 	/*
 	 * Increment the nprocs resource before blocking can occur.  There
@@ -238,7 +260,8 @@
 			printf("maxproc limit exceeded by uid %d, please "
 			       "see tuning(7) and login.conf(5).\n", uid);
 		tsleep(&forksleep, 0, "fork", hz / 2);
-		return (EAGAIN);
+		error = EAGAIN;
+		goto done;
 	}
 
 	/* Allocate new proc. */
@@ -506,7 +529,10 @@
 	 * Return child proc pointer to parent.
 	 */
 	*procp = p2;
-	return (0);
+done:
+	if (pgrp)
+		lockmgr(&pgrp->pg_lock, LK_RELEASE);
+	return (error);
 }
 
 /*
Index: kern/kern_proc.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_proc.c,v
retrieving revision 1.28
diff -u -r1.28 kern_proc.c
--- kern/kern_proc.c	5 Sep 2006 00:55:45 -0000	1.28
+++ kern/kern_proc.c	6 Oct 2006 16:28:15 -0000
@@ -228,6 +228,7 @@
 		LIST_INSERT_HEAD(PGRPHASH(pgid), pgrp, pg_hash);
 		pgrp->pg_jobc = 0;
 		SLIST_INIT(&pgrp->pg_sigiolst);
+		lockinit(&pgrp->pg_lock, "pgwt", 0, 0);
 	} else if (pgrp == p->p_pgrp)
 		return (0);
 
Index: kern/kern_sig.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_sig.c,v
retrieving revision 1.54
diff -u -r1.54 kern_sig.c
--- kern/kern_sig.c	19 Sep 2006 11:47:35 -0000	1.54
+++ kern/kern_sig.c	6 Oct 2006 17:07:50 -0000
@@ -56,6 +56,7 @@
 #include <sys/systm.h>
 #include <sys/acct.h>
 #include <sys/fcntl.h>
+#include <sys/lock.h>
 #include <sys/wait.h>
 #include <sys/ktrace.h>
 #include <sys/syslog.h>
@@ -630,6 +631,7 @@
 			if (pgrp == NULL)
 				return (ESRCH);
 		}
+		lockmgr(&pgrp->pg_lock, LK_EXCLUSIVE);
 		LIST_FOREACH(p, &pgrp->pg_members, p_pglist) {
 			if (p->p_pid <= 1 || 
 			    (p->p_flag & (P_SYSTEM | P_ZOMBIE)) ||
@@ -640,6 +642,7 @@
 			if (sig)
 				ksignal(p, sig);
 		}
+		lockmgr(&pgrp->pg_lock, LK_RELEASE);
 	}
 	return (info.nfound ? 0 : ESRCH);
 }
@@ -713,16 +716,23 @@
 /*
  * Send a signal to a process group.  If checktty is 1,
  * limit to members which have a controlling terminal.
+ *
+ * pg_lock interlocks against a fork that might be in progress, to
+ * ensure that the new child process picks up the signal.
  */
 void
 pgsignal(struct pgrp *pgrp, int sig, int checkctty)
 {
 	struct proc *p;
 
-	if (pgrp)
-		LIST_FOREACH(p, &pgrp->pg_members, p_pglist)
+	if (pgrp) {
+		lockmgr(&pgrp->pg_lock, LK_EXCLUSIVE);
+		LIST_FOREACH(p, &pgrp->pg_members, p_pglist) {
 			if (checkctty == 0 || p->p_flag & P_CONTROLT)
 				ksignal(p, sig);
+		}
+		lockmgr(&pgrp->pg_lock, LK_RELEASE);
+	}
 }
 
 /*
@@ -1751,10 +1761,13 @@
 	} else if (sigio->sio_pgid < 0) {
 		struct proc *p;
 
-		LIST_FOREACH(p, &sigio->sio_pgrp->pg_members, p_pglist)
+		lockmgr(&sigio->sio_pgrp->pg_lock, LK_EXCLUSIVE);
+		LIST_FOREACH(p, &sigio->sio_pgrp->pg_members, p_pglist) {
 			if (CANSIGIO(sigio->sio_ruid, sigio->sio_ucred, p) &&
 			    (checkctty == 0 || (p->p_flag & P_CONTROLT)))
 				ksignal(p, sig);
+		}
+		lockmgr(&sigio->sio_pgrp->pg_lock, LK_RELEASE);
 	}
 }
 
Index: sys/proc.h
===================================================================
RCS file: /cvs/src/sys/sys/proc.h,v
retrieving revision 1.86
diff -u -r1.86 proc.h
--- sys/proc.h	30 Sep 2006 22:14:31 -0000	1.86
+++ sys/proc.h	6 Oct 2006 16:54:26 -0000
@@ -54,6 +54,7 @@
 #include <sys/queue.h>
 #include <sys/rtprio.h>			/* For struct rtprio. */
 #include <sys/signal.h>
+#include <sys/lock.h>
 #ifndef _KERNEL
 #include <sys/time.h>			/* For structs itimerval, timeval. */
 #endif
@@ -92,6 +93,7 @@
 	struct  sigiolst pg_sigiolst;	/* List of sigio sources. */
 	pid_t	pg_id;			/* Pgrp id. */
 	int	pg_jobc;	/* # procs qualifying pgrp for job control */
+	struct lock pg_lock;		/* Lock during fork */
 };
 
 struct	procsig {
Index: sys/signalvar.h
===================================================================
RCS file: /cvs/src/sys/sys/signalvar.h,v
retrieving revision 1.14
diff -u -r1.14 signalvar.h
--- sys/signalvar.h	3 Sep 2006 18:29:17 -0000	1.14
+++ sys/signalvar.h	6 Oct 2006 17:17:03 -0000
@@ -212,6 +212,7 @@
  * Inline functions:
  */
 #define	CURSIG(p)	__cursig(p)
+#define CURSIGNB(p)	__cursignb(p)
 
 /*
  * Determine signal that should be delivered to process p, the current
@@ -220,7 +221,9 @@
  *
  * MP SAFE
  */
-static __inline int __cursig(struct proc *p)
+static __inline
+int
+__cursig(struct proc *p)
 {
 	sigset_t tmpset;
 	int r;
@@ -235,6 +238,21 @@
 	return(r);
 }
 
+static __inline
+int
+__cursignb(struct proc *p)
+{
+	sigset_t tmpset;
+
+	tmpset = p->p_siglist;
+	SIGSETNAND(tmpset, p->p_sigmask);
+	if (SIGISEMPTY(p->p_siglist) ||
+	     (!(p->p_flag & P_TRACED) && SIGISEMPTY(tmpset))) {
+		return(FALSE);
+	}
+	return (TRUE);
+}
+
 #endif	/* _KERNEL */
 
 #endif	/* !_SYS_SIGNALVAR_H_ */
Index: sys/unistd.h
===================================================================
RCS file: /cvs/src/sys/sys/unistd.h,v
retrieving revision 1.5
diff -u -r1.5 unistd.h
--- sys/unistd.h	3 Dec 2005 02:19:52 -0000	1.5
+++ sys/unistd.h	6 Oct 2006 16:51:17 -0000
@@ -219,8 +219,9 @@
 #define RFTHREAD	(1<<13)	/* enable kernel thread support */
 #define RFSIGSHARE	(1<<14)	/* share signal handlers */
 #define RFLINUXTHPN     (1<<16) /* do linux clone exit parent notification */
+#define RFPGLOCK	(1<<30) /* process group interlock for signal race */
 #define RFPPWAIT	(1<<31) /* parent sleeps until child exits (vfork) */
-#define RFKERNELONLY	RFPPWAIT
+#define RFKERNELONLY	(RFPPWAIT|RFPGLOCK)
 
 
 #endif /* !_POSIX_SOURCE */
