From 0192a491ad7b4d97446338be8bc730bb663f4ec9 Mon Sep 17 00:00:00 2001 From: Taylor R Campbell Date: Mon, 6 Aug 2018 03:26:41 +0000 Subject: [PATCH] Fix tun(4) kevent locking. filt_tunread gets called in two contexts: - by calls to selnotify in if_tun.c (or knote, as the case may be, but not here), in which case tp->tun_lock is held; and - by internal logic in kevent, in which tp->tun_lock is not held. The standard convention to discriminate between these two cases is by setting the kernel-only NOTE_SUBMIT bit in the hint to selnotify or knote; then in filt_*: if (hint & NOTE_SUBMIT) KASSERT(mutex_owned(&tp->tun_lock)); else mutex_enter(&tp->tun_lock); ... if (hint & NOTE_SUBMIT) KASSERT(mutex_owned(&tp->tun_lock)); else mutex_exit(&tp->tun_lock); --- sys/net/if_tun.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/sys/net/if_tun.c b/sys/net/if_tun.c index cf8f9db99ea1..e4d97333de70 100644 --- a/sys/net/if_tun.c +++ b/sys/net/if_tun.c @@ -288,7 +288,7 @@ tun_clone_destroy(struct ifnet *ifp) tp->tun_flags &= ~TUN_RWAIT; cv_broadcast(&tp->tun_cv); } - selnotify(&tp->tun_rsel, 0, 0); + selnotify(&tp->tun_rsel, 0, NOTE_SUBMIT); mutex_exit(&tp->tun_lock); @@ -381,7 +381,7 @@ tunclose(dev_t dev, int flag, int mode, tp->tun_flags &= ~TUN_OPEN; tp->tun_pgid = 0; - selnotify(&tp->tun_rsel, 0, 0); + selnotify(&tp->tun_rsel, 0, NOTE_SUBMIT); TUNDEBUG ("%s: closed\n", ifp->if_xname); mutex_exit(&tp->tun_lock); @@ -625,7 +625,7 @@ tun_output(struct ifnet *ifp, struct mbuf *m0, const struct sockaddr *dst, if (tp->tun_flags & TUN_ASYNC && tp->tun_pgid) softint_schedule(tp->tun_isih); - selnotify(&tp->tun_rsel, 0, 0); + selnotify(&tp->tun_rsel, 0, NOTE_SUBMIT); mutex_exit(&tp->tun_lock); out: @@ -996,7 +996,7 @@ tunstart(struct ifnet *ifp) if (tp->tun_flags & TUN_ASYNC && tp->tun_pgid) softint_schedule(tp->tun_osih); - selnotify(&tp->tun_rsel, 0, 0); + selnotify(&tp->tun_rsel, 0, NOTE_SUBMIT); } mutex_exit(&tp->tun_lock); } @@ -1057,17 +1057,24 @@ filt_tunread(struct knote *kn, long hint) struct tun_softc *tp = kn->kn_hook; struct ifnet *ifp = &tp->tun_if; struct mbuf *m; + int ready; - KASSERT(mutex_owned(&tp->tun_lock)); + if (hint == NOTE_SUBMIT) + KASSERT(mutex_owned(&tp->tun_lock)); + else + mutex_enter(&tp->tun_lock); IF_POLL(&ifp->if_snd, m); - if (m == NULL) - return 0; - + ready = (m != NULL); for (kn->kn_data = 0; m != NULL; m = m->m_next) kn->kn_data += m->m_len; - return 1; + if (hint == NOTE_SUBMIT) + KASSERT(mutex_owned(&tp->tun_lock)); + else + mutex_exit(&tp->tun_lock); + + return ready; } static const struct filterops tunread_filtops = { -- 2.11.0