diff options
author | Nicholas Marriott <nicm@cvs.openbsd.org> | 2013-04-24 09:52:55 +0000 |
---|---|---|
committer | Nicholas Marriott <nicm@cvs.openbsd.org> | 2013-04-24 09:52:55 +0000 |
commit | c498326329acbd7489c0d49fc52aa934fc02560c (patch) | |
tree | e6a2236735199bd55e727eacd7d3fc0d30ba07f7 | |
parent | e6d151cfc0936be5a5f780959d71b0a30a26d2ac (diff) |
When a ucom(4) is removed, it frees the tty with ttyfree(). However if
anyone is waiting with kqueue their knotes may still have a reference to
the tty and later try to use it in the filt_tty* functions.
To avoid this, walk the knotes in ttyfree(), remove them from the tty's
list and invalidate them by setting kn_hook to NODEV. The filter
functions can then check for this and safely ignore the knotes.
ok tedu matthieu
-rw-r--r-- | sys/kern/kern_event.c | 11 | ||||
-rw-r--r-- | sys/kern/tty.c | 53 | ||||
-rw-r--r-- | sys/sys/event.h | 3 |
3 files changed, 58 insertions, 9 deletions
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c index dc99aa7b449..9a57e8e6672 100644 --- a/sys/kern/kern_event.c +++ b/sys/kern/kern_event.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_event.c,v 1.48 2012/07/08 17:21:08 guenther Exp $ */ +/* $OpenBSD: kern_event.c,v 1.49 2013/04/24 09:52:54 nicm Exp $ */ /*- * Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org> @@ -941,6 +941,15 @@ kqueue_wakeup(struct kqueue *kq) } /* + * activate one knote. + */ +void +knote_activate(struct knote *kn) +{ + KNOTE_ACTIVATE(kn); +} + +/* * walk down a list of knotes, activating them if their event has triggered. */ void diff --git a/sys/kern/tty.c b/sys/kern/tty.c index 45f7283658c..48e3ed66f7d 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty.c,v 1.96 2013/01/17 21:24:58 deraadt Exp $ */ +/* $OpenBSD: tty.c,v 1.97 2013/04/24 09:52:54 nicm Exp $ */ /* $NetBSD: tty.c,v 1.68.4.2 1996/06/06 16:04:52 thorpej Exp $ */ /*- @@ -71,6 +71,7 @@ void ttyunblock(struct tty *); static void ttyecho(int, struct tty *); static void ttyrubo(struct tty *, int); static int proc_compare(struct proc *, struct proc *); +void ttkqflush(struct klist *klist); int filt_ttyread(struct knote *kn, long hint); void filt_ttyrdetach(struct knote *kn); int filt_ttywrite(struct knote *kn, long hint); @@ -1113,12 +1114,30 @@ ttkqfilter(dev_t dev, struct knote *kn) } void +ttkqflush(struct klist *klist) +{ + struct knote *kn, *kn1; + + SLIST_FOREACH_SAFE(kn, klist, kn_selnext, kn1) { + SLIST_REMOVE(klist, kn, knote, kn_selnext); + kn->kn_hook = (caddr_t)((u_long)NODEV); + kn->kn_flags |= EV_EOF; + knote_activate(kn); + } +} + +void filt_ttyrdetach(struct knote *kn) { dev_t dev = (dev_t)((u_long)kn->kn_hook); - struct tty *tp = (*cdevsw[major(dev)].d_tty)(dev); - int s = spltty(); + struct tty *tp; + int s; + + if (dev == NODEV) + return; + tp = (*cdevsw[major(dev)].d_tty)(dev); + s = spltty(); SLIST_REMOVE(&tp->t_rsel.si_note, kn, knote, kn_selnext); splx(s); } @@ -1127,9 +1146,15 @@ int filt_ttyread(struct knote *kn, long hint) { dev_t dev = (dev_t)((u_long)kn->kn_hook); - struct tty *tp = (*cdevsw[major(dev)].d_tty)(dev); + struct tty *tp; int s; + if (dev == NODEV) { + kn->kn_flags |= EV_EOF; + return (1); + } + tp = (*cdevsw[major(dev)].d_tty)(dev); + s = spltty(); kn->kn_data = ttnread(tp); splx(s); @@ -1144,9 +1169,14 @@ void filt_ttywdetach(struct knote *kn) { dev_t dev = (dev_t)((u_long)kn->kn_hook); - struct tty *tp = (*cdevsw[major(dev)].d_tty)(dev); - int s = spltty(); + struct tty *tp; + int s; + + if (dev == NODEV) + return; + tp = (*cdevsw[major(dev)].d_tty)(dev); + s = spltty(); SLIST_REMOVE(&tp->t_wsel.si_note, kn, knote, kn_selnext); splx(s); } @@ -1155,9 +1185,15 @@ int filt_ttywrite(struct knote *kn, long hint) { dev_t dev = (dev_t)((u_long)kn->kn_hook); - struct tty *tp = (*cdevsw[major(dev)].d_tty)(dev); + struct tty *tp; int canwrite, s; + if (dev == NODEV) { + kn->kn_flags |= EV_EOF; + return (1); + } + tp = (*cdevsw[major(dev)].d_tty)(dev); + s = spltty(); kn->kn_data = tp->t_outq.c_cn - tp->t_outq.c_cc; canwrite = (tp->t_outq.c_cc <= tp->t_lowat); @@ -2309,6 +2345,9 @@ ttyfree(struct tty *tp) #endif TAILQ_REMOVE(&ttylist, tp, tty_link); + ttkqflush(&tp->t_rsel.si_note); + ttkqflush(&tp->t_wsel.si_note); + clfree(&tp->t_rawq); clfree(&tp->t_canq); clfree(&tp->t_outq); diff --git a/sys/sys/event.h b/sys/sys/event.h index fada3e779cb..96812ab59a2 100644 --- a/sys/sys/event.h +++ b/sys/sys/event.h @@ -1,4 +1,4 @@ -/* $OpenBSD: event.h,v 1.17 2012/06/08 05:22:49 guenther Exp $ */ +/* $OpenBSD: event.h,v 1.18 2013/04/24 09:52:54 nicm Exp $ */ /*- * Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org> @@ -167,6 +167,7 @@ struct knote { struct proc; extern void knote(struct klist *list, long hint); +extern void knote_activate(struct knote *); extern void knote_remove(struct proc *p, struct klist *list); extern void knote_fdclose(struct proc *p, int fd); extern void knote_processexit(struct process *); |