Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F142786359
D53438.id165374.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
2 KB
Referenced Files
None
Subscribers
None
D53438.id165374.diff
View Options
diff --git a/sys/net/if_tuntap.c b/sys/net/if_tuntap.c
--- a/sys/net/if_tuntap.c
+++ b/sys/net/if_tuntap.c
@@ -649,7 +649,7 @@
error = cv_wait_sig(&tp->tun_cv, &tp->tun_mtx);
else
cv_wait(&tp->tun_cv, &tp->tun_mtx);
- if (error != 0) {
+ if (error != 0 && tp->tun_busy != 0) {
tp->tun_flags &= ~TUN_DYING;
TUN_UNLOCK(tp);
return (error);
@@ -661,10 +661,21 @@
mtx_lock(&tunmtx);
TAILQ_REMOVE(&tunhead, tp, tun_list);
- mtx_unlock(&tunmtx);
- /* destroy_dev will take care of any alias. */
- destroy_dev(tp->tun_dev);
+ /*
+ * destroy_dev() will take care of any alias. For transient tunnels,
+ * we're being called from close(2) so we can't destroy it ourselves
+ * without deadlocking, but we already know that we can cleanup
+ * everything else and just continue to prevent it from being reopened.
+ */
+ if ((tp->tun_flags & TUN_TRANSIENT) != 0) {
+ tp->tun_dev->si_drv1 = tp->tun_dev;
+ mtx_unlock(&tunmtx);
+ destroy_dev_sched(tp->tun_dev);
+ } else {
+ mtx_unlock(&tunmtx);
+ destroy_dev(tp->tun_dev);
+ }
seldrain(&tp->tun_rsel);
knlist_clear(&tp->tun_rsel.si_note, 0);
knlist_destroy(&tp->tun_rsel.si_note);
@@ -742,6 +753,7 @@
mtx_unlock(&tunmtx);
for (i = 0; i < nitems(tuntap_drivers); ++i) {
drv = &tuntap_drivers[i];
+ destroy_dev_drain(&drv->cdevsw);
delete_unrhdr(drv->unrhdr);
clone_cleanup(&drv->clones);
}
@@ -1116,17 +1128,45 @@
return (error); /* Shouldn't happen */
}
+ /*
+ * Transient tunnels do deferred destroy of the tun device but want
+ * to immediately cleanup state, so they clobber si_drv1 to avoid a
+ * use-after-free in case someone does happen to open it in the interim.
+ * We avoid using NULL to be able to distinguish from an uninitialized
+ * cdev.
+ *
+ * tun_destroy() takes the tunmtx to do the swap so that we can safely
+ * handle a concurrent tunopen(). If it hasn't been swapped out yet,
+ * then we can safely take its lock and check if it's dying without
+ * risk as long as we're still holding the tunmtx.
+ */
+ mtx_lock(&tunmtx);
+ if (dev->si_drv1 == dev) {
+ mtx_unlock(&tunmtx);
+ CURVNET_RESTORE();
+ return (ENXIO);
+ }
+
tp = dev->si_drv1;
KASSERT(tp != NULL,
("si_drv1 should have been initialized at creation"));
TUN_LOCK(tp);
+ if ((tp->tun_flags & TUN_DYING) != 0) {
+ TUN_UNLOCK(tp);
+ mtx_unlock(&tunmtx);
+ CURVNET_RESTORE();
+ return (EBUSY);
+ }
+
+ mtx_unlock(&tunmtx);
+
if ((tp->tun_flags & TUN_INITED) == 0) {
TUN_UNLOCK(tp);
CURVNET_RESTORE();
return (ENXIO);
}
- if ((tp->tun_flags & (TUN_OPEN | TUN_DYING)) != 0) {
+ if ((tp->tun_flags & TUN_OPEN) != 0) {
TUN_UNLOCK(tp);
CURVNET_RESTORE();
return (EBUSY);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Jan 24, 3:09 PM (7 h, 10 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
24497867
Default Alt Text
D53438.id165374.diff (2 KB)
Attached To
Mode
D53438: if_tuntap: defer transient destroy_dev() to a taskqueue
Attached
Detach File
Event Timeline
Log In to Comment