The advent of if_detach, allowing interface removal at runtime, makes it

possible for a panic to occur if BPF is in use on the interface at the
time of the call to if_detach.  This happens because BPF maintains pointers
to the struct ifnet describing the interface, which is freed by if_detach.

To correct this problem, a new call, bpfdetach, is introduced.  bpfdetach
locates BPF descriptor references to the interface, and NULLs them.  Other
BPF code is modified so that discovery of a NULL interface results in
ENXIO (already implemented for some calls).  Processes blocked on a BPF
call will also be woken up so that they can receive ENXIO.

Interface drivers that invoke bpfattach and if_detach must be modified to
also call bpfattach(ifp) before calling if_detach(ifp).  This is relevant
for buses that support hot removal, such as pccard and usb.  Patches to
all effected devices will not be committed, only to if_wi.c, due to
testing limitations.  To reproduce the crash, load up tcpdump on you
favorite pccard ethernet card, and then eject the card.  As some pccard
drivers do not invoke if_detach(ifp), this bug will not manifest itself
for those drivers.

Reviewed by:	wes
This commit is contained in:
Robert Watson 2000-03-19 05:42:34 +00:00
parent ea20901db7
commit de5d99354f
2 changed files with 65 additions and 0 deletions

View File

@ -477,6 +477,18 @@ bpfread(dev, uio, ioflag)
ROTATE_BUFFERS(d);
break;
}
/*
* No data is available, check to see if the bpf device
* is still pointed at a real interface. If not, return
* ENXIO so that the userland process knows to rebind
* it before using it again.
*/
if (d->bd_bif == NULL) {
splx(s);
return (ENXIO);
}
if (ioflag & IO_NDELAY)
error = EWOULDBLOCK;
else
@ -1035,6 +1047,9 @@ bpfpoll(dev, events, p)
*/
d = dev->si_drv1;
if (d->bd_bif == NULL)
return (ENXIO);
s = splimp();
if (events & (POLLIN | POLLRDNORM)) {
if (d->bd_hlen != 0 || (d->bd_immediate && d->bd_slen != 0))
@ -1287,6 +1302,54 @@ bpfattach(ifp, dlt, hdrlen)
printf("bpf: %s%d attached\n", ifp->if_name, ifp->if_unit);
}
/*
* Detach bpf from an interface. This involves detaching each descriptor
* associated with the interface, and leaving bd_bif NULL. Notify each
* descriptor as it's detached so that any sleepers wake up and get
* ENXIO.
*/
void
bpfdetach(ifp)
struct ifnet *ifp;
{
struct bpf_if *bp, *bp_prev;
struct bpf_d *d;
int s;
s = splimp();
/* Locate BPF interface information */
bp_prev = NULL;
for (bp = bpf_iflist; bp != NULL; bp = bp->bif_next) {
if (ifp == bp->bif_ifp)
break;
bp_prev = bp;
}
/* Interface wasn't attached */
if (bp->bif_ifp == NULL) {
splx(s);
printf("bpfdetach: %s%d was not attached\n", ifp->if_name,
ifp->if_unit);
return;
}
while ((d = bp->bif_dlist) != NULL) {
bpf_detachd(d);
bpf_wakeup(d);
}
if (bp_prev) {
bp_prev->bif_next = bp->bif_next;
} else {
bpf_iflist = bp->bif_next;
}
free(bp, M_BPF);
splx(s);
}
static void bpf_drvinit __P((void *unused));
static void

View File

@ -232,6 +232,8 @@ int bpf_validate __P((const struct bpf_insn *, int));
void bpf_tap __P((struct ifnet *, u_char *, u_int));
void bpf_mtap __P((struct ifnet *, struct mbuf *));
void bpfattach __P((struct ifnet *, u_int, u_int));
void bpfdetach __P((struct ifnet *));
void bpfilterattach __P((int));
u_int bpf_filter __P((const struct bpf_insn *, u_char *, u_int, u_int));
#endif