From owner-freebsd-hackers Sat Sep 27 01:01:58 1997 Return-Path: Received: (from root@localhost) by hub.freebsd.org (8.8.7/8.8.7) id BAA08337 for hackers-outgoing; Sat, 27 Sep 1997 01:01:58 -0700 (PDT) Received: from gatekeeper.tsc.tdk.com (root@gatekeeper.tsc.tdk.com [207.113.159.21]) by hub.freebsd.org (8.8.7/8.8.7) with ESMTP id BAA08332 for ; Sat, 27 Sep 1997 01:01:52 -0700 (PDT) Received: from sunrise.gv.tsc.tdk.com (root@sunrise.gv.tsc.tdk.com [192.168.241.191]) by gatekeeper.tsc.tdk.com (8.8.4/8.8.4) with ESMTP id BAA23823 for ; Sat, 27 Sep 1997 01:01:50 -0700 (PDT) Received: from salsa.gv.tsc.tdk.com (salsa.gv.tsc.tdk.com [192.168.241.194]) by sunrise.gv.tsc.tdk.com (8.8.5/8.8.5) with ESMTP id BAA10710 for ; Sat, 27 Sep 1997 01:01:49 -0700 (PDT) Received: (from gdonl@localhost) by salsa.gv.tsc.tdk.com (8.8.5/8.8.5) id BAA00388 for freebsd-hackers@freebsd.org; Sat, 27 Sep 1997 01:01:48 -0700 (PDT) Date: Sat, 27 Sep 1997 01:01:48 -0700 (PDT) From: Don Lewis Message-Id: <199709270801.BAA00388@salsa.gv.tsc.tdk.com> To: freebsd-hackers@freebsd.org Subject: F_SETOWN implementation redesign Sender: owner-freebsd-hackers@freebsd.org X-Loop: FreeBSD.org Precedence: bulk After hearing about the OpenBSD security fix for F_SETOWN, I went digging through the FreeBSD mail list archives and stumbled across a lot of complaints about the current F_SETOWN implementation dating back to the early part of 1996 (as well as mention of the security flaw). This inspired me to revamp the F_SETOWN implementation to address some of these issues. Security: I added credential checking which should work in a similar manner as the OpenBSD change. I added a restriction that only allows F_SETOWN to specify a process or process group in the same session as the caller. This change is somewhat debatable, but I can't think of a good use for allowing a process in another session to be specified and there is some potential for misuse as well as traps for the careless. When a process exits or a process group loses all it's members, any references to it specified by F_SETOWN are revoked even though the device or socket has not been closed (it may be held open by another process). The old implementation would start sending signals to a new process once the process IDs wrapped around. Funtionality: The old implementation used TIOCSPGRP when used on a tty device. This meant that you could only do async I/O on your controlling tty, which meant that you were limited to doing async I/O on at most one tty device. My new implementation separates the F_SETOWN functionality from TIOCSPGRP on tty devices, so you can do async I/O on multiple devices and still have a separate control tty. The only restriction is that these tty devices can't be the control ttys for another session. The new implementation always returns an error if you specify a non-existent process or process group. Performance: The old implemenation needed to search for the process group or process whenever it wanted to deliver a signal. The new implementation keeps a pointer, which should reduce the overhead for heavy users of async I/O. The attached patch is relative to my somewhat hacked copy of 2.1-stable, so it might not apply quite cleanly to a fresh copy. It'll require even more changes to make it work with 2.2 or later (for instance the appropriate changes will need to be made to kern_pipe.c). I've only done limited testing so far, but I thought this would be an appropriate time to share my work. One thing to consider is whether it is appropriate to remove the TIOCSPGRP/TIOCGPGRP code from the non-tty drivers. I left it in in case anyone is using these ioctl commands instead of F_SETOWN or F_GETOWN on non-ttys. Removing this code would reduce the bloat somewhat. --- Truck Index: lib/libc/sys/fcntl.2 =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/lib/libc/sys/fcntl.2,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 fcntl.2 --- fcntl.2 1997/05/09 09:31:04 1.1.1.1 +++ fcntl.2 1997/09/27 01:35:13 @@ -477,29 +477,23 @@ .Dv F_SETLKW , and satisfying the lock or unlock request would result in the number of locked regions in the system exceeding a system-imposed limit. +.It Bq Er ENOTTY +.Fa Cmd +is +.Dv F_GETOWN +or +.Dv F_SETOWN , +.Fa fd +refers to a descriptor open on a terminal device (as opposed to a +descriptor open on a socket), and the device is the controlling +terminal for a session other than the session of the calling process. .It Bq Er ESRCH .Fa Cmd is .Dv F_SETOWN and -the process ID given as argument is not in use. +the process ID or process group ID given as argument is not in use. .El -.Pp -In addition, if -.Fa fd -refers to a descriptor open on a terminal device (as opposed to a -descriptor open on a socket), a -.Fa cmd -of -.Dv F_SETOWN -can fail for the same reasons as in -.Xr tcsetpgrp 3 , -and a -.Fa cmd -of -.Dv F_GETOWN -for the reasons as stated in -.Xr tcgetpgrp 3 . .Sh SEE ALSO .Xr close 2 , .Xr execve 2 , @@ -507,8 +501,6 @@ .Xr getdtablesize 2 , .Xr open 2 , .Xr sigvec 2 , -.Xr tcgetpgrp 3 , -.Xr tcsetpgrp 3 .Sh HISTORY The .Nm Index: sys/kern/kern_descrip.c =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/kern/kern_descrip.c,v retrieving revision 1.2 diff -u -r1.2 kern_descrip.c --- kern_descrip.c 1997/05/13 11:55:43 1.2 +++ kern_descrip.c 1997/09/26 23:24:36 @@ -226,30 +226,13 @@ return (error); case F_GETOWN: - if (fp->f_type == DTYPE_SOCKET) { - *retval = ((struct socket *)fp->f_data)->so_pgid; - return (0); - } error = (*fp->f_ops->fo_ioctl) - (fp, (int)TIOCGPGRP, (caddr_t)retval, p); - *retval = -*retval; + (fp, (int)FIOGETOWN, (caddr_t)retval, p); return (error); case F_SETOWN: - if (fp->f_type == DTYPE_SOCKET) { - ((struct socket *)fp->f_data)->so_pgid = uap->arg; - return (0); - } - if (uap->arg <= 0) { - uap->arg = -uap->arg; - } else { - struct proc *p1 = pfind(uap->arg); - if (p1 == 0) - return (ESRCH); - uap->arg = p1->p_pgrp->pg_id; - } return ((*fp->f_ops->fo_ioctl) - (fp, (int)TIOCSPGRP, (caddr_t)&uap->arg, p)); + (fp, (int)FIOSETOWN, (caddr_t)&uap->arg, p)); case F_SETLKW: flg |= F_WAIT; @@ -325,6 +308,94 @@ fdp->fd_lastfile = new; *retval = new; return (0); +} + +/* + * If sigio is on the list associated with a process or process group, + * remove it. + */ +void +funsetown(sigio) + register struct sigio *sigio; +{ + if (sigio->sio_pgid == 0) + return; + + if (sigio->sio_pgid < 0) { + SLIST_REMOVE(&(sigio->sio_pgrp->pg_sigiolst), sigio, sigio, + sio_pgsigio); + sigio->sio_pgrp = NULL; + } else if (sigio->sio_pgid > 0) { + SLIST_REMOVE(&(sigio->sio_proc->p_sigiolst), sigio, sigio, + sio_pgsigio); + sigio->sio_proc = NULL; + } + sigio->sio_pgid = 0; + crfree(sigio->sio_ucred); + sigio->sio_ucred = NULL; +} + +/* + * Common code for FIOSETOWN ioctl called by F_SETOWN + * + * After permission checking, add sigio structure to the sigio list for + * the process or process group. + */ +int +fsetown(pgid, sigio, funset) + register pid_t pgid; + register struct sigio *sigio; + void (*funset) __P((struct sigio *)); +{ + if (pgid == 0) { + funsetown(sigio); + return (0); + } else if (pgid > 0) { + register struct proc *proc = pfind(pgid); + + if (proc == NULL) + return (ESRCH); + else if (proc->p_session != curproc->p_session) + return (EPERM); + + funsetown(sigio); + + SLIST_INSERT_HEAD(&(proc->p_sigiolst), sigio, sio_pgsigio); + sigio->sio_proc = proc; + } else /* if (pgid < 0) */ { + register struct pgrp *pgrp = pgfind(-pgid); + + if (pgrp == NULL) + return (ESRCH); + else if (pgrp->pg_session != curproc->p_session) + return (EPERM); + + funsetown(sigio); + + SLIST_INSERT_HEAD(&(pgrp->pg_sigiolst), sigio, sio_pgsigio); + sigio->sio_pgrp = pgrp; + } + + sigio->sio_pgid = pgid; + + crhold(curproc->p_ucred); + sigio->sio_ucred = curproc->p_ucred; + sigio->sio_ruid = curproc->p_cred->p_ruid; /* wish this was in ucred */ + + sigio->sio_unsetown = funset; + + return (0); +} + +/* + * Common code for FIOGETOWN ioctl called by F_GETOWN + */ +pid_t +fgetown(sigio) + register struct sigio *sigio; +{ + /* we could also return sigio->sio_{proc->p_pid,pgrp->pg_id} */ + return (sigio->sio_pgid); } /* Index: sys/kern/kern_exit.c =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/kern/kern_exit.c,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 kern_exit.c --- kern_exit.c 1997/05/09 09:34:33 1.1.1.1 +++ kern_exit.c 1997/09/26 23:25:32 @@ -131,6 +131,14 @@ untimeout(realitexpire, (caddr_t)p); /* + * Reset any sigio structures pointing to us as a result of + * F_SETOWN with our pid + */ + while (p->p_sigiolst.slh_first != NULL) + (*p->p_sigiolst.slh_first->sio_unsetown) + (p->p_sigiolst.slh_first); + + /* * Close open files and release open-file table. * This may block! */ Index: sys/kern/kern_proc.c =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/kern/kern_proc.c,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 kern_proc.c --- kern_proc.c 1997/05/09 09:34:34 1.1.1.1 +++ kern_proc.c 1997/09/27 06:31:39 @@ -49,6 +49,7 @@ #include #include #include +#include struct prochd qs[NQS]; /* as good a place as any... */ struct prochd rtqs[NQS]; /* Space for REALTIME queues too */ @@ -246,6 +247,7 @@ pgrphash[n] = pgrp; pgrp->pg_jobc = 0; pgrp->pg_mem = NULL; + SLIST_INIT(&(pgrp->pg_sigiolst)); } else if (pgrp == p->p_pgrp) return (0); @@ -317,6 +319,14 @@ register struct pgrp *pgrp; { register struct pgrp **pgp = &pgrphash[PIDHASH(pgrp->pg_id)]; + + /* + * Reset any sigio structures pointing to us as a result of + * F_SETOWN with our pgid + */ + while (pgrp->pg_sigiolst.slh_first != NULL) + (*pgrp->pg_sigiolst.slh_first->sio_unsetown) + (pgrp->pg_sigiolst.slh_first); if (pgrp->pg_session->s_ttyp != NULL && pgrp->pg_session->s_ttyp->t_pgrp == pgrp) Index: sys/kern/kern_sig.c =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/kern/kern_sig.c,v retrieving revision 1.2 diff -u -r1.2 kern_sig.c --- kern_sig.c 1997/05/13 11:58:02 1.2 +++ kern_sig.c 1997/09/26 13:02:08 @@ -93,6 +93,16 @@ ((signum) == SIGCONT && (q)->p_session == (p)->p_session)) #endif +/* + * Can real uid ruid with ucred uc send a signal to process q? + */ +#define CANSIGIO(ruid, uc, q) \ + ((uc)->cr_uid == 0 || \ + ruid == (q)->p_cred->p_ruid || \ + (uc)->cr_uid == (q)->p_cred->p_ruid || \ + ruid == (q)->p_ucred->cr_uid || \ + (uc)->cr_uid == (q)->p_ucred->cr_uid) + struct sigaction_args { int signum; struct sigaction *nsa; @@ -1241,4 +1251,27 @@ psignal(p, SIGSYS); return (EINVAL); +} + +/* + * Send a signal to a SIGIO or SIGURG to a process or process group using + * stored credentials rather than those of the current process + */ +void +sigio_p_or_pg(sigio, signum, checkctty) + register struct sigio *sigio; + int signum, checkctty; +{ + if (sigio->sio_pgid > 0) { + if (CANSIGIO(sigio->sio_ruid, sigio->sio_ucred, + sigio->sio_proc)) + psignal(sigio->sio_proc, signum); + } else if (sigio->sio_pgid < 0) { + register struct proc *p; + + for (p = sigio->sio_pgrp->pg_mem; p != NULL; p = p->p_pgrpnxt) + if (CANSIGIO(sigio->sio_ruid, sigio->sio_ucred, p) && + (checkctty == 0 || (p->p_flag & P_CONTROLT))) + psignal(p, signum); + } } Index: sys/kern/subr_log.c =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/kern/subr_log.c,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 subr_log.c --- subr_log.c 1997/05/09 09:34:34 1.1.1.1 +++ subr_log.c 1997/09/26 22:45:58 @@ -46,6 +46,7 @@ #include #include #include +#include /* get fsetown, funsetown, fgetown */ #define LOG_RDPRI (PZERO + 1) @@ -55,7 +56,8 @@ struct logsoftc { int sc_state; /* see above for possibilities */ struct selinfo sc_selp; /* process waiting on select call */ - int sc_pgid; /* process/group for async I/O */ + struct sigio sc_sigio; /* credentials and pointer to proc + * or group for SIGIO */ } logsoftc; int log_open; /* also used in log() */ @@ -72,7 +74,8 @@ if (log_open) return (EBUSY); log_open = 1; - logsoftc.sc_pgid = p->p_pid; /* signal process only */ + /* signal process only */ + fsetown(p->p_pid, &logsoftc.sc_sigio, funsetown); return (0); } @@ -86,6 +89,7 @@ log_open = 0; logsoftc.sc_state = 0; + funsetown(&logsoftc.sc_sigio); return (0); } @@ -166,12 +170,8 @@ if (!log_open) return; selwakeup(&logsoftc.sc_selp); - if (logsoftc.sc_state & LOG_ASYNC) { - if (logsoftc.sc_pgid < 0) - gsignal(-logsoftc.sc_pgid, SIGIO); - else if ((p = pfind(logsoftc.sc_pgid))) - psignal(p, SIGIO); - } + if (logsoftc.sc_state & LOG_ASYNC) + sigio_p_or_pg(&(logsoftc.sc_sigio), SIGIO, 0); if (logsoftc.sc_state & LOG_RDWAIT) { wakeup((caddr_t)msgbufp); logsoftc.sc_state &= ~LOG_RDWAIT; @@ -188,7 +188,7 @@ struct proc *p; { long l; - int s; + int s, error; switch (com) { @@ -212,12 +212,25 @@ logsoftc.sc_state &= ~LOG_ASYNC; break; + case FIOSETOWN: + error = fsetown(*(int *)data, &(logsoftc.sc_sigio), funsetown); + if (error) + return (error); + break; + + case FIOGETOWN: + *(int *)data = fgetown(&(logsoftc.sc_sigio)); + break; + case TIOCSPGRP: - logsoftc.sc_pgid = *(int *)data; + error = fsetown(-(*(int *)data), &(logsoftc.sc_sigio), + funsetown); + if (error) + return (error); break; case TIOCGPGRP: - *(int *)data = logsoftc.sc_pgid; + *(int *)data = -fgetown(&(logsoftc.sc_sigio)); break; default: Index: sys/kern/sys_generic.c =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/kern/sys_generic.c,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 sys_generic.c --- sys_generic.c 1997/05/09 09:34:34 1.1.1.1 +++ sys_generic.c 1997/09/26 10:54:41 @@ -472,36 +472,8 @@ break; case FIOSETOWN: - tmp = *(int *)data; - if (fp->f_type == DTYPE_SOCKET) { - ((struct socket *)fp->f_data)->so_pgid = tmp; - error = 0; - break; - } - if (tmp <= 0) { - tmp = -tmp; - } else { - struct proc *p1 = pfind(tmp); - if (p1 == 0) { - error = ESRCH; - break; - } - tmp = p1->p_pgrp->pg_id; - } - error = (*fp->f_ops->fo_ioctl) - (fp, (int)TIOCSPGRP, (caddr_t)&tmp, p); - break; - case FIOGETOWN: - if (fp->f_type == DTYPE_SOCKET) { - error = 0; - *(int *)data = ((struct socket *)fp->f_data)->so_pgid; - break; - } - error = (*fp->f_ops->fo_ioctl)(fp, (int)TIOCGPGRP, data, p); - *(int *)data = -*(int *)data; - break; - + /* fall through */ default: error = (*fp->f_ops->fo_ioctl)(fp, com, data, p); /* Index: sys/kern/sys_socket.c =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/kern/sys_socket.c,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 sys_socket.c --- sys_socket.c 1997/05/09 09:34:35 1.1.1.1 +++ sys_socket.c 1997/09/26 23:14:42 @@ -76,6 +76,19 @@ uio, (struct mbuf *)0, (struct mbuf *)0, 0)); } +/* + * Callback to undo FIOSETOWN on process or process group death + */ +void +soo_unsetown(sigio) + struct sigio *sigio; +{ + register int s = splnet(); + + funsetown(sigio); + splx(s); +} + int soo_ioctl(fp, cmd, data, p) struct file *fp; @@ -84,6 +97,7 @@ struct proc *p; { register struct socket *so = (struct socket *)fp->f_data; + register int s, error; switch (cmd) { @@ -110,12 +124,28 @@ *(int *)data = so->so_rcv.sb_cc; return (0); - case SIOCSPGRP: - so->so_pgid = *(int *)data; + case FIOSETOWN: + s = splnet(); + error = fsetown(*(int *)data, &(so->so_sigio), soo_unsetown); + splx(s); + return(error); + + case FIOGETOWN: + s = splnet(); + *(int *)data = fgetown(&(so->so_sigio)); + splx(s); return (0); + case SIOCSPGRP: + s = splnet(); + error = fsetown(-(*(int *)data), &(so->so_sigio), soo_unsetown); + splx(s); + return(error); + case SIOCGPGRP: - *(int *)data = so->so_pgid; + s = splnet(); + *(int *)data = -fgetown(&(so->so_sigio)); + splx(s); return (0); case SIOCATMARK: Index: sys/kern/tty.c =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/kern/tty.c,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 tty.c --- tty.c 1997/05/09 09:34:33 1.1.1.1 +++ tty.c 1997/09/26 23:28:11 @@ -86,6 +86,7 @@ #include #include #include +#include /* get fsetown, funsetown, fgetown */ #if NSNP > 0 #include #endif @@ -237,10 +238,13 @@ { int s; + s = spltty(); if (constty == tp) constty = NULL; + funsetown(&tp->t_sigio); + ttyflush(tp, FREAD | FWRITE); clist_free_cblocks(&tp->t_canq); clist_free_cblocks(&tp->t_outq); @@ -690,6 +694,19 @@ } /* + * Callback to undo FIOSETOWN on process or process group death + */ +void +tty_unsetown(sigio) + struct sigio *sigio; +{ + register int s = spltty(); + + funsetown(sigio); + splx(s); +} + +/* * Ioctls for all tty devices. Called after line-discipline specific ioctl * has been called to do discipline-specific functions and/or reject any * of these ioctl commands. @@ -758,6 +775,21 @@ *(int *)data = ttnread(tp); splx(s); break; + case FIOSETOWN: + if (tp->t_session != NULL && !isctty(p, tp)) + return (ENOTTY); + s = spltty(); + error = fsetown(*(int *)data, &(tp->t_sigio), tty_unsetown); + splx(s); + if (error) + return (error); + break; + case FIOGETOWN: + if (tp->t_session != NULL && !isctty(p, tp)) + return (ENOTTY); + *(int *)data = fgetown(&(tp->t_sigio)); + break; + case TIOCEXCL: /* set exclusive use of tty */ s = spltty(); SET(tp->t_state, TS_XCLUDE); @@ -2077,7 +2109,7 @@ if (tp->t_rsel.si_pid != 0) selwakeup(&tp->t_rsel); if (ISSET(tp->t_state, TS_ASYNC)) - pgsignal(tp->t_pgrp, SIGIO, 1); + sigio_p_or_pg(&(tp->t_sigio), SIGIO, (tp->t_session != NULL)); wakeup(TSA_HUP_OR_INPUT(tp)); } Index: sys/kern/uipc_socket.c =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/kern/uipc_socket.c,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 uipc_socket.c --- uipc_socket.c 1997/05/09 09:34:35 1.1.1.1 +++ uipc_socket.c 1997/09/26 11:43:25 @@ -166,6 +166,8 @@ int s = splnet(); /* conservative */ int error = 0; + funsetown(&(so->so_sigio)); + if (so->so_options & SO_ACCEPTCONN) { while (so->so_q0) (void) soabort(so->so_q0); @@ -1052,9 +1054,6 @@ { struct proc *p; - if (so->so_pgid < 0) - gsignal(-so->so_pgid, SIGURG); - else if (so->so_pgid > 0 && (p = pfind(so->so_pgid)) != 0) - psignal(p, SIGURG); + sigio_p_or_pg(&(so->so_sigio), SIGURG, 0); selwakeup(&so->so_rcv.sb_sel); } Index: sys/kern/uipc_socket2.c =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/kern/uipc_socket2.c,v retrieving revision 1.1.1.2 diff -u -r1.1.1.2 uipc_socket2.c --- uipc_socket2.c 1997/05/10 08:30:30 1.1.1.2 +++ uipc_socket2.c 1997/09/26 22:50:38 @@ -177,7 +177,7 @@ so->so_state = head->so_state | SS_NOFDREF; so->so_proto = head->so_proto; so->so_timeo = head->so_timeo; - so->so_pgid = head->so_pgid; + fsetown(fgetown(&(head->so_sigio)), &(so->so_sigio), soo_unsetown); (void) soreserve(so, head->so_snd.sb_hiwat, head->so_rcv.sb_hiwat); soqinsque(head, so, soqueue); if ((*so->so_proto->pr_usrreq)(so, PRU_ATTACH, @@ -328,10 +328,7 @@ wakeup((caddr_t)&sb->sb_cc); } if (so->so_state & SS_ASYNC) { - if (so->so_pgid < 0) - gsignal(-so->so_pgid, SIGIO); - else if (so->so_pgid > 0 && (p = pfind(so->so_pgid)) != 0) - psignal(p, SIGIO); + sigio_p_or_pg(&(so->so_sigio), SIGIO, 0); } } Index: sys/net/bpf.c =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/net/bpf.c,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 bpf.c --- bpf.c 1997/05/09 09:34:44 1.1.1.1 +++ bpf.c 1997/09/27 00:11:57 @@ -59,6 +59,7 @@ #include #include #include +#include /* get fsetown, funsetown, fgetown */ #include #if defined(sparc) && BSD < 199103 @@ -352,6 +353,7 @@ register int s; s = splimp(); + funsetown(&d->bd_sigio); if (d->bd_bif) bpf_detachd(d); splx(s); @@ -510,10 +512,7 @@ wakeup((caddr_t)d); if (d->bd_async && d->bd_sig) - if (d->bd_pgid > 0) - gsignal (d->bd_pgid, d->bd_sig); - else if (p = pfind (-d->bd_pgid)) - psignal (p, d->bd_sig); + sigio_p_or_pg(&(d->bd_sigio), d->bd_sig, 0); #if BSD >= 199103 selwakeup(&d->bd_sel); @@ -589,6 +588,19 @@ } /* + * Callback to undo FIOSETOWN on process or process group death + */ +static void +bpfunsetown(sigio) + struct sigio *sigio; +{ + register int s = splimp(); + + funsetown(sigio); + splx(s); +} + +/* * FIONREAD Check for read packet available. * SIOCGIFADDR Get interface address - convenient hook to driver. * BIOCGBLEN Get buffer len [for read()]. @@ -815,18 +827,28 @@ d->bd_async = *(int *)addr; break; -/* N.B. ioctl (FIOSETOWN) and fcntl (F_SETOWN) both end up doing the - equivalent of a TIOCSPGRP and hence end up here. *However* TIOCSPGRP's arg - is a process group if it's positive and a process id if it's negative. This - is exactly the opposite of what the other two functions want! Therefore - there is code in ioctl and fcntl to negate the arg before calling here. */ + case FIOSETOWN: + s = splimp(); + error = fsetown(*(int *)addr, &(d->bd_sigio), bpfunsetown); + splx(s); + break; - case TIOCSPGRP: /* Process or group to send signals to */ - d->bd_pgid = *(int *)addr; + case FIOGETOWN: + s = splimp(); + *(int *)addr = fgetown(&(d->bd_sigio)); + splx(s); + break; + + case TIOCSPGRP: + s = splimp(); + error = fsetown(-(*(int *)addr), &(d->bd_sigio), bpfunsetown); + splx(s); break; case TIOCGPGRP: - *(int *)addr = d->bd_pgid; + s = splimp(); + *(int *)addr = -fgetown(&(d->bd_sigio)); + splx(s); break; case BIOCSRSIG: /* Set receive signal */ Index: sys/net/bpfdesc.h =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/net/bpfdesc.h,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 bpfdesc.h --- bpfdesc.h 1997/05/09 09:34:45 1.1.1.1 +++ bpfdesc.h 1997/09/26 14:06:51 @@ -44,6 +44,7 @@ #define _NET_BPFDESC_H_ #include +#include /* pick up sigio */ /* * Descriptor associated with each open bpf file. @@ -78,7 +79,8 @@ u_char bd_immediate; /* true to return on packet arrival */ int bd_async; /* non-zero if packet reception should generate signal */ int bd_sig; /* signal to send upon packet reception */ - pid_t bd_pgid; /* process or group id for signal */ + struct sigio bd_sigio; /* credentials and pointer to proc + * or group for SIGIO */ #if BSD < 199103 u_char bd_selcoll; /* true if selects collide */ int bd_timedout; Index: sys/net/if_tun.c =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/net/if_tun.c,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 if_tun.c --- if_tun.c 1997/05/09 09:34:46 1.1.1.1 +++ if_tun.c 1997/09/27 00:18:39 @@ -30,6 +30,7 @@ #include #include #include +#include #ifdef __FreeBSD__ #include #endif @@ -194,7 +195,7 @@ } splx(s); } - tp->tun_pgrp = 0; + funsetown(&tp->tun_sigio); selwakeup(&tp->tun_rsel); TUNDEBUG ("%s%d: closed\n", ifp->if_name, ifp->if_unit); @@ -359,11 +360,8 @@ tp->tun_flags &= ~TUN_RWAIT; wakeup((caddr_t)tp); } - if (tp->tun_flags & TUN_ASYNC && tp->tun_pgrp) { - if (tp->tun_pgrp > 0) - gsignal(tp->tun_pgrp, SIGIO); - else if (p = pfind(-tp->tun_pgrp)) - psignal(p, SIGIO); + if (tp->tun_flags & TUN_ASYNC) { + sigio_p_or_pg(&(tp->tun_sigio), SIGIO, 0); } selwakeup(&tp->tun_rsel); return 0; @@ -423,12 +421,20 @@ *(int *)data = 0; splx(s); break; + case FIOSETOWN: + return (fsetown(*(int *)data, &(tp->tun_sigio), funsetown)); + + case FIOGETOWN: + *(int *)data = fgetown(&(tp->tun_sigio)); + return (0); + case TIOCSPGRP: - tp->tun_pgrp = *(int *)data; - break; + return (fsetown(-(*(int *)data), &(tp->tun_sigio), funsetown)); + case TIOCGPGRP: - *(int *)data = tp->tun_pgrp; - break; + *(int *)data = -fgetown(&(tp->tun_sigio)); + return (0); + default: return (ENOTTY); } Index: sys/net/if_tun.h =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/net/if_tun.h,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 if_tun.h --- if_tun.h 1997/05/09 09:34:46 1.1.1.1 +++ if_tun.h 1997/09/26 12:25:04 @@ -33,7 +33,8 @@ #define TUN_READY (TUN_OPEN | TUN_INITED | TUN_IASET) struct ifnet tun_if; /* the interface */ - int tun_pgrp; /* the process group - if any */ + struct sigio tun_sigio; /* credentials and pointer to proc + * or group for SIGIO */ struct selinfo tun_rsel; /* read select */ struct selinfo tun_wsel; /* write select (not used) */ #if NBPFILTER > 0 Index: sys/sys/filedesc.h =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/sys/filedesc.h,v retrieving revision 1.2 diff -u -r1.2 filedesc.h --- filedesc.h 1997/05/13 12:05:40 1.2 +++ filedesc.h 1997/09/26 22:39:20 @@ -96,6 +96,9 @@ /* * Kernel global variables and routines. */ +void funsetown __P((struct sigio *)); +int fsetown __P((pid_t, struct sigio *, void (*) (struct sigio *) )); +pid_t fgetown __P((struct sigio *)); int dupfdopen __P((struct filedesc *, int, int, int, int)); int fdalloc __P((struct proc *p, int want, int *result)); int fdavail __P((struct proc *p, int n)); Index: sys/sys/proc.h =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/sys/proc.h,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 proc.h --- proc.h 1997/05/09 09:35:08 1.1.1.1 +++ proc.h 1997/09/26 22:37:25 @@ -46,6 +46,7 @@ #include /* For struct rtprio. */ #include /* For struct selinfo. */ #include /* For structs itimerval, timeval. */ +#include /* For SLIST_HEAD, SLIST_ENTRY. */ /* * One structure allocated per session. @@ -58,6 +59,8 @@ char s_login[MAXLOGNAME]; /* Setlogin() name. */ }; +SLIST_HEAD(sigiolst, sigio); + /* * One structure allocated per process group. */ @@ -65,6 +68,7 @@ struct pgrp *pg_hforw; /* Forward link in hash bucket. */ struct proc *pg_mem; /* Pointer to pgrp members. */ struct session *pg_session; /* Pointer to session. */ + struct sigiolst pg_sigiolst; /* List of sigio sources */ pid_t pg_id; /* Pgrp id. */ int pg_jobc; /* # procs qualifying pgrp for job control */ }; @@ -136,9 +140,11 @@ struct vnode *p_textvp; /* Vnode of executable. */ + struct sigiolst p_sigiolst; /* List of sigio sources */ + char p_lock; /* Process lock count. */ char p_pad2[3]; /* alignment */ - long p_spare[2]; /* Pad to 256, avoid shifting eproc. XXX */ + long p_spare[1]; /* Pad to 256, avoid shifting eproc. XXX */ /* End area that is zeroed on creation. */ #define p_endzero p_startcopy @@ -221,6 +227,24 @@ gid_t p_svgid; /* Saved effective group id. */ int p_refcnt; /* Number of references. */ }; + +/* + * + */ +struct sigio { + struct ucred *sio_ucred; /* Current credentials. */ + uid_t sio_ruid; /* Real user id. */ + union { + struct proc *siu_proc; /* Process to receive SIGIO/SIGURG */ + struct pgrp *siu_pgrp; /* Process group to receive ... */ + } sio_u; + void (*sio_unsetown) __P((struct sigio *)); + SLIST_ENTRY(sigio) sio_pgsigio; /* sigio's for process or group */ + pid_t sio_pgid; /* pgid for signals */ +}; +#define sio_proc sio_u.siu_proc +#define sio_pgrp sio_u.siu_pgrp + #ifdef KERNEL /* Index: sys/sys/signalvar.h =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/sys/signalvar.h,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 signalvar.h --- signalvar.h 1997/05/09 09:35:09 1.1.1.1 +++ signalvar.h 1997/09/26 13:24:15 @@ -162,6 +162,7 @@ void sigexit __P((struct proc *, int)); void siginit __P((struct proc *p)); void trapsignal __P((struct proc *p, int sig, unsigned code)); +void sigio_p_or_pg __P((struct sigio *, int signum, int checkctty)); /* * Machine-dependent functions: Index: sys/sys/socketvar.h =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/sys/socketvar.h,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 socketvar.h --- socketvar.h 1997/05/09 09:35:09 1.1.1.1 +++ socketvar.h 1997/09/26 22:52:13 @@ -40,6 +40,7 @@ #include /* for struct stat */ #include /* for struct filedesc */ #include /* for struct selinfo */ +#include /* for struct sigio */ /* * Kernel structure per socket. @@ -73,7 +74,8 @@ short so_qlimit; /* max number queued connections */ short so_timeo; /* connection timeout */ u_short so_error; /* error affecting connection */ - pid_t so_pgid; /* pgid for signals */ + struct sigio so_sigio; /* credentials and pointer to proc + * or group for SIGIO/SIGURG */ u_long so_oobmark; /* chars to oob mark */ /* * Variables for socket buffering. @@ -211,6 +213,7 @@ int soo_select __P((struct file *fp, int which, struct proc *p)); int soo_close __P((struct file *fp, struct proc *p)); int soo_stat __P((struct socket *, struct stat *)); +void soo_unsetown __P((struct sigio *)); /* * From uipc_socket and friends Index: sys/sys/tty.h =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/sys/tty.h,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 tty.h --- tty.h 1997/05/09 09:35:10 1.1.1.1 +++ tty.h 1997/09/26 13:00:15 @@ -44,6 +44,7 @@ #include #include /* For struct selinfo. */ +#include /* For struct sigio */ /* * Clists are character lists, which is a variable length linked list @@ -79,6 +80,8 @@ int t_timeout; /* Timeout for ttywait() */ struct pgrp *t_pgrp; /* Foreground process group. */ struct session *t_session; /* Enclosing session. */ + struct sigio t_sigio; /* credentials and pointer to proc + * or group for SIGIO */ struct selinfo t_rsel; /* Tty read/oob select. */ struct selinfo t_wsel; /* Tty write select. */ struct termios t_termios; /* Termios state. */ Index: sys/sys/types.h =================================================================== RCS file: /usr/cvs/repository/FreeBSD-2.1/sys/sys/types.h,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 types.h --- types.h 1997/05/09 09:35:10 1.1.1.1 +++ types.h 1997/09/26 13:41:46 @@ -157,6 +157,7 @@ */ struct proc; struct pgrp; +struct sigio; struct ucred; struct rusage; struct file;