Fixes in the iwi(4) driver to better handle errors:

- bus_dmamap fixes, with checks against NULL
- move the rings' allocation code later in iwi_attach() to avoid NULL pointer
dereference if allocation fails
- avoid double free() in case of failure during attach. If an allocation fails,
do not free the ring directly, as it is handled by iwi_detach()
- only set ring->count for RX/TX rings when ring allocation is successful, or
else the for loop during detach will fail with a NULL dereference.
- call pci_intr_disestablish() if iwi_reset() fails during attach; driver is
in an incoherent state, interrupt handler should not be used.

Proposed by me on tech-net@, approved by christos@. Compiled and tested
with GENERIC and XEN3_DOM0 kernels.
This commit is contained in:
jym 2009-03-13 21:57:07 +00:00
parent 1894a7d27b
commit 96f9080096

View File

@ -1,4 +1,4 @@
/* $NetBSD: if_iwi.c,v 1.79 2009/02/13 21:11:47 bouyer Exp $ */
/* $NetBSD: if_iwi.c,v 1.80 2009/03/13 21:57:07 jym Exp $ */
/*-
* Copyright (c) 2004, 2005
@ -28,7 +28,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_iwi.c,v 1.79 2009/02/13 21:11:47 bouyer Exp $");
__KERNEL_RCSID(0, "$NetBSD: if_iwi.c,v 1.80 2009/03/13 21:57:07 jym Exp $");
/*-
* Intel(R) PRO/Wireless 2200BG/2225BG/2915ABG driver
@ -278,51 +278,11 @@ iwi_attach(device_t parent, device_t self, void *aux)
aprint_normal_dev(self, "interrupting at %s\n", intrstr);
if (iwi_reset(sc) != 0) {
pci_intr_disestablish(sc->sc_pct, sc->sc_ih);
aprint_error_dev(self, "could not reset adapter\n");
return;
}
/*
* Allocate rings.
*/
if (iwi_alloc_cmd_ring(sc, &sc->cmdq, IWI_CMD_RING_COUNT) != 0) {
aprint_error_dev(self, "could not allocate command ring\n");
goto fail;
}
error = iwi_alloc_tx_ring(sc, &sc->txq[0], IWI_TX_RING_COUNT,
IWI_CSR_TX1_RIDX, IWI_CSR_TX1_WIDX);
if (error != 0) {
aprint_error_dev(self, "could not allocate Tx ring 1\n");
goto fail;
}
error = iwi_alloc_tx_ring(sc, &sc->txq[1], IWI_TX_RING_COUNT,
IWI_CSR_TX2_RIDX, IWI_CSR_TX2_WIDX);
if (error != 0) {
aprint_error_dev(self, "could not allocate Tx ring 2\n");
goto fail;
}
error = iwi_alloc_tx_ring(sc, &sc->txq[2], IWI_TX_RING_COUNT,
IWI_CSR_TX3_RIDX, IWI_CSR_TX3_WIDX);
if (error != 0) {
aprint_error_dev(self, "could not allocate Tx ring 3\n");
goto fail;
}
error = iwi_alloc_tx_ring(sc, &sc->txq[3], IWI_TX_RING_COUNT,
IWI_CSR_TX4_RIDX, IWI_CSR_TX4_WIDX);
if (error != 0) {
aprint_error_dev(self, "could not allocate Tx ring 4\n");
goto fail;
}
if (iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT) != 0) {
aprint_error_dev(self, "could not allocate Rx ring\n");
goto fail;
}
ic->ic_ifp = ifp;
ic->ic_wme.wme_update = iwi_wme_update;
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
@ -413,6 +373,47 @@ iwi_attach(device_t parent, device_t self, void *aux)
ic->ic_newstate = iwi_newstate;
ieee80211_media_init(ic, iwi_media_change, iwi_media_status);
/*
* Allocate rings.
*/
if (iwi_alloc_cmd_ring(sc, &sc->cmdq, IWI_CMD_RING_COUNT) != 0) {
aprint_error_dev(self, "could not allocate command ring\n");
goto fail;
}
error = iwi_alloc_tx_ring(sc, &sc->txq[0], IWI_TX_RING_COUNT,
IWI_CSR_TX1_RIDX, IWI_CSR_TX1_WIDX);
if (error != 0) {
aprint_error_dev(self, "could not allocate Tx ring 1\n");
goto fail;
}
error = iwi_alloc_tx_ring(sc, &sc->txq[1], IWI_TX_RING_COUNT,
IWI_CSR_TX2_RIDX, IWI_CSR_TX2_WIDX);
if (error != 0) {
aprint_error_dev(self, "could not allocate Tx ring 2\n");
goto fail;
}
error = iwi_alloc_tx_ring(sc, &sc->txq[2], IWI_TX_RING_COUNT,
IWI_CSR_TX3_RIDX, IWI_CSR_TX3_WIDX);
if (error != 0) {
aprint_error_dev(self, "could not allocate Tx ring 3\n");
goto fail;
}
error = iwi_alloc_tx_ring(sc, &sc->txq[3], IWI_TX_RING_COUNT,
IWI_CSR_TX4_RIDX, IWI_CSR_TX4_WIDX);
if (error != 0) {
aprint_error_dev(self, "could not allocate Tx ring 4\n");
goto fail;
}
if (iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT) != 0) {
aprint_error_dev(self, "could not allocate Rx ring\n");
goto fail;
}
#if NBPFILTER > 0
bpfattach2(ifp, DLT_IEEE802_11_RADIO,
sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf);
@ -494,6 +495,7 @@ iwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring,
if (error != 0) {
aprint_error_dev(sc->sc_dev,
"could not create command ring DMA map\n");
ring->desc_map = NULL;
goto fail;
}
@ -529,8 +531,7 @@ iwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring,
return 0;
fail: iwi_free_cmd_ring(sc, ring);
return error;
fail: return error;
}
static void
@ -571,7 +572,7 @@ iwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring,
{
int i, error, nsegs;
ring->count = count;
ring->count = 0;
ring->queued = 0;
ring->cur = ring->next = 0;
ring->csr_ridx = csr_ridx;
@ -587,6 +588,7 @@ iwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring,
if (error != 0) {
aprint_error_dev(sc->sc_dev,
"could not create tx ring DMA map\n");
ring->desc_map = NULL;
goto fail;
}
@ -626,6 +628,7 @@ iwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring,
error = ENOMEM;
goto fail;
}
ring->count = count;
/*
* Allocate Tx buffers DMA maps
@ -636,13 +639,13 @@ iwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring,
if (error != 0) {
aprint_error_dev(sc->sc_dev,
"could not create tx buf DMA map");
ring->data[i].map = NULL;
goto fail;
}
}
return 0;
fail: iwi_free_tx_ring(sc, ring);
return error;
fail: return error;
}
static void
@ -655,11 +658,14 @@ iwi_reset_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring)
data = &ring->data[i];
if (data->m != NULL) {
m_freem(data->m);
data->m = NULL;
}
if (data->map != NULL) {
bus_dmamap_sync(sc->sc_dmat, data->map, 0,
data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_dmat, data->map);
m_freem(data->m);
data->m = NULL;
}
if (data->ni != NULL) {
@ -676,6 +682,7 @@ static void
iwi_free_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring)
{
int i;
struct iwi_tx_data *data;
if (ring->desc_map != NULL) {
if (ring->desc != NULL) {
@ -688,11 +695,16 @@ iwi_free_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring)
}
for (i = 0; i < ring->count; i++) {
if (ring->data[i].m != NULL) {
bus_dmamap_unload(sc->sc_dmat, ring->data[i].map);
m_freem(ring->data[i].m);
data = &ring->data[i];
if (data->m != NULL) {
m_freem(data->m);
}
if (data->map != NULL) {
bus_dmamap_unload(sc->sc_dmat, data->map);
bus_dmamap_destroy(sc->sc_dmat, data->map);
}
bus_dmamap_destroy(sc->sc_dmat, ring->data[i].map);
}
}
@ -701,7 +713,7 @@ iwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count)
{
int i, error;
ring->count = count;
ring->count = 0;
ring->cur = 0;
ring->data = malloc(count * sizeof (struct iwi_rx_data), M_DEVBUF,
@ -712,6 +724,8 @@ iwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count)
goto fail;
}
ring->count = count;
/*
* Allocate and map Rx buffers
*/
@ -722,6 +736,7 @@ iwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count)
if (error != 0) {
aprint_error_dev(sc->sc_dev,
"could not create rx buf DMA map");
ring->data[i].map = NULL;
goto fail;
}
@ -744,8 +759,7 @@ iwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count)
return 0;
fail: iwi_free_rx_ring(sc, ring);
return error;
fail: return error;
}
static void
@ -758,13 +772,20 @@ static void
iwi_free_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring)
{
int i;
struct iwi_rx_data *data;
for (i = 0; i < ring->count; i++) {
if (ring->data[i].m != NULL) {
bus_dmamap_unload(sc->sc_dmat, ring->data[i].map);
m_freem(ring->data[i].m);
data = &ring->data[i];
if (data->m != NULL) {
m_freem(data->m);
}
bus_dmamap_destroy(sc->sc_dmat, ring->data[i].map);
if (data->map != NULL) {
bus_dmamap_unload(sc->sc_dmat, data->map);
bus_dmamap_destroy(sc->sc_dmat, data->map);
}
}
}
@ -2028,6 +2049,7 @@ iwi_load_firmware(struct iwi_softc *sc, void *fw, int size)
if (error != 0) {
aprint_error_dev(sc->sc_dev,
"could not create firmware DMA map\n");
map = NULL;
goto fail1;
}
@ -2137,7 +2159,8 @@ fail3:
bus_dmamap_sync(sc->sc_dmat, map, 0, size, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_dmat, map);
fail2:
bus_dmamap_destroy(sc->sc_dmat, map);
if (map != NULL)
bus_dmamap_destroy(sc->sc_dmat, map);
fail1:
return error;