diff options
author | melifaro <melifaro@FreeBSD.org> | 2012-12-02 21:43:37 +0000 |
---|---|---|
committer | melifaro <melifaro@FreeBSD.org> | 2012-12-02 21:43:37 +0000 |
commit | 20cedcd3c8fa7521244fd9cba3bc2a20b48e646c (patch) | |
tree | b4fcf28f757f9d62d7b372a84a74d65f27a187ea /sys/net | |
parent | 39db48f0b80b6bbb3c4078623889926be8068cb0 (diff) | |
download | FreeBSD-src-20cedcd3c8fa7521244fd9cba3bc2a20b48e646c.zip FreeBSD-src-20cedcd3c8fa7521244fd9cba3bc2a20b48e646c.tar.gz |
Fix bpf_if structure leak introduced in r235745.
Move all such structures to delayed-free lists and
delete all matching on interface departure event.
MFC after: 1 week
Diffstat (limited to 'sys/net')
-rw-r--r-- | sys/net/bpf.c | 118 |
1 files changed, 66 insertions, 52 deletions
diff --git a/sys/net/bpf.c b/sys/net/bpf.c index cc76314..79ea524 100644 --- a/sys/net/bpf.c +++ b/sys/net/bpf.c @@ -141,7 +141,7 @@ struct bpf_dltlist32 { * structures registered by different layers in the stack (i.e., 802.11 * frames, ethernet frames, etc). */ -static LIST_HEAD(, bpf_if) bpf_iflist; +static LIST_HEAD(, bpf_if) bpf_iflist, bpf_freelist; static struct mtx bpf_mtx; /* bpf global lock */ static int bpf_bpfd_cnt; @@ -2491,52 +2491,51 @@ bpfattach2(struct ifnet *ifp, u_int dlt, u_int hdrlen, struct bpf_if **driverp) void bpfdetach(struct ifnet *ifp) { - struct bpf_if *bp; + struct bpf_if *bp, *bp_temp; struct bpf_d *d; -#ifdef INVARIANTS int ndetached; ndetached = 0; -#endif BPF_LOCK(); /* Find all bpf_if struct's which reference ifp and detach them. */ - do { - LIST_FOREACH(bp, &bpf_iflist, bif_next) { - if (ifp == bp->bif_ifp) - break; - } - if (bp != NULL) - LIST_REMOVE(bp, bif_next); + LIST_FOREACH_SAFE(bp, &bpf_iflist, bif_next, bp_temp) { + if (ifp != bp->bif_ifp) + continue; - if (bp != NULL) { -#ifdef INVARIANTS - ndetached++; -#endif - while ((d = LIST_FIRST(&bp->bif_dlist)) != NULL) { - bpf_detachd_locked(d); - BPFD_LOCK(d); - bpf_wakeup(d); - BPFD_UNLOCK(d); - } - /* Free writer-only descriptors */ - while ((d = LIST_FIRST(&bp->bif_wlist)) != NULL) { - bpf_detachd_locked(d); - BPFD_LOCK(d); - bpf_wakeup(d); - BPFD_UNLOCK(d); - } + LIST_REMOVE(bp, bif_next); + /* Add to to-be-freed list */ + LIST_INSERT_HEAD(&bpf_freelist, bp, bif_next); - /* - * Delay freing bp till interface is detached - * and all routes through this interface are removed. - * Mark bp as detached to restrict new consumers. - */ - BPFIF_WLOCK(bp); - bp->flags |= BPFIF_FLAG_DYING; - BPFIF_WUNLOCK(bp); + ndetached++; + /* + * Delay freeing bp till interface is detached + * and all routes through this interface are removed. + * Mark bp as detached to restrict new consumers. + */ + BPFIF_WLOCK(bp); + bp->flags |= BPFIF_FLAG_DYING; + BPFIF_WUNLOCK(bp); + + CTR4(KTR_NET, "%s: sheduling free for encap %d (%p) for if %p", + __func__, bp->bif_dlt, bp, ifp); + + /* Free common descriptors */ + while ((d = LIST_FIRST(&bp->bif_dlist)) != NULL) { + bpf_detachd_locked(d); + BPFD_LOCK(d); + bpf_wakeup(d); + BPFD_UNLOCK(d); } - } while (bp != NULL); + + /* Free writer-only descriptors */ + while ((d = LIST_FIRST(&bp->bif_wlist)) != NULL) { + bpf_detachd_locked(d); + BPFD_LOCK(d); + bpf_wakeup(d); + BPFD_UNLOCK(d); + } + } BPF_UNLOCK(); #ifdef INVARIANTS @@ -2548,32 +2547,46 @@ bpfdetach(struct ifnet *ifp) /* * Interface departure handler. * Note departure event does not guarantee interface is going down. + * Interface renaming is currently done via departure/arrival event set. + * + * Departure handled is called after all routes pointing to + * given interface are removed and interface is in down state + * restricting any packets to be sent/received. We assume it is now safe + * to free data allocated by BPF. */ static void bpf_ifdetach(void *arg __unused, struct ifnet *ifp) { - struct bpf_if *bp; + struct bpf_if *bp, *bp_temp; + int nmatched = 0; BPF_LOCK(); - if ((bp = ifp->if_bpf) == NULL) { - BPF_UNLOCK(); - return; - } + /* + * Find matching entries in free list. + * Nothing should be found if bpfdetach() was not called. + */ + LIST_FOREACH_SAFE(bp, &bpf_freelist, bif_next, bp_temp) { + if (ifp != bp->bif_ifp) + continue; - /* Check if bpfdetach() was called previously */ - if ((bp->flags & BPFIF_FLAG_DYING) == 0) { - BPF_UNLOCK(); - return; - } + CTR3(KTR_NET, "%s: freeing BPF instance %p for interface %p", + __func__, bp, ifp); + + LIST_REMOVE(bp, bif_next); - CTR3(KTR_NET, "%s: freing BPF instance %p for interface %p", - __func__, bp, ifp); + rw_destroy(&bp->bif_lock); + free(bp, M_BPF); - ifp->if_bpf = NULL; + nmatched++; + } BPF_UNLOCK(); - rw_destroy(&bp->bif_lock); - free(bp, M_BPF); + /* + * Note that we cannot zero other pointers to + * custom DLTs possibly used by given interface. + */ + if (nmatched != 0) + ifp->if_bpf = NULL; } /* @@ -2653,6 +2666,7 @@ bpf_drvinit(void *unused) mtx_init(&bpf_mtx, "bpf global lock", NULL, MTX_DEF); LIST_INIT(&bpf_iflist); + LIST_INIT(&bpf_freelist); dev = make_dev(&bpf_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "bpf"); /* For compatibility */ |