Commit f69fa76482e654f7d94e4aa40ea0ebf04363396a

Authored by Linus Torvalds

Merge branch 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6

* 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6:
  firewire: ohci: fix race when reading count in AR descriptor
  firewire: ohci: avoid reallocation of AR buffers
  firewire: ohci: fix race in AR split packet handling
  firewire: ohci: fix buffer overflow in AR split packet handling

Showing 1 changed file Side-by-side Diff

drivers/firewire/ohci.c
... ... @@ -577,17 +577,11 @@
577 577 return ret;
578 578 }
579 579  
580   -static int ar_context_add_page(struct ar_context *ctx)
  580 +static void ar_context_link_page(struct ar_context *ctx,
  581 + struct ar_buffer *ab, dma_addr_t ab_bus)
581 582 {
582   - struct device *dev = ctx->ohci->card.device;
583   - struct ar_buffer *ab;
584   - dma_addr_t uninitialized_var(ab_bus);
585 583 size_t offset;
586 584  
587   - ab = dma_alloc_coherent(dev, PAGE_SIZE, &ab_bus, GFP_ATOMIC);
588   - if (ab == NULL)
589   - return -ENOMEM;
590   -
591 585 ab->next = NULL;
592 586 memset(&ab->descriptor, 0, sizeof(ab->descriptor));
593 587 ab->descriptor.control = cpu_to_le16(DESCRIPTOR_INPUT_MORE |
594 588  
... ... @@ -606,7 +600,20 @@
606 600  
607 601 reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
608 602 flush_writes(ctx->ohci);
  603 +}
609 604  
  605 +static int ar_context_add_page(struct ar_context *ctx)
  606 +{
  607 + struct device *dev = ctx->ohci->card.device;
  608 + struct ar_buffer *ab;
  609 + dma_addr_t uninitialized_var(ab_bus);
  610 +
  611 + ab = dma_alloc_coherent(dev, PAGE_SIZE, &ab_bus, GFP_ATOMIC);
  612 + if (ab == NULL)
  613 + return -ENOMEM;
  614 +
  615 + ar_context_link_page(ctx, ab, ab_bus);
  616 +
610 617 return 0;
611 618 }
612 619  
613 620  
614 621  
... ... @@ -730,16 +737,17 @@
730 737 static void ar_context_tasklet(unsigned long data)
731 738 {
732 739 struct ar_context *ctx = (struct ar_context *)data;
733   - struct fw_ohci *ohci = ctx->ohci;
734 740 struct ar_buffer *ab;
735 741 struct descriptor *d;
736 742 void *buffer, *end;
  743 + __le16 res_count;
737 744  
738 745 ab = ctx->current_buffer;
739 746 d = &ab->descriptor;
740 747  
741   - if (d->res_count == 0) {
742   - size_t size, rest, offset;
  748 + res_count = ACCESS_ONCE(d->res_count);
  749 + if (res_count == 0) {
  750 + size_t size, size2, rest, pktsize, size3, offset;
743 751 dma_addr_t start_bus;
744 752 void *start;
745 753  
746 754  
747 755  
748 756  
749 757  
750 758  
751 759  
752 760  
... ... @@ -750,29 +758,63 @@
750 758 */
751 759  
752 760 offset = offsetof(struct ar_buffer, data);
753   - start = buffer = ab;
  761 + start = ab;
754 762 start_bus = le32_to_cpu(ab->descriptor.data_address) - offset;
  763 + buffer = ab->data;
755 764  
756 765 ab = ab->next;
757 766 d = &ab->descriptor;
758   - size = buffer + PAGE_SIZE - ctx->pointer;
  767 + size = start + PAGE_SIZE - ctx->pointer;
  768 + /* valid buffer data in the next page */
759 769 rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count);
  770 + /* what actually fits in this page */
  771 + size2 = min(rest, (size_t)PAGE_SIZE - offset - size);
760 772 memmove(buffer, ctx->pointer, size);
761   - memcpy(buffer + size, ab->data, rest);
762   - ctx->current_buffer = ab;
763   - ctx->pointer = (void *) ab->data + rest;
764   - end = buffer + size + rest;
  773 + memcpy(buffer + size, ab->data, size2);
765 774  
766   - while (buffer < end)
767   - buffer = handle_ar_packet(ctx, buffer);
  775 + while (size > 0) {
  776 + void *next = handle_ar_packet(ctx, buffer);
  777 + pktsize = next - buffer;
  778 + if (pktsize >= size) {
  779 + /*
  780 + * We have handled all the data that was
  781 + * originally in this page, so we can now
  782 + * continue in the next page.
  783 + */
  784 + buffer = next;
  785 + break;
  786 + }
  787 + /* move the next packet to the start of the buffer */
  788 + memmove(buffer, next, size + size2 - pktsize);
  789 + size -= pktsize;
  790 + /* fill up this page again */
  791 + size3 = min(rest - size2,
  792 + (size_t)PAGE_SIZE - offset - size - size2);
  793 + memcpy(buffer + size + size2,
  794 + (void *) ab->data + size2, size3);
  795 + size2 += size3;
  796 + }
768 797  
769   - dma_free_coherent(ohci->card.device, PAGE_SIZE,
770   - start, start_bus);
771   - ar_context_add_page(ctx);
  798 + if (rest > 0) {
  799 + /* handle the packets that are fully in the next page */
  800 + buffer = (void *) ab->data +
  801 + (buffer - (start + offset + size));
  802 + end = (void *) ab->data + rest;
  803 +
  804 + while (buffer < end)
  805 + buffer = handle_ar_packet(ctx, buffer);
  806 +
  807 + ctx->current_buffer = ab;
  808 + ctx->pointer = end;
  809 +
  810 + ar_context_link_page(ctx, start, start_bus);
  811 + } else {
  812 + ctx->pointer = start + PAGE_SIZE;
  813 + }
772 814 } else {
773 815 buffer = ctx->pointer;
774 816 ctx->pointer = end =
775   - (void *) ab + PAGE_SIZE - le16_to_cpu(d->res_count);
  817 + (void *) ab + PAGE_SIZE - le16_to_cpu(res_count);
776 818  
777 819 while (buffer < end)
778 820 buffer = handle_ar_packet(ctx, buffer);