diff options
Diffstat (limited to 'package/mac80211/patches/510-b43-poison-rx-buffers.patch')
-rw-r--r-- | package/mac80211/patches/510-b43-poison-rx-buffers.patch | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/package/mac80211/patches/510-b43-poison-rx-buffers.patch b/package/mac80211/patches/510-b43-poison-rx-buffers.patch new file mode 100644 index 0000000000..c50df132c3 --- /dev/null +++ b/package/mac80211/patches/510-b43-poison-rx-buffers.patch @@ -0,0 +1,105 @@ +This patch adds poisoning and sanity checking to the RX DMA buffers. +This is used for protection against buggy hardware/firmware that raises +RX interrupts without doing an actual DMA transfer. + +This mechanism protects against rare "bad packets" (due to uninitialized skb data) +and rare kernel crashes due to uninitialized RX headers. + +The poison is selected to not match on valid frames and to be cheap for checking. + +The poison check mechanism _might_ trigger incorrectly, if we are voluntarily +receiving frames with bad PLCP headers. However, this is nonfatal, because the +chance of such a match is basically zero and in case it happens it just results +in dropping the packet. +Bad-PLCP RX defaults to off, and you should leave it off unless you want to listen +to the latest news broadcasted by your microwave oven. + +This patch also moves the initialization of the RX-header "length" field in front of +the mapping of the DMA buffer. The CPU should not touch the buffer after we mapped it. + +Cc: stable@kernel.org +Reported-by: Francesco Gringoli <francesco.gringoli@ing.unibs.it> +Signed-off-by: Michael Buesch <mb@bu3sch.de> + +--- + +Index: compat-wireless-2009-03-31/drivers/net/wireless/b43/dma.c +=================================================================== +--- compat-wireless-2009-03-31.orig/drivers/net/wireless/b43/dma.c 2009-04-06 18:52:25.000000000 +0200 ++++ compat-wireless-2009-03-31/drivers/net/wireless/b43/dma.c 2009-04-06 18:52:30.000000000 +0200 +@@ -555,11 +555,32 @@ address_error: + return 1; + } + ++static bool b43_rx_buffer_is_poisoned(struct b43_dmaring *ring, struct sk_buff *skb) ++{ ++ unsigned char *f = skb->data + ring->frameoffset; ++ ++ return ((f[0] & f[1] & f[2] & f[3] & f[4] & f[5] & f[6] & f[7]) == 0xFF); ++} ++ ++static void b43_poison_rx_buffer(struct b43_dmaring *ring, struct sk_buff *skb) ++{ ++ struct b43_rxhdr_fw4 *rxhdr; ++ unsigned char *frame; ++ ++ /* This poisons the RX buffer to detect DMA failures. */ ++ ++ rxhdr = (struct b43_rxhdr_fw4 *)(skb->data); ++ rxhdr->frame_len = 0; ++ ++ B43_WARN_ON(ring->rx_buffersize < ring->frameoffset + sizeof(struct b43_plcp_hdr6) + 2); ++ frame = skb->data + ring->frameoffset; ++ memset(frame, 0xFF, sizeof(struct b43_plcp_hdr6) + 2 /* padding */); ++} ++ + static int setup_rx_descbuffer(struct b43_dmaring *ring, + struct b43_dmadesc_generic *desc, + struct b43_dmadesc_meta *meta, gfp_t gfp_flags) + { +- struct b43_rxhdr_fw4 *rxhdr; + dma_addr_t dmaaddr; + struct sk_buff *skb; + +@@ -568,6 +589,7 @@ static int setup_rx_descbuffer(struct b4 + skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); + if (unlikely(!skb)) + return -ENOMEM; ++ b43_poison_rx_buffer(ring, skb); + dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); + if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { + /* ugh. try to realloc in zone_dma */ +@@ -578,6 +600,7 @@ static int setup_rx_descbuffer(struct b4 + skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); + if (unlikely(!skb)) + return -ENOMEM; ++ b43_poison_rx_buffer(ring, skb); + dmaaddr = map_descbuffer(ring, skb->data, + ring->rx_buffersize, 0); + if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { +@@ -592,9 +615,6 @@ static int setup_rx_descbuffer(struct b4 + ring->ops->fill_descriptor(ring, desc, dmaaddr, + ring->rx_buffersize, 0, 0, 0); + +- rxhdr = (struct b43_rxhdr_fw4 *)(skb->data); +- rxhdr->frame_len = 0; +- + return 0; + } + +@@ -1489,6 +1509,15 @@ static void dma_rx(struct b43_dmaring *r + goto drop; + } + } ++ if (unlikely(b43_rx_buffer_is_poisoned(ring, skb))) { ++ /* Something went wrong with the DMA. ++ * The device did not touch the buffer and did not overwrite the poison. */ ++ b43dbg(ring->dev->wl, "DMA RX: Dropping poisoned buffer.\n"); ++ /* recycle the descriptor buffer. */ ++ sync_descbuffer_for_device(ring, meta->dmaaddr, ++ ring->rx_buffersize); ++ goto drop; ++ } + if (unlikely(len > ring->rx_buffersize)) { + /* The data did not fit into one descriptor buffer + * and is split over multiple buffers. |