Consumers of bpfdetach() expect it to remove all bpf_if structs from the

bpf_iflist list which reference the specified ifnet. The existing implementation
only removes the first matching bpf_if found in the list, effectively leaking
list entries if an ifnet has been bpfattach()ed multiple times with different
DLTs.

Fix the leak by performing the detach logic in a loop, stopping when all bpf_if
structs referencing the specified ifnet have been detached and removed from the
bpf_iflist list.

Whilst here, also:

- Remove the unnecessary "bp->bif_ifp == NULL" check, as a bpf_if should never
  exist in the list with a NULL ifnet pointer.

- Except when INVARIANTS is in the kernel config, silently ignore the case where
  no bpf_if referencing the specified ifnet is found, as it is harmless and does
  not require user attention.

Reviewed by:	csjp
MFC after:	1 week
This commit is contained in:
Lawrence Stewart 2012-01-10 00:48:29 +00:00
parent ee5f87f458
commit 9a7e6bac47
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=229898

View File

@ -2253,33 +2253,42 @@ bpfdetach(struct ifnet *ifp)
{
struct bpf_if *bp;
struct bpf_d *d;
#ifdef INVARIANTS
int ndetached;
/* Locate BPF interface information */
mtx_lock(&bpf_mtx);
LIST_FOREACH(bp, &bpf_iflist, bif_next) {
if (ifp == bp->bif_ifp)
break;
}
ndetached = 0;
#endif
/* Interface wasn't attached */
if ((bp == NULL) || (bp->bif_ifp == NULL)) {
/* Find all bpf_if struct's which reference ifp and detach them. */
do {
mtx_lock(&bpf_mtx);
LIST_FOREACH(bp, &bpf_iflist, bif_next) {
if (ifp == bp->bif_ifp)
break;
}
if (bp != NULL)
LIST_REMOVE(bp, bif_next);
mtx_unlock(&bpf_mtx);
if (bp != NULL) {
#ifdef INVARIANTS
ndetached++;
#endif
while ((d = LIST_FIRST(&bp->bif_dlist)) != NULL) {
bpf_detachd(d);
BPFD_LOCK(d);
bpf_wakeup(d);
BPFD_UNLOCK(d);
}
mtx_destroy(&bp->bif_mtx);
free(bp, M_BPF);
}
} while (bp != NULL);
#ifdef INVARIANTS
if (ndetached == 0)
printf("bpfdetach: %s was not attached\n", ifp->if_xname);
return;
}
LIST_REMOVE(bp, bif_next);
mtx_unlock(&bpf_mtx);
while ((d = LIST_FIRST(&bp->bif_dlist)) != NULL) {
bpf_detachd(d);
BPFD_LOCK(d);
bpf_wakeup(d);
BPFD_UNLOCK(d);
}
mtx_destroy(&bp->bif_mtx);
free(bp, M_BPF);
#endif
}
/*