|
@@ -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: [email protected]
|
|
|
|
|
+Reported-by: Francesco Gringoli <[email protected]>
|
|
|
|
|
+Signed-off-by: Michael Buesch <[email protected]>
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+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.
|