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:
parent
ea20901db7
commit
de5d99354f
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user