summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Marriott <nicm@cvs.openbsd.org>2013-04-24 09:52:55 +0000
committerNicholas Marriott <nicm@cvs.openbsd.org>2013-04-24 09:52:55 +0000
commitc498326329acbd7489c0d49fc52aa934fc02560c (patch)
treee6a2236735199bd55e727eacd7d3fc0d30ba07f7
parente6d151cfc0936be5a5f780959d71b0a30a26d2ac (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.c11
-rw-r--r--sys/kern/tty.c53
-rw-r--r--sys/sys/event.h3
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 *);