aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire/ohci.c
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2010-10-25 05:41:53 -0400
committerStefan Richter <stefanr@s5r6.in-berlin.de>2010-10-30 17:37:19 -0400
commit85f7ffd5d2b320f73912b15fe8cef34bae297daf (patch)
treed9151979bb3834bd1254239a42247a668f059133 /drivers/firewire/ohci.c
parentf6f94e2ab1b33f0082ac22d71f66385a60d8157f (diff)
firewire: ohci: fix buffer overflow in AR split packet handling
When the controller had to split a received asynchronous packet into two buffers, the driver tries to reassemble it by copying both parts into the first page. However, if size + rest > PAGE_SIZE, i.e., if the yet unhandled packets before the split packet, the split packet itself, and any received packets after the split packet are together larger than one page, then the memory after the first page would get overwritten. To fix this, do not try to copy the data of all unhandled packets at once, but copy the possibly needed data every time when handling a packet. This gets rid of most of the infamous crashes and data corruptions when using firewire-net. Signed-off-by: Clemens Ladisch <clemens@ladisch.de> Cc: 2.6.22-2.6.36 <stable@kernel.org> Tested-by: Maxim Levitsky <maximlevitsky@gmail.com> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (cast PAGE_SIZE to size_t)
Diffstat (limited to 'drivers/firewire/ohci.c')
-rw-r--r--drivers/firewire/ohci.c35
1 files changed, 32 insertions, 3 deletions
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index 9dcb17d51aee..5826ae333b19 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -739,7 +739,7 @@ static void ar_context_tasklet(unsigned long data)
739 d = &ab->descriptor; 739 d = &ab->descriptor;
740 740
741 if (d->res_count == 0) { 741 if (d->res_count == 0) {
742 size_t size, rest, offset; 742 size_t size, size2, rest, pktsize, size3, offset;
743 dma_addr_t start_bus; 743 dma_addr_t start_bus;
744 void *start; 744 void *start;
745 745
@@ -756,12 +756,41 @@ static void ar_context_tasklet(unsigned long data)
756 ab = ab->next; 756 ab = ab->next;
757 d = &ab->descriptor; 757 d = &ab->descriptor;
758 size = buffer + PAGE_SIZE - ctx->pointer; 758 size = buffer + PAGE_SIZE - ctx->pointer;
759 /* valid buffer data in the next page */
759 rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count); 760 rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count);
761 /* what actually fits in this page */
762 size2 = min(rest, (size_t)PAGE_SIZE - size);
760 memmove(buffer, ctx->pointer, size); 763 memmove(buffer, ctx->pointer, size);
761 memcpy(buffer + size, ab->data, rest); 764 memcpy(buffer + size, ab->data, size2);
762 ctx->current_buffer = ab; 765 ctx->current_buffer = ab;
763 ctx->pointer = (void *) ab->data + rest; 766 ctx->pointer = (void *) ab->data + rest;
764 end = buffer + size + rest; 767
768 while (size > 0) {
769 void *next = handle_ar_packet(ctx, buffer);
770 pktsize = next - buffer;
771 if (pktsize >= size) {
772 /*
773 * We have handled all the data that was
774 * originally in this page, so we can now
775 * continue in the next page.
776 */
777 buffer = next;
778 break;
779 }
780 /* move the next packet to the start of the buffer */
781 memmove(buffer, next, size + size2 - pktsize);
782 size -= pktsize;
783 /* fill up this page again */
784 size3 = min(rest - size2,
785 (size_t)PAGE_SIZE - size - size2);
786 memcpy(buffer + size + size2,
787 (void *) ab->data + size2, size3);
788 size2 += size3;
789 }
790
791 /* handle the packets that are fully in the next page */
792 buffer = (void *) ab->data + (buffer - (start + size));
793 end = (void *) ab->data + rest;
765 794
766 while (buffer < end) 795 while (buffer < end)
767 buffer = handle_ar_packet(ctx, buffer); 796 buffer = handle_ar_packet(ctx, buffer);