diff options
author | csjp <csjp@FreeBSD.org> | 2006-06-02 19:59:33 +0000 |
---|---|---|
committer | csjp <csjp@FreeBSD.org> | 2006-06-02 19:59:33 +0000 |
commit | 2c4f67981e37d4914db61b39de9ce50520b8ab77 (patch) | |
tree | 91b5bc64ab856cef269d9fab6ff3feca3e06cf2c /sys/dev | |
parent | 420f0a56b11b92d44992ae037cd8d5e18cc582f6 (diff) | |
download | FreeBSD-src-2c4f67981e37d4914db61b39de9ce50520b8ab77.zip FreeBSD-src-2c4f67981e37d4914db61b39de9ce50520b8ab77.tar.gz |
Fix the following bpf(4) race condition which can result in a panic:
(1) bpf peer attaches to interface netif0
(2) Packet is received by netif0
(3) ifp->if_bpf pointer is checked and handed off to bpf
(4) bpf peer detaches from netif0 resulting in ifp->if_bpf being
initialized to NULL.
(5) ifp->if_bpf is dereferenced by bpf machinery
(6) Kaboom
This race condition likely explains the various different kernel panics
reported around sending SIGINT to tcpdump or dhclient processes. But really
this race can result in kernel panics anywhere you have frequent bpf attach
and detach operations with high packet per second load.
Summary of changes:
- Remove the bpf interface's "driverp" member
- When we attach bpf interfaces, we now set the ifp->if_bpf member to the
bpf interface structure. Once this is done, ifp->if_bpf should never be
NULL. [1]
- Introduce bpf_peers_present function, an inline operation which will do
a lockless read bpf peer list associated with the interface. It should
be noted that the bpf code will pickup the bpf_interface lock before adding
or removing bpf peers. This should serialize the access to the bpf descriptor
list, removing the race.
- Expose the bpf_if structure in bpf.h so that the bpf_peers_present function
can use it. This also removes the struct bpf_if; hack that was there.
- Adjust all consumers of the raw if_bpf structure to use bpf_peers_present
Now what happens is:
(1) Packet is received by netif0
(2) Check to see if bpf descriptor list is empty
(3) Pickup the bpf interface lock
(4) Hand packet off to process
From the attach/detach side:
(1) Pickup the bpf interface lock
(2) Add/remove from bpf descriptor list
Now that we are storing the bpf interface structure with the ifnet, there is
is no need to walk the bpf interface list to locate the correct bpf interface.
We now simply look up the interface, and initialize the pointer. This has a
nice side effect of changing a bpf interface attach operation from O(N) (where
N is the number of bpf interfaces), to O(1).
[1] From now on, we can no longer check ifp->if_bpf to tell us whether or
not we have any bpf peers that might be interested in receiving packets.
In collaboration with: sam@
MFC after: 1 month
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ath/if_ath.c | 7 | ||||
-rw-r--r-- | sys/dev/ipw/if_ipw.c | 4 | ||||
-rw-r--r-- | sys/dev/iwi/if_iwi.c | 4 | ||||
-rw-r--r-- | sys/dev/ral/rt2560.c | 14 | ||||
-rw-r--r-- | sys/dev/ral/rt2661.c | 6 | ||||
-rw-r--r-- | sys/dev/usb/if_ural.c | 6 | ||||
-rw-r--r-- | sys/dev/wi/if_wi.c | 6 |
7 files changed, 24 insertions, 23 deletions
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index 397e87f..09d5976 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -2905,7 +2905,7 @@ ath_rx_proc(void *arg, int npending) * pass decrypt+mic errors but others may be * interesting (e.g. crc). */ - if (sc->sc_drvbpf != NULL && + if (bpf_peers_present(sc->sc_drvbpf) && (ds->ds_rxstat.rs_status & sc->sc_monpass)) { bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_POSTREAD); @@ -2936,7 +2936,8 @@ rx_accept: sc->sc_stats.ast_ant_rx[ds->ds_rxstat.rs_antenna]++; - if (sc->sc_drvbpf != NULL && !ath_rx_tap(sc, m, ds, tsf, nf)) { + if (bpf_peers_present(sc->sc_drvbpf) && + !ath_rx_tap(sc, m, ds, tsf, nf)) { m_freem(m); /* XXX reclaim */ goto rx_next; } @@ -3636,7 +3637,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf if (ic->ic_rawbpf) bpf_mtap(ic->ic_rawbpf, m0); - if (sc->sc_drvbpf) { + if (bpf_peers_present(sc->sc_drvbpf)) { u_int64_t tsf = ath_hal_gettsf64(ah); sc->sc_tx_th.wt_tsf = htole64(tsf); diff --git a/sys/dev/ipw/if_ipw.c b/sys/dev/ipw/if_ipw.c index b3dcfad..c7da03e 100644 --- a/sys/dev/ipw/if_ipw.c +++ b/sys/dev/ipw/if_ipw.c @@ -1054,7 +1054,7 @@ ipw_data_intr(struct ipw_softc *sc, struct ipw_status *status, m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = le32toh(status->len); - if (sc->sc_drvbpf != NULL) { + if (bpf_peers_present(sc->sc_drvbpf)) { struct ipw_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; @@ -1328,7 +1328,7 @@ ipw_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni) wh = mtod(m0, struct ieee80211_frame *); } - if (sc->sc_drvbpf != NULL) { + if (bpf_peers_present(sc->sc_drvbpf)) { struct ipw_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; diff --git a/sys/dev/iwi/if_iwi.c b/sys/dev/iwi/if_iwi.c index 34713e4..3079fb4 100644 --- a/sys/dev/iwi/if_iwi.c +++ b/sys/dev/iwi/if_iwi.c @@ -1300,7 +1300,7 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i, m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame)); - if (sc->sc_drvbpf != NULL) { + if (bpf_peers_present(sc->sc_drvbpf)) { struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; @@ -1829,7 +1829,7 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni, wh = mtod(m0, struct ieee80211_frame *); } - if (sc->sc_drvbpf != NULL) { + if (bpf_peers_present(sc->sc_drvbpf)) { struct iwi_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; diff --git a/sys/dev/ral/rt2560.c b/sys/dev/ral/rt2560.c index 3840c50..652a089 100644 --- a/sys/dev/ral/rt2560.c +++ b/sys/dev/ral/rt2560.c @@ -1225,7 +1225,7 @@ rt2560_decryption_intr(struct rt2560_softc *sc) m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff; - if (sc->sc_drvbpf != NULL) { + if (bpf_peers_present(sc->sc_drvbpf)) { struct rt2560_rx_radiotap_header *tap = &sc->sc_rxtap; uint32_t tsf_lo, tsf_hi; @@ -1346,7 +1346,7 @@ rt2560_beacon_expire(struct rt2560_softc *sc) ieee80211_beacon_update(ic, data->ni, &sc->sc_bo, data->m, 1); - if (ic->ic_rawbpf != NULL) + if (bpf_peers_present(ic->ic_rawbpf)) bpf_mtap(ic->ic_rawbpf, data->m); rt2560_tx_bcn(sc, data->m, data->ni); @@ -1606,7 +1606,7 @@ rt2560_tx_bcn(struct rt2560_softc *sc, struct mbuf *m0, return error; } - if (sc->sc_drvbpf != NULL) { + if (bpf_peers_present(sc->sc_drvbpf)) { struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; @@ -1663,7 +1663,7 @@ rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0, return error; } - if (sc->sc_drvbpf != NULL) { + if (bpf_peers_present(sc->sc_drvbpf)) { struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; @@ -1882,7 +1882,7 @@ rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0, wh = mtod(m0, struct ieee80211_frame *); } - if (sc->sc_drvbpf != NULL) { + if (bpf_peers_present(sc->sc_drvbpf)) { struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; @@ -1961,7 +1961,7 @@ rt2560_start(struct ifnet *ifp) ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif; m0->m_pkthdr.rcvif = NULL; - if (ic->ic_rawbpf != NULL) + if (bpf_peers_present(ic->ic_rawbpf)) bpf_mtap(ic->ic_rawbpf, m0); if (rt2560_tx_mgt(sc, m0, ni) != 0) @@ -1997,7 +1997,7 @@ rt2560_start(struct ifnet *ifp) continue; } - if (ic->ic_rawbpf != NULL) + if (bpf_peers_present(ic->ic_rawbpf)) bpf_mtap(ic->ic_rawbpf, m0); if (rt2560_tx_data(sc, m0, ni) != 0) { diff --git a/sys/dev/ral/rt2661.c b/sys/dev/ral/rt2661.c index 7cfa6cd..d40e906 100644 --- a/sys/dev/ral/rt2661.c +++ b/sys/dev/ral/rt2661.c @@ -1132,7 +1132,7 @@ rt2661_rx_intr(struct rt2661_softc *sc) m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff; - if (sc->sc_drvbpf != NULL) { + if (bpf_peers_present(sc->sc_drvbpf)) { struct rt2661_rx_radiotap_header *tap = &sc->sc_rxtap; uint32_t tsf_lo, tsf_hi; @@ -1480,7 +1480,7 @@ rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0, return error; } - if (sc->sc_drvbpf != NULL) { + if (bpf_peers_present(sc->sc_drvbpf)) { struct rt2661_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; @@ -1704,7 +1704,7 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0, wh = mtod(m0, struct ieee80211_frame *); } - if (sc->sc_drvbpf != NULL) { + if (bpf_peers_present(sc->sc_drvbpf)) { struct rt2661_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; diff --git a/sys/dev/usb/if_ural.c b/sys/dev/usb/if_ural.c index e3a6111..5a92e2c 100644 --- a/sys/dev/usb/if_ural.c +++ b/sys/dev/usb/if_ural.c @@ -956,7 +956,7 @@ ural_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff; m->m_flags |= M_HASFCS; /* h/w leaves FCS */ - if (sc->sc_drvbpf != NULL) { + if (bpf_peers_present(sc->sc_drvbpf)) { struct ural_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = IEEE80211_RADIOTAP_F_FCS; @@ -1204,7 +1204,7 @@ ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) flags |= RAL_TX_TIMESTAMP; } - if (sc->sc_drvbpf != NULL) { + if (bpf_peers_present(sc->sc_drvbpf)) { struct ural_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; @@ -1293,7 +1293,7 @@ ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) *(uint16_t *)wh->i_dur = htole16(dur); } - if (sc->sc_drvbpf != NULL) { + if (bpf_peers_present(sc->sc_drvbpf)) { struct ural_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; diff --git a/sys/dev/wi/if_wi.c b/sys/dev/wi/if_wi.c index e238f3a..aa2dc0f 100644 --- a/sys/dev/wi/if_wi.c +++ b/sys/dev/wi/if_wi.c @@ -961,7 +961,7 @@ wi_start(struct ifnet *ifp) wh = mtod(m0, struct ieee80211_frame *); } #if NBPFILTER > 0 - if (ic->ic_rawbpf) + if (bpf_peers_present(ic->ic_rawbpf)) bpf_mtap(ic->ic_rawbpf, m0); #endif frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX); @@ -980,7 +980,7 @@ wi_start(struct ifnet *ifp) frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT); } #if NBPFILTER > 0 - if (sc->sc_drvbpf) { + if (bpf_peers_present(ic->ic_rawbpf)) { sc->sc_tx_th.wt_rate = ni->ni_rates.rs_rates[ni->ni_txrate]; bpf_mtap2(sc->sc_drvbpf, @@ -1534,7 +1534,7 @@ wi_rx_intr(struct wi_softc *sc) } #if NBPFILTER > 0 - if (sc->sc_drvbpf) { + if (bpf_peers_present(sc->sc_drvbpf)) { /* XXX replace divide by table */ sc->sc_rx_th.wr_rate = frmhdr.wi_rx_rate / 5; sc->sc_rx_th.wr_antsignal = frmhdr.wi_rx_signal; |