diff options
author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2016-10-29 15:28:18 -0400 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2016-11-03 09:46:39 -0400 |
commit | 667121ace9dbafb368618dbabcf07901c962ddac (patch) | |
tree | a73ac08b8ff287151a62bfadc8acf167a3837194 | |
parent | 6449e31ddebdce68508cfaf0915d31aad3835f4f (diff) |
firewire: net: guard against rx buffer overflows
The IP-over-1394 driver firewire-net lacked input validation when
handling incoming fragmented datagrams. A maliciously formed fragment
with a respectively large datagram_offset would cause a memcpy past the
datagram buffer.
So, drop any packets carrying a fragment with offset + length larger
than datagram_size.
In addition, ensure that
- GASP header, unfragmented encapsulation header, or fragment
encapsulation header actually exists before we access it,
- the encapsulated datagram or fragment is of nonzero size.
Reported-by: Eyal Itkin <eyal.itkin@gmail.com>
Reviewed-by: Eyal Itkin <eyal.itkin@gmail.com>
Fixes: CVE 2016-8633
Cc: stable@vger.kernel.org
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
-rw-r--r-- | drivers/firewire/net.c | 51 |
1 files changed, 35 insertions, 16 deletions
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index 309311b1faae..cbaa8d776d83 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c | |||
@@ -578,6 +578,9 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, | |||
578 | int retval; | 578 | int retval; |
579 | u16 ether_type; | 579 | u16 ether_type; |
580 | 580 | ||
581 | if (len <= RFC2374_UNFRAG_HDR_SIZE) | ||
582 | return 0; | ||
583 | |||
581 | hdr.w0 = be32_to_cpu(buf[0]); | 584 | hdr.w0 = be32_to_cpu(buf[0]); |
582 | lf = fwnet_get_hdr_lf(&hdr); | 585 | lf = fwnet_get_hdr_lf(&hdr); |
583 | if (lf == RFC2374_HDR_UNFRAG) { | 586 | if (lf == RFC2374_HDR_UNFRAG) { |
@@ -602,7 +605,12 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, | |||
602 | return fwnet_finish_incoming_packet(net, skb, source_node_id, | 605 | return fwnet_finish_incoming_packet(net, skb, source_node_id, |
603 | is_broadcast, ether_type); | 606 | is_broadcast, ether_type); |
604 | } | 607 | } |
608 | |||
605 | /* A datagram fragment has been received, now the fun begins. */ | 609 | /* A datagram fragment has been received, now the fun begins. */ |
610 | |||
611 | if (len <= RFC2374_FRAG_HDR_SIZE) | ||
612 | return 0; | ||
613 | |||
606 | hdr.w1 = ntohl(buf[1]); | 614 | hdr.w1 = ntohl(buf[1]); |
607 | buf += 2; | 615 | buf += 2; |
608 | len -= RFC2374_FRAG_HDR_SIZE; | 616 | len -= RFC2374_FRAG_HDR_SIZE; |
@@ -616,6 +624,9 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, | |||
616 | datagram_label = fwnet_get_hdr_dgl(&hdr); | 624 | datagram_label = fwnet_get_hdr_dgl(&hdr); |
617 | dg_size = fwnet_get_hdr_dg_size(&hdr); /* ??? + 1 */ | 625 | dg_size = fwnet_get_hdr_dg_size(&hdr); /* ??? + 1 */ |
618 | 626 | ||
627 | if (fg_off + len > dg_size) | ||
628 | return 0; | ||
629 | |||
619 | spin_lock_irqsave(&dev->lock, flags); | 630 | spin_lock_irqsave(&dev->lock, flags); |
620 | 631 | ||
621 | peer = fwnet_peer_find_by_node_id(dev, source_node_id, generation); | 632 | peer = fwnet_peer_find_by_node_id(dev, source_node_id, generation); |
@@ -722,6 +733,22 @@ static void fwnet_receive_packet(struct fw_card *card, struct fw_request *r, | |||
722 | fw_send_response(card, r, rcode); | 733 | fw_send_response(card, r, rcode); |
723 | } | 734 | } |
724 | 735 | ||
736 | static int gasp_source_id(__be32 *p) | ||
737 | { | ||
738 | return be32_to_cpu(p[0]) >> 16; | ||
739 | } | ||
740 | |||
741 | static u32 gasp_specifier_id(__be32 *p) | ||
742 | { | ||
743 | return (be32_to_cpu(p[0]) & 0xffff) << 8 | | ||
744 | (be32_to_cpu(p[1]) & 0xff000000) >> 24; | ||
745 | } | ||
746 | |||
747 | static u32 gasp_version(__be32 *p) | ||
748 | { | ||
749 | return be32_to_cpu(p[1]) & 0xffffff; | ||
750 | } | ||
751 | |||
725 | static void fwnet_receive_broadcast(struct fw_iso_context *context, | 752 | static void fwnet_receive_broadcast(struct fw_iso_context *context, |
726 | u32 cycle, size_t header_length, void *header, void *data) | 753 | u32 cycle, size_t header_length, void *header, void *data) |
727 | { | 754 | { |
@@ -731,9 +758,6 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context, | |||
731 | __be32 *buf_ptr; | 758 | __be32 *buf_ptr; |
732 | int retval; | 759 | int retval; |
733 | u32 length; | 760 | u32 length; |
734 | u16 source_node_id; | ||
735 | u32 specifier_id; | ||
736 | u32 ver; | ||
737 | unsigned long offset; | 761 | unsigned long offset; |
738 | unsigned long flags; | 762 | unsigned long flags; |
739 | 763 | ||
@@ -750,22 +774,17 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context, | |||
750 | 774 | ||
751 | spin_unlock_irqrestore(&dev->lock, flags); | 775 | spin_unlock_irqrestore(&dev->lock, flags); |
752 | 776 | ||
753 | specifier_id = (be32_to_cpu(buf_ptr[0]) & 0xffff) << 8 | 777 | if (length > IEEE1394_GASP_HDR_SIZE && |
754 | | (be32_to_cpu(buf_ptr[1]) & 0xff000000) >> 24; | 778 | gasp_specifier_id(buf_ptr) == IANA_SPECIFIER_ID && |
755 | ver = be32_to_cpu(buf_ptr[1]) & 0xffffff; | 779 | (gasp_version(buf_ptr) == RFC2734_SW_VERSION |
756 | source_node_id = be32_to_cpu(buf_ptr[0]) >> 16; | ||
757 | |||
758 | if (specifier_id == IANA_SPECIFIER_ID && | ||
759 | (ver == RFC2734_SW_VERSION | ||
760 | #if IS_ENABLED(CONFIG_IPV6) | 780 | #if IS_ENABLED(CONFIG_IPV6) |
761 | || ver == RFC3146_SW_VERSION | 781 | || gasp_version(buf_ptr) == RFC3146_SW_VERSION |
762 | #endif | 782 | #endif |
763 | )) { | 783 | )) |
764 | buf_ptr += 2; | 784 | fwnet_incoming_packet(dev, buf_ptr + 2, |
765 | length -= IEEE1394_GASP_HDR_SIZE; | 785 | length - IEEE1394_GASP_HDR_SIZE, |
766 | fwnet_incoming_packet(dev, buf_ptr, length, source_node_id, | 786 | gasp_source_id(buf_ptr), |
767 | context->card->generation, true); | 787 | context->card->generation, true); |
768 | } | ||
769 | 788 | ||
770 | packet.payload_length = dev->rcv_buffer_size; | 789 | packet.payload_length = dev->rcv_buffer_size; |
771 | packet.interrupt = 1; | 790 | packet.interrupt = 1; |