summaryrefslogtreecommitdiffstats
path: root/sys/net
diff options
context:
space:
mode:
authortrociny <trociny@FreeBSD.org>2012-07-09 20:38:18 +0000
committertrociny <trociny@FreeBSD.org>2012-07-09 20:38:18 +0000
commitf5f1e197556558f5b7ecb769aca02ec45c179e82 (patch)
treeb6c729c4603b2055e459ae486fddadec87945f6c /sys/net
parent640353451129e7f97c75b615a8d2021ab796de51 (diff)
downloadFreeBSD-src-f5f1e197556558f5b7ecb769aca02ec45c179e82.zip
FreeBSD-src-f5f1e197556558f5b7ecb769aca02ec45c179e82.tar.gz
In epair_clone_destroy(), when destroying the second half, we have to
switch to its vnet before calling ether_ifdetach(). Otherwise if the second half resides in a different vnet, if_detach() silently fails leaving a stale pointer in V_ifnet list, and the system crashes trying to access this pointer later. Another solution could be not to allow to destroy epair unless both ends are in the home vnet. Discussed with: bz Tested by: delphij
Diffstat (limited to 'sys/net')
-rw-r--r--sys/net/if_epair.c38
1 files changed, 20 insertions, 18 deletions
diff --git a/sys/net/if_epair.c b/sys/net/if_epair.c
index fe22ed7..2a67e18 100644
--- a/sys/net/if_epair.c
+++ b/sys/net/if_epair.c
@@ -904,39 +904,41 @@ epair_clone_destroy(struct if_clone *ifc, struct ifnet *ifp)
if_link_state_change(oifp, LINK_STATE_DOWN);
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
oifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+
+ /*
+ * Get rid of our second half. As the other of the two
+ * interfaces may reside in a different vnet, we need to
+ * switch before freeing them.
+ */
+ CURVNET_SET_QUIET(oifp->if_vnet);
ether_ifdetach(oifp);
- ether_ifdetach(ifp);
/*
* Wait for all packets to be dispatched to if_input.
- * The numbers can only go down as the interfaces are
+ * The numbers can only go down as the interface is
* detached so there is no need to use atomics.
*/
- DPRINTF("sca refcnt=%u scb refcnt=%u\n", sca->refcount, scb->refcount);
- EPAIR_REFCOUNT_ASSERT(sca->refcount == 1 && scb->refcount == 1,
- ("%s: ifp=%p sca->refcount!=1: %d || ifp=%p scb->refcount!=1: %d",
- __func__, ifp, sca->refcount, oifp, scb->refcount));
-
- /*
- * Get rid of our second half.
- */
+ DPRINTF("scb refcnt=%u\n", scb->refcount);
+ EPAIR_REFCOUNT_ASSERT(scb->refcount == 1,
+ ("%s: ifp=%p scb->refcount!=1: %d", __func__, oifp, scb->refcount));
oifp->if_softc = NULL;
error = if_clone_destroyif(ifc, oifp);
if (error)
panic("%s: if_clone_destroyif() for our 2nd iface failed: %d",
__func__, error);
+ if_free(oifp);
+ ifmedia_removeall(&scb->media);
+ free(scb, M_EPAIR);
+ CURVNET_RESTORE();
+ ether_ifdetach(ifp);
/*
- * Finish cleaning up. Free them and release the unit.
- * As the other of the two interfaces my reside in a different vnet,
- * we need to switch before freeing them.
+ * Wait for all packets to be dispatched to if_input.
*/
- CURVNET_SET_QUIET(oifp->if_vnet);
- if_free(oifp);
- CURVNET_RESTORE();
+ DPRINTF("sca refcnt=%u\n", sca->refcount);
+ EPAIR_REFCOUNT_ASSERT(sca->refcount == 1,
+ ("%s: ifp=%p sca->refcount!=1: %d", __func__, ifp, sca->refcount));
if_free(ifp);
ifmedia_removeall(&sca->media);
- ifmedia_removeall(&scb->media);
- free(scb, M_EPAIR);
free(sca, M_EPAIR);
ifc_free_unit(ifc, unit);
OpenPOWER on IntegriCloud